Visual Studio Build Event Macros - Solution Configuration Name - visual-studio

In my Post-build event I call a batch file and pass it the current build config.
C:\Dev\Project\Build\build.bat /$(Configuration)
This passes the Project configuration name to the build script.
Is there anyway to pass the current Solution configuration name?

I created a VS2010 extension for this, it lets you use $(SolutionConfiguration) and $(SolutionPlaform). $(BuildMacro) build macros. You can download the source and build it yourself from here.
Showing some code, it's just about registering to UpdateSolution_Begin method
of IVsUpdateSolutionEvents VS and setting some Environment.SetEnvironmentVariable() there.
_IVsSolutionBuildManager = (IVsSolutionBuildManager)GetService<SVsSolutionBuildManager>();
_UpdateSolutionEvents = new UpdateSolutionEvents(); // IVsUpdateSolutionEvents
int hr;
uint pdwCookie;
hr = _IVsSolutionBuildManager.AdviseUpdateSolutionEvents(_UpdateSolutionEvents, out pdwCookie);

Not directly. When you use Edit/Macros on a property field, the only configuration name listed is the one for the project that you already have.
You can, however, define your own macro. For each solution configuration, create a new property sheet, use the "User Macros" tab to define a macro whose name is "SolutionConfiguration" and whose value is the name of the configuration, then add this property sheet to the appropriate project configuration of every project in the solution.
If there's a better way I'd love to learn of it.

There is property SolutionConfigurationContents witch is created by Msbulid during soluton file processing it contains solution configuration in it. When building from VS it will contains project (not solution) configuration.

Related

Running multiple C# classes in one project in Visual Studio

I have a C# project in Visual Studio which has several classes under it. I am trying to run each class separately but when ever I hit the start or debug buttons,only one of the classes (the first one I created) runs.I tried right-clicking the other classes but they don't have the run option. I am using Visual Studio Express 2013
Update (To clarify the question)
Under the Solution C-SharpTutorial i have two .cs files (ArrayTest.cs and Program.cs). What am asking is if it's possible to run these files separately. Right now, I am only able to run the Program.cs file which is the first one i created.
I assume that by classes you actually mean projects. Because one Solution contains one or more projects, and projects can be run.
The answer to that is here: http://msdn.microsoft.com/en-us/library/a1awth7y.aspx
To set a single startup project
In Solution Explorer, select the desired startup project within your solution.
On the Project menu, choose Set as StartUp Project.
Otherwise, please clarify your question.
Okay, i assume you have a console application. While you cannot "run classes", you can set a startup method: https://stackoverflow.com/a/49585943/1974021
A class is a set of methods. To execute (non-static) methods, a class must be instantiated. But the runtime does not know how to call an arbitrary constructor. Therefore, the program execution starts in a static method called "Main".
If multiple classes contain a suitable Main() method, you should be able to select the desired one according to the link above.
It sounds like you mean Projects, not Classes. To change the project that is executed when you start debug mode, you can right click on the project and select "Set Active Project".
If you set breakpoints in any of the other projects that are referenced, they will still be hit and you will be able to debug using Visual Studio.
If you need to run multiple projects, you will need to run these manually from the bin\Debug folder, and then use the "Attach To Process" feature in Visual Studio to attach the debugger to those processes so that you can debug them.
Update
No, you cannot 'run' two different classes separately. A console application has only one entry point. However, if you're learning C# and testing code, you can use a switch statement.
For example:
void main()
{
Console.Write("Choose Option (1/2):");
var key = Console.ReadKey().KeyChar;
switch (key)
{
case "1":
{
var arrayTest = new ArrayTest();
arrayTest.Run();
break;
}
case "2":
{
var anotherTest = new AnotherTest();
anotherTest.Run();
break;
}
}
}
This way, when the app runs it will prompt you for a key, and you can press 1 or 2 to execute whatever you want.
With that said, for writing basic test code, I find using LINQPad significantly more productive as it bypasses the need of writing all of the boilerplate console application code.
There are 2 Reasons
Every Class have their own Main() Method
2.C# has case sensitive so method name like Main() not like main() it won't show in project properties window
---->Kept as startup project under project properties-->> Application--->
select a project which u want run
There are two types of reasons for this:
Every class has their own Main() method.
C# is case-sensitive, so method names like Main() are not like main() and won't show in the project properties window.
Solution: Keep as startup project under project properties --> Application --> select a project which you want run.

How to get the updated argument(s) of a build process template refreshed in 'Process' section of a build definition?

