Thursday, January 19, 2006

Determining Name And Path Of Output File in VS.NET 2005 Automation

When implementing an extension, e.g. an addin for VS, you may want to determine the path (location) and name of the assembly that is being built as part of a project. One way would be to read the project file, e.g. *.csproj. As it happens to be an XML file it is easy to determine the correct node. A different approach though is using the Project automation object. Project has a set of properties and for C#/VB.NET/J# projects it contains a property named "OutputFileName". The value of that property is the assembly name. Next, you need to determine the path. First you need to know the path to the project, which you can determine using the directory part of Project.FileName. This gives you the location of the project file. In order to determine the output path for the assembly, you need the active configuration for the project, which you obtain using Project.ConfigurationManager.ActiveConfiguration. The ActiveConfiguration object has a property named "OutputPath", which is the relative path from the project file to the output folder. One might be tempted to use ActiveConfiguration.OutputGroups. For that to work the project needs to have been built successfully once, which might not always be the case. Furthermore, Configuration.OutputGroups doesn't work in a handler for the solutions event Opened. Therefore I settled on the following code for determining the path and the name of the target assembly:
string GetOutputPathAndFile(Project p) {
   string outputPath;
   string outputFileName;
   string outputPathAndFile;
   string projectLocation;
   try {
      Configuration activeConf = p.ConfigurationManager.ActiveConfiguration;
      projectLocation = (new FileInfo(p.FileName)).DirectoryName;
      outputFileName = p.Properties.Item("OutputFileName").Value.ToString();
      outputPath = activeConf.Properties.Item("OutputPath").Value.ToString();
      outputPath = Path.Combine(projectLocation, outputPath);
      outputPathAndFile = Path.Combine(outputPath, outputFileName);
   }
   catch(Exception ex) {
      System.Diagnostics.Debug.WriteLine(ex.Message);
   }
   return outputPathAndFile;
}
If you are aware of a better solution, please don't hesitate to comment! I'm eager to learn.

2 comments:

Matt said...

My experiments showed that this works just fine for C# (and presumably VB) projects. However, the exception coding is triggered on C++ projects. Since my C++ projects target Win32, I'm not sure if it would work on a C++ project that targets .NET or not (I suspect not).

Mind you, I haven't come up with anything better yet...

BPFH said...

My ultimate solution was generally similar to yours. It differs in a number of respects, including:

I explicitly test to make sure I don't run it on a "Miscellaneous Files" project (Constants.vsProjectKindMisc), which can cause exceptions because those projects don't have a configuration manager object.

For my analog to your code (I use the FullPath, OutputPath, and OutputFileName properties), I've added a check to make sure that it's a C# (PrjKind.prjKindCSharpProject) or VB.NET (PrjKind.prjKindVBProject) project. This is to make sure that the properties I'm referencing actually exist.

I've added a clause to allow me to retrieve the path for a VC++ project. The Microsoft.VisualStudio.VCProjectEngine namespace came in quite handy here.

Code's available if you want it--I did, after all, base my original code on yours, so it only seems fair to offer mine.

Post a Comment

All comments, questions and other feedback is much appreciated. Thank you!