For a build process template, we can add/remove/edit the argument list of it and use it as variables within the build/work-flow steps. I reading the nice guide here
I'm cloning the template DefaultTemplate.11.1.xaml to sayHello-DefaultTemplate.11.1.xaml and edit it via Visual Studio 2012. I first added one argument, called TestMessage. I check in my changeset to the Source Control.
Then I create a build definition sayHelloBuild based on this template. And when I go to the Process tab, I can see TestMessage in the Misc section. I save the build definition. Trying to queue it and it gets succeeded.
Here comes the issue. I added another argument for the template named ABBCCC and checkin the source code. But when I edit the build definition sayHelloBuild, I cannot see ABBCCC in the Process tab as MyArugment01 does.
How can I get the argument list refreshed?
Within the arugments there is a property called "Metadata" as shown in the picture below. Click on the button at the right end and a window should pop up as shown in the second picture. Enter the name of the new variable in the Parameter Name and enter the other details (Display name etc). Save and check-in the build definition and you should be good to go.
Use the below script to delete the registed build process template in Database:
use Tfs_YourTeamCollectionName;
delete from tbl_BuildProcessTemplate where ProcessTemplateID = 'Your ProcessTemplateID';
Create new build definition again.
The database name is your TFS team collection name e.g. Tfs_YourTeamCollection in the backend SQL Server of your TFS server. I'm using TFS 2012.
Hope it helps!

Visual Studio custom wizard - add additional configuration

I am writing a visual studio custom wizard for creating C++ project.
I need to define additional build configuration, that inherits the debug configuration.
I googled a lot, but couldn't find anything.
I guess this should be done in the JScript file (default.js), AddConfig function, by calling proj.Object.AddConfiguration. But I couldn't find examples, nor syntax rules.
The only thing I found is : http://www.codeproject.com/Articles/200039/A-Visual-Studio-Wizard-to-add-more-project-configu but it is way too complicated, and I couldn't figure it out.
Can you please help?
Found it. It can be done using C# code:
solution.SolutionBuild.SolutionConfigurations.Add("Conf", "Debug", true);
//set Conf to be active build configuration
solution.SolutionBuild.SolutionConfigurations.Item("Conf").Activate();
//Get project
VCProject pro = solution.Projects.Item(1).Object;
//Get comiler tool for project
VCCLCompilerTool tool = pro.Configurations.item("Conf").Tools("VCCLCompilerTool");
//set Prprocessor definition for Conf
tool.PreprocessorDefinitions = "NEW";

Add a link to an existing item during project creation

I've created a custom VS template which uses an IWizard class to do some automatic actions when a user creates a project to set project properties and paths, I've managed to set some project properties like build path by saving the .csproj file with parameters inside $ signs and setting those parameters in the replacementDictionary, during the RunStarted method.
Unfortunately I'm having trouble adding items as links to the .csproj using the same method. I have a .cs file I need to add as an existing and as a link item to each project created, it's path would be determined by where the user chooses to save the project. I've got to the part where I know the path of the .cs file, (absolute and relative to the project's path).
Here's what I've tried so far:
Save the .csproj file with a section for the item, with placeholders for the path:
<Compile Include="$path_to_cs_file\cs_file_name.cs$">
<Link>$cs_file_name.cs$</Link>
</Compile>
I've tried doing this with both absolute and relative paths, but this for some reason makes VS replace the path with a completely different relative path under Documents and Settings\user\Local Settings.
In RunStarted, cast the automationObject as DTE and call it's ItemOperations.AddExistingItem method. Using either path results in errors (The parameter is incorrect).
In ProjectFinishedGenerating, save the project's path, then at RunFinished, create a Microsoft.Build.BuildEngine.Project object with that path, call DTE commands to save all files and unload the project, then call the project object's AddNewItem Method and SetMetaData on the resulting ProjectItem, afterwards I save the project and reload it with the DTE object, this, again results the same errors as before
I'd appreciate any help with the subject, I'm pretty much stumped. Thank you in advance.
I managed to "solve" this issue, what I did is the following:
Kept the placeholder in the csproj, but never added the related parameters to the replacement dictionary:
<Compile Include="$path_to_cs_file$\$cs_file_name.cs$">
<Link>$cs_file_name.cs$</Link>
</Compile>
At the ProjectFinishedGenerated method, unloaded the project, edited the csproj file to replace the paths, and reloaded the project:
projectFileName = project.FullName
// Unload file and manually add the linked item
dte.ExecuteCommand("File.SaveAll");
dte.ExecuteCommand("Project.UnloadProject"); // See Note Below
StreamReader reader = new StreamReader(projectFileName);
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, #"\$path_to_cs_file\$", ...);
content = Regex.Replace(content, #"\$cs_file_name\$", ...);
StreamWriter writer = new StreamWriter(projectFileName);
writer.Write(content);
writer.Close();
dte.ExecuteCommand("Project.ReloadProject");
Note: The above code assumes the project needed modifying is currently selected project, usually when ProjectFinishedGenerating runs this is the case, however in a multi-project template or if you've added a project manually to the solution this might not be the case, you'll have to call dte methods to choose your "main" project in the project explorer, then go on with unloading, editing, and reloading. The code to do so would look something like this:
UIHierarchy UIH = dte2.ToolWindows.SolutionExplorer;
UIHierarchyItem UIHItem = UIH.UIHierarchyItems.Item(1);
UIHItem.UIHierarchyItems.Item(testProjectName).Select(vsUISelectionType.vsUISelectionTypeSelect);
An alternative solution if you don't want to mess around with IWizard is to set CreateInPlace to true in your vstemplate in TemplateData.
<CreateInPlace>true</CreateInPlace>
I'm having exactly the same problem and it's driving me mad.
I have found one really dirty workaround however:
In my situation I am using the following in the RunStarted method:
EnvDTE.DTE dte = automationObject as EnvDTE.DTE;
string solutionPath = System.IO.Path.GetDirectory(dte.DTE.Solution.FullName);
This returns a path which includes the folder "documents". Calling System.IO.Directory.Exists() confirms that this is a valid directory, however on checking my file system, it seems that this does not exist. If you replace "documents" with "my documents", and then continue to use that path for the linked item, all works perfectly.
So it seems that VS is getting confused with the "documents" directory alias and therefore defaulting to some crazy "AppData" directory instead.
I hope this helps, but if you find a better way to do this, please let me know!

"Run Custom Tool" for resx files in MVC2 project (from an external application/script)

I am trying to get the localization for my MVC project working with our existing infrastructure for editing string resources. We store all our resource string in database tables and have a front end web UI to edit them with, and an export application which generated the .resx files. This all works great, but I am having a little difficulty with a new project using MVC2 and VS2010.
I have asked another question on this, the answer to which almost got me there, but not quite.
I have now changed the resources to be in a Resources folder (instead of App_GlobalResources), as recommended by a number of people. And have the following settings against my .resx files ...
Build Action = Embedded Resource
Copy to Output Directory = Do not copy
Custom Tool = PublicResXFileCodeGenerator
Custom Tool Namespace = Resources
File Name = MyApp.resx
I have changed my export application to run the resgen.exe tool with the following parameters ...
string args = string.Format("/publicClass \"{0}\" /str:cs,Resources,{1},\"{2}\"", resourceFile, Path.GetFileNameWithoutExtension(resourceFile), csFilename);
... which generates an almost identical .designer.cs file as I get when I add the .resx file to my project initially. The only difference is the
The generated .designer.cs file differs slightly from the file I get when I run the resgen.exe tool from within my export application.
This is the code generated by VS2010 when I first add the .resx file to my Resources folder ...
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Resources.MyApp", typeof(MyApp).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
... the difference when I run the resgen.exe tool is that is prefixs MyCompany.MyApp to the namespace in the constructor to ResourceManager
new global::System.Resources.ResourceManager("MyCompany.MyApp.Resources.MyApp", typeof(MyApp).Assembly);
Now, this to me seems to be a bug in the resgen.exe tool, because I've told it that the Namespace for my resources is Resources, not MyCompany.MyApp.Resources.
So, is there a fix/work-around for this problem?
The only thing I can think to do at the moment is to post-process the generated .designer.cs file with powershell and fix it!
Finally, I have solved the problem.
I decided to simplify things a bit by breaking my resources out in to a new assembly called Resources. I then added my resx files and set the properties for them as below ...
Build Action = Embedded Resource
Copy to Output Directory = Do not copy
Custom Tool = PublicResXFileCodeGenerator
Custom Tool Namespace = Resources
File Name = MyApp.resx
I then changed my export application to run ...
resgen MyApp.resx /str:c#,Resources,MyApp,MyApp.designer.cs /publicClass
... and to delete *.resources from the folder (created by the resgen.exe utility, but not needed)
This got rid of the prefix on the constructor to ResourceManager, and then i just added a reference to my new Resources assembly to my web application.
I've run a few tests to make sure all is good, including going in to the .designer.cs file and deleting one of the properties to cause a compiler error. Then re-ran my export app, and everything worked again.
So, I am a happy bunny!

Resources