P3.NET

App.config Magic In Visual Studio

One area that causes lots of confusion to developers is the behavior of app.config while running within Visual Studio.  Because VS treats app.config specially it can be confusing when app.config does not work the way we expect.  This post will clarify how the config file is treated while running inside VS.  But first an aside.

Visual Studio Hosting Process

Debugging another process is not trivial.  There are lots of details to worry about.  Consequently debuggers are not simple pieces of code.  Starting a debugger can be expensive.  Beginning a few versions back VS started using the VS hosting process for debugging processes.  This process (vshost) is a wrapper around the debugger.  It allows the debugger to be started onced and then continue to run between debug sessions.  This helps to alleviate the overhead of the debugger and to also allow the debugger to maintain information between debugging sessions. 

The vshost process is started when VS loads a project that is using it.  You can see this by using Task Manager to see the running processes.  The actual process is called <project>.vshost.exe.  It is within this process that your code runs rather than through the normal <process>.exe.  Running through a host process is generally the best idea but it can cause some issues.  Therefore in the Debug properties of the executable’s project settings is an option to turn off the hosting process.

App.config

In .NET an application configuration file is required to be called <process>.exe.config.  This is the file that the configuration subsystem will load.  The file must reside in the same directory as the executable.  Because VS recreates the output directory each time you build you cannot just create this file and store it in the output directory.  Instead when you add an Application Configuration File item to your project it is added to the root of the project and called app.config.  This file is automatically copied to the output directory and renamed on each build.  Therefore after each build you will have a brand new configuration file.  Note that if you call the file anything else then this process will not work.  Rarely should you change the name.

For most development you will edit the configuration file in VS.  These changes will be automatically seen when the program runs under the debugger.  However if you make changes to the configuration file at runtime (while running under the debugger) the changes will only persist until the next build. At that time VS will overwrite the file with the root copy.  Therefore you CAN NOT run your program and modify the project’s configuration file.  The only way to emulate this behavior would be to copy the (modified) configuration back to the root project and rename it.  As an aside note that if you modify the configuration file at runtime but do not rebuild your code then the configuration changes may persist. 

Which Config Is Which?

One area of confusion is which configuration file your application is actually using while running under the debugger.  Many people will say it is the <process>.exe.config file but that is only partially correct.  Remember the vshost process of earlier?  It complicates things.  Remember that if you are using the VS host (the default) then your process is actually <process>.vshost.exe.  The configuration subsystem knows nothing about the VS host process so it expects to load the <process>.vshost.exe.config file.  When a project is configured to use the hosting process then VS will copy the standard <process>.exe.config file to <process>.vshost.exe.config when the debugger starts.  This gives the appearance that the hosting process is using the application’s configuration file when in fact it is VS that is just copying the file for us.  More importantly the configuration subsystem is not impacted by this change at all.  If the program modifies the configuration file while it runs then those changes are made to the <process>.vshost.exe.config file.  The actual process’s configuration and the project’s app.config files are not modified.   

DLL Config Files – Myth or Fact?

Many class libraries (DLLs) require configuration information.  Most devs like to rely on the configuration subsystem to store this information.  Makes sense.  If you add an application configuration to the class library project then VS will copy the app.config to the output directory and call it <project>.dll.config as you would expect.  It behaves just like a standard Windows application project.  Here’s the issue though – the configuration subsystem neither understands nor supports these files.  Let me say it again.  The configuration subsystem WILL NOT load .dll.config files. 

Technically you can change the mappings used by the subsystem to get it to load the files but that is beyond the scope of this article and beyond most applications.  The configuration subsystem will only load the <process>.exe.config file.  There are forum postings all the time from devs who are confused as to why their dll.config file contains some configuration section but the configuration subsystem won’t read it.  They believe, incorrectly, that the subsystem uses the configuration file associated with the assembly that is calling it.  That is false.  The subsystem only reads the application’s configuration file.

There are two possible workarounds to this issue.  The first workaround, the most common, is to merge the dll.config file into the application’s configuration.  This is generally the easiest approach and can be done at build time.  The alternative approach is to read the configuration file manually at runtime.  Some complex libraries follow this route but I cannot recommend it.  That is just too much code to write.

UpdateUserType (Visual Studio Addin)

Here is another addin I wrote for VS2008 and then updated for VS2010.  In C++ you can define your own custom keywords and VS will color them differently if you say so (ToolsOptions -> Fonts and Colors).  There are a couple of problems with the VS solution.

  1. You have to edit the UserType.dat file.
  2. You have to restart VS.
  3. You have only one set of keywords for all of C++.

The second and third problems are the killers here.  As you are developing code you will often find a set of keywords you’ll want to add.  To do so you’ll have to open the .dat file, add the keywords and then restart VS.  Even worse is that if you are working on an MFC project then you’ll likely want some MFC keywords but if you switch to Win32 then you want a different set of keywords.  To resolve this problem you’ll have to keep multiple .dat files that you can swap in and out.  Enter UpdateUserType.

UpdateUserType does a couple of things. Firstly it allows you to separate your keywords into different files (i.e. C++, MFC, Win32).  Secondly UpdateUserType can merge all these files into a single .dat file.  Thirdly the addin will detect when the .dat file changes and request VS to refresh its keyword list.  

Using the addin UI you can add a list of .txt files to be monitored.  Whenever one of these files is modified the addin merges them all into a new .dat file.  This allows you to separate keywords by area and enable only the keywords appropriate for your current project.  My personal technique is to keep one or more of them open in a separate editor so that I can add new keywords as I go through code.  Saving the files in the editor causes the .dat file to be regenerated.

When the .dat file changes the addin requests VS to refresh the keyword list.  When this happens VS will read the .dat file and begin coloring the keywords again.  This feature allows you to add keywords on the fly and have VS begin coloring them almost instantiately. 

Attached is the source and binaries for the VS2010 version of this addin.  Additionally I’ve included some starter .txt files that you can use.  A caveat is in order.  The .XML file where the addin stores the files to be merged supports relative or absolute paths.  However the UI and the code only supports absolute paths.  Therefore if you use the UI to change the file list then the paths are converted to absolute paths.

To install the addin copy the contents of the Bin directory to your %My Documents%Visual Studio 2010Addins directory.  You might need to enable the addin via ToolsAddin Manager.  If the addin does not show up (it’ll appear under the Tools menu) then you might need to force a re-registration.

Code

CodeHTMLer (Visual Studio Addin)

CodeHTMLer is a utility written by Weshaggard and available here.  The utility allows you to convert code to HTML for insertion into a website or blog.  It happens to be the tool I use for code on this site.  I know, I know.  There are many tools already available for this but I prefer CodeHTMLer for several reasons.

  1. It supports multiple languages.
  2. The HTML is partially configurable via the XML file it loads.
  3. Some code elements can be ignored rather than having to generate style information for everything.
  4. Can use either existing CSS styles or inline styles.
  5. It is fast.

The only issue I had with it was that I wanted to use it inside Visual Studio.  Being a developer I did the logical thing and wrote an addin for it.  The addin was originally written for VS2008 and the updated for VS2010.  The attached code runs under VS2010.  The addin wraps CodeHTMLer and adds the following features.

  1. Copy as HTML is exposed as an option in the editor.
  2. A UI is added to allow you to change the options in the XML file to control how HTML is generated.
  3. Uses the VS code model to determine which language definition to use for formatting so you can format VB one way and C# another, for example.

I only ran into a couple of problems with CodeHTMLer.  These mainly revolved around the manner in which it expected to be used.  I had to modify (and annotate) the areas I changed.  Furthermore I needed a few additional options for controlling formatting so I extended the language definition code to add a few extra attributes along with the serialization logic.  So the version that is included here is a modified version of the one available from CodePlex.  As a final note the attached language definition file is modified to support how VS names languages.  The original version did not use the same name for C# as VS.

IMPORTANT: I don’t take credit for CodeHTMLer or any of its source.  I only take credit for the addin code.  Please support the original author for CodeHTMLer.

The current project is for VS2010 but you can recompile the code if you want a VS2008 version.  To use this code extract the binaries to your addin directory (%My Documents%Visual Studio 2010Addins).  In VS you probably need to enable the loading of the addin via the ToolsAddin Manager command.  If it does not show up then you might need to modify the addin file to force a re-registration.  The addin shows up as an item in the Tools menu and as a context menu option when you right-click in the code area.

FAQ: Rectangular selection in Visual Studio

Q: I need to delete some code from several lines and the code is well formatted.  Can I use some sort of regular expression to replace the code?

A: You could but this might be overkill.  VS has supported rectangular selection for a while.  With rectangular selection you can select a block of code and then select that same block on several consecutive lines and then delete the selected code. 

Do the following to use rectangular selection.

  1. Place the caret on the column where you want to begin selection.
  2. Hold down the ALT key.
  3. Drag the mouse to select the region you want to select.  If you cross lines then the same columns are selected on all the lines.
  4. Release the ALT key and then press DEL to delete the columns from each line.

Starting with VS2010 some new capabilites were added.  They are discussed here.  My personal favorite is the ability to replace the same block of code on all lines (such as renaming private to public). 

To do that just select the region that you want to replace and then begin typing the replacement text.

FAQ: Temporary Projects

Q: I create a lot of test projects and I am getting tired of cleaning up the temporary projects directory and clearing out the recent projects menu.  Is there some way to get VS to do this for me?

A: Yes there is.  Under ToolsOptions -> Projects and Solutions there is an option called Save new projects when created.  Unchecking this option will allow you to create (most) new projects without saving them.  It will still end up in your temporary projects folder but it will be automatically deleted when you close the project unless you chose to save it.  Furthermore it will not be added to the MRU project list.

 There are some restrictions to this option.  Firstly not all project types support it.  C++ does not while C# and VB do.  Secondly, you cannot create multiple projects in a single solution without saving it first.  You also cannot do some project changes without first saving the project.  Avoid using the Save All button as this will prompt you to save the project.  However you can add files and compile and debug without saving.

FAQ: Toolbox is slow to populate

Q: Whenever I accidentally mouse over the Toolbox it locks up VS for a while.  What is going on?

A: This should only happen the first time you mouse over the Toolbox after starting VS.  VS is populating the window with the controls that are defined within your solution in addition to the pre-defined controls.  This can be a slow process.  To speed it up go to ToolsOptions -> Windows Form DesignerGeneral and set AutoToolboxPopulate to false.  This will tell VS not to scan your solution looking for controls to add to the toolbox.

 

FAQ: Adding a lot of files to a solution

Q: I need to add a lot of existing files and folders to a project but using Add Existing Item is slow.  Is there a faster way?

A: When you select a project in Solution Explorer you might have noticed the toolbar at the top of the window changing.  One of the option (in most projects) is Show All Files.  This option shows all files and folders under the project folder whether they are in the project or not.  Files/folders not in the project are shown grayed out.  If you right-click the files/folders (multiple selection is supported) you can right-click and select Include in Project to automatically add them to the project.  If you add a folder it will automatically include any children in the folder.  This is a great way to add a lot of items when they already reside in the project directory.

If the files do not already reside in the directory then you can drag the files from Windows Explorer to the project directory.  Unfortunately this does not work in all projects.  Also be careful about what actually happens when you drop the file.  For projects like C# that assume all files are in the project directory then a copy is performed.  However some project types may result in a link being created to the original file location.

FAQ: Access Denied when signing an assembly

Q: When I try to sign an assembly I get an access denied error.  What is going on?

A: By default you can not sign an assembly unless you have administrative privileges.  This is due to security set on the key(s) used for signing.  To enable your account for signing assembly you will need to modify the NTFS security on the folder %ALLUSERS%Application DataMicrosoftCryptoRSAMachineKeys to give your user account (or better yet a group in which your account is a member of) full control.  You will need read & execute, list and modify rights.  You can then sign assemblies without administrative privileges.

Integrating GAC Assemblies with Visual Studio

Originally published: 19 February 2006

Updated: 20 May 2010

The question comes up all the time about how to get documentation and debugging support for GAC assemblies into Visual Studio (VS).  This article will discuss the steps it takes to make this happen.  The steps are straightforward once you understand what goes on behind the scenes.

Visual Studio and the GAC

The first thing that must be understood is the fact that VS does not reference assemblies from the GAC.  If you open the Add References dialog in VS you will notice that none of the assemblies are actually coming from the GAC.  Instead the .NET framework assemblies are pointing to C:WindowsMicrosoft.NETFrameworkvx.y.z while SQL and Office point to different directories.  VS ALWAYS refers to assemblies from regular directory paths.  At runtime the CLR will load GAC assemblies from the GAC but this is independent of VS.

The next thing to understand is that the GAC only supports storing assemblies.  Documentation and debug files, along with configuration or other files, can not go in the GAC.  Therefore if VS did use the GAC it still would not have access to the documentation or debug files of assemblies.  So how do we get VS to recognize these files?

Assembly Folders

All assemblies along with their debug (.pdb) and documentation (.xml) files should be installed into a normal directory.  This is in addition to placing the assemblies in the GAC.  The target directory for these files is known as the assembly folder.  It is this folder that you will point VS to.  When VS loads an assembly it will automatically pick up any .pdb and .xml files in the same directory provided they match the assembly.  If VS loads the .pdb file then debug information is available for the assembly.  If VS loads the .xml file then Intellisense will display documentation about the members of the assembly.  This is automatic.

So, when installing your GAC assemblies, copy the original assemblies, the associated .pdb files and the .xml documentation files to a normal directory.  Then install the assemblies into the GAC.  Now all you need to do is have your users point VS to the appropriate folder and they’ll get debugging and Intellisense support.

Registering Your Assembly Folder

I know what you are thinking.  “But SQL, Office and .NET itself automatically shows up in Add References dialog.  My assemblies do not even though I put them into the GAC.  What is going on?”  Please refer to what I said in the first part of this article: “VS ALWAYS refers to assemblies from regular directory paths.”  The Add References dialog IS NOT populated from the assemblies in the GAC.  VS must be told where the assemblies come from.  Enter the Registry.

When VS wants to get the assemblies for the Add References dialog it will query the Registry for the keys under HKLMSoftwareVisualStudio8.0AssemblyFolders.  Each key under the main key represents an assembly folder.  The actual name of each key is irrelevant but the default value of the key must be the full path to an assembly folder.  VS will add all the assemblies from the assembly folder to the Add References dialog.

Update: Beginning with Visual Studio 2008 the way VS finds assemblies has changed.  The aforementioned registry key still works but a new path is also used (although this has not been formally confirmed).  For VS2008 the following key can also be used: HKLMSoftwareVisualStudio9.0ExpAssemblyFolders .  For VS2010 the path is HKLMSoftwareVisualStudio10.0ExpAssemblyFolders.

Therefore if you want your assemblies to automatically show up in the dialog all you need do during installation is to add a new subkey under the base key, given earlier, and specify the full path to your assembly folder as the default value.  VS will automatically list your assemblies when the dialog is displayed.  Assuming that you followed the steps given earlier in the article the user will also automatically have debugging and documentation support. 

Here is an example of what you might have in the Registry:

HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFoldersAvalonAssemblies => (Default): C:WindowsMicrosoft.NETWindowsv6.0.4030
HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFoldersKraken => (Default): C:Program FilesKrakenv2.1.0

Multi-targeting in Visual Studio

Beginning with VS2008 you can now target different versions of the framework.  This introduces an issue for assembly references.  Some references can only be used with certain versions of the framework.  The existing registry key doesn’t differentiate between the (now) large number of frameworks available so a new set of keys are used.  Under HKLMSoftwareMicrosoft.NETFramework is a new set of directories that VS will also search.  Through some simple (aka not official) testing it turns out that VS will search a bunch of different paths.  Under the key it searches AssemblyFolders.  Additionally it will search for <version>AssemblyFoldersEx under the version of the framework you are targeting.  This is how VS can filter out assemblies that are not valid for a particular framework. 

A word of caution here: if you are running x64 of Windows then some of these keys reside under the x64 tree (HKLMSoftware) while others reside under the x86 tree (HKLMSoftwareWow6432Node).  VS will search both sets.  Also, as an aside, note that VS will check in HKCU as well.

Here is an example of what would be searched if you are targeting the v4 framework in VS2010

 HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFolders
HKLMSoftwareMicrosoftVisualStudio9.0ExpAssemblyFolders
HKLMSoftwareMicrosoftVisualStudio10.0ExpAssemblyFolders
HKLMSoftwareMicrosoft.NETFrameworkAssemblyFolders
HKLMSoftwareMicrosoft.NETFrameworkv4.0.30319AssemblyFoldersEx
HKLMSoftwareMicrosoft.NETFrameworkv3.0AssemblyFoldersEx
HKLMSoftwareMicrosoft.NETFrameworkv2.0.50727AssemblyFoldersEx 

Putting It All Together

So, in summary, to expose your GAC assemblies, debug files and documentation files to VS do the following.

  1. Create a regular directory for your files.
  2. Copy the assemblies, debug files and documentation files to the directory.
  3. Install the assemblies into the GAC.
  4. Create a new subkey in the registry in the appropriate version-specific key (HKLMSoftwareMicrosoft.NETFramework<version>AssemblyFoldersEx).
  5. Set the default value of the key to the directory created in step 1.

Caveats

When I say that VS does not use the GAC please be aware that it might but not in a manner in which we can take advantage of it here.  You will also notice that the core framework is not listed in the Registry.  Whether the framework is hard-coded into VS or whether it uses a different techique I can not say for sure.  Nevertheless it is not extensible for our purposes.

Configurations and Platforms in Visual Studio

(Originally published: 7 June 2008)

Anyone who has worked with Microsoft Visual Studio (VS) knows about configurations but not many people, other than handheld developers, know about platforms.  This article will discuss configurations and platforms as they pertain to Visual Studio.  There seems to be more interest in extending Visual Studio these days so I will provide Visual Studio macros, where possible, to give you a better grip on the discussion.

Before we get into configurations and platforms we need to first clarify the relationship between a solution and its projects.

Solutions

A solution is a collection of projects.  A solution is used to group a set of related projects into one cohesive unit.  Most of VS is solution centric.  Therefore when we talk about building one’s code we are generally talking about building the solution and all the projects within it.  There are exception of course.  Most of the debug functionality is specific to the startup project, for example.  VS only supports one open solution at a time. 

The important part to remember about a solution is that it has few configurable properties beyond the projects it contains.  The few configurable properties a solution does have generally match the same properties in a project.  These properties are initially linked such that the property value for each project matches that of the solution.  This can be changed when we discuss configurations later. 

A solution folder is a virtual folder contained within a solution.  Unlike most project folders it does not actually exist on disk and, hence, will not show up in any source control system (CM).  A solution folder is generally used to hold solution-level files such as make files, documentation files, etc.  Items in a solution folder are not part of any build nor are they deployed with the solution.

Projects

A project is a collection of files.  Most files within a project are source file that get compiled down to a single output file.  The output file is generally an assembly.  These types of projects can be referred to as code projects.  Other project types that do not (necessarily) generate assemblies include reporting projects, unit tests and database projects.

Each project has a project type such as WinForms app, console app, web service, etc.  The project type determines the properties available on each project.  Code projects generally have properties to control compilation and the output files.  Code projects generally have an associated source langauge as well.  All these properties are exposed through the project’s properties.  Some of these properties, such as type and language, are set when the project is created and cannot be changed.  (Note: you can change between WinForm, class library and console apps but this is an exception rather than the norm because it merely changes a linker setting.)

For many projects you can create project folders.  Some project types and languages (such as C#) create physical folders to back the project folders.  If you look at these folders on disk then they should mimic the layout you see in Solution Explorer.  Given that a solution is a collection of projects and a project is a collection of files we can write a macro that will dump all projects, folders and files in the active solution.

Public Sub DisplaySolutionFiles()
    ‘Get the output window 
    Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Files”)

    ‘Enumerate projects of the solution 
    For Each proj As Project In DTE.Solution.Projects
        window.OutputString(String.Format(“Project: {0} Kind: {1}” & vbCrLf, _
                                    proj.Name, proj.Kind))

        ‘Enumerate the files 
        ListProjectItems(proj.ProjectItems, window, 1)
    Next
End Sub

Private Sub ListProjectItems(ByVal items As ProjectItems, _
                       ByVal window As OutputWindowPane, ByVal level As Integer)
    If Not items Is Nothing Then
        For Each item As ProjectItem In items
            If item.Collection Is items Then
                ‘List the item 
                window.OutputString(IndentString(level) & item.Name & vbCrLf)

                ‘List any children 
                If Not (item.ProjectItems Is NothingThen
                    ListProjectItems(item.ProjectItems, window, level + 1)
                End If
            End If
        Next
    End If
End Sub

A few points about the code.  The active project (as determined by Solution Explorer) is accessible via DTE.ActiveSolutionProjects.  To get a list of all the projects in a solution use DTE.Solution.Projects.  You should first consider verifying a solution exists.  If the solution has a solution folder then it will appear as a project even though it is not truly one.

Now that we can get the projects and files inside a solution the next step is to get the properties of a project.  Here is a macro to dump the properties of a project.

Public Sub DisplayProjectProperties()
    Dim window As OutputWindowPane = GetOutputWindowPane(“Project Properties”)

    ‘Enumerate projects 
    For Each proj As Project In DTE.Solution.Projects
        window.OutputString(“Project: “ & proj.Name & vbCrLf)

        ListProperties(proj.Properties, window, 1)
    Next
End Sub

Private Sub ListProperties(ByVal props As Properties, ByVal window As OutputWindowPane, _
                           ByVal level As Integer)
    If Not (props Is NothingThen
        For Each prop As [PropertyIn props
            window.OutputString(IndentString(level) & prop.Name & “: “)
            Try
                window.OutputString(prop.Value)
            Catch ex As Exception
                window.OutputString(“<“ & ex.Message & “>”)
            End Try
            window.OutputString(vbCrLf)
        Next
    End If
End Sub

You should see properties for the target framework version, output file name (but not path) and root namespace information.  Some projects will not have any properties so we have to check for nothing first.  Notice also the try-catch to handle the case where retrieving a property value will throw an exception.  It can happen as some properties don’t apply to all projects.  Also be aware that if a property is not set within a project then it might not appear in the list.  The various build events (which are backed by properties) will only appear as properties if they are set within the project.

Configurations

You might have noticed that the above macro doesn’t seem to display many important properties such as the output path and compilation settings.  That is because build settings (and friends) might change depending upon the type of build you want (debug or release, for example).  This is where configurations come in.  A configuration is a collection of project properties.  For most projects you will have two configurations by default: Debug and Release.  The Debug configuration is used during development.  It sets up the project properties to allow for easier debugging.  The Release configuration is used when you are ready to deploy a project.  This configuration removes most of the debugging information and enables many code optimizations to speed up your code.  You can create your own custom configurations as well.  The following code will display the name of each configuration for a project.

Public Sub DisplayProjectConfigurations()

   ‘Get the output window
   Dim window As OutputWindowPane = GetOutputWindowPane(“Project Configurations”)

   ‘Enumerate projects
   For Each proj As Project In DTE.Solution.Projects
      ListProjectConfigurations(proj, window)
   Next
End Sub

Private Sub ListProjectConfigurations(ByVal proj As Project, _
                                      ByVal window As OutputWindowPane)
   window.OutputString(“Project: “ & proj.Name & vbCrLf)

   ‘ Get the configuration manager
   Dim cm As ConfigurationManager = proj.ConfigurationManager

   If Not (cm Is NothingThen
         ‘Get the active configuration
         Dim active As String = cm.ActiveConfiguration.ConfigurationName

         ‘Enumerate the configuration names
         For Each config As String In cm.ConfigurationRowNames
            window.OutputString(IndentString(1) & configName)

         If active = configName Then
            window.OutputString(” [Active]”)
         End If
         window.OutputString(vbCrLf)

         ‘List configuration properties
      Next
   End If
End Sub

The configurations of a project are accessed via the ConfigurationManager property.  The CMs job is to keep track of the various configurations defined for a project.  The active configuration is accessible via the CM’s ActiveConfiguration property.  Now that we know what configurations are available we can list the properties associated with a specific configuration.

Private Sub ListProjectConfigurations(ByVal proj As Project, ByVal window As OutputWindowPane)
   window.OutputString(“Project: “ & proj.Name & vbCrLf)

   ‘ Get the configuration manager
   Dim cm As ConfigurationManager = proj.ConfigurationManager

   If Not (cm Is NothingThen
      Dim active As String = cm.ActiveConfiguration.ConfigurationName

      For Each configName As String In cm.ConfigurationRowNames
         window.OutputString(IndentString(1) & configName)

         If active = configName Then
            window.OutputString(” [Active]”)
         End If
         window.OutputString(vbCrLf)

         ‘List configuration properties
         Dim configs As Configurations = cm.ConfigurationRow(configName)
         For Each config As Configuration In configs
            ListProperties(config.Properties, window, 2)
         Next
      Next
   End If
End Sub

The only tricky part is that a configuration row is actually a collection of Configuration objects.  Generally it’ll only have a single configuration but just to be safe we enumerate them all.  You can compare the results of the macros with Visual Studio by using Solution Explorer and the project’s property pages.  At the top of the property pages you should see a combo listing each of the supported configurations. For pages that contain configuration-level properties the combo is enabled.  The results you get from the pages should match the macro results.

Configuration Manager

Solutions have configurations just like projects.  Unlike a project configuration however a solution configuration has only one set of properties: the configuration of the projects.  The settings of a solution configuration is controlled by the Configuration Manager.  You can access the CM by right-clicking the solution and selecting Configuration Manager.  It might look something like this.

By default the solution configuration shares the same name as all the project configurations within the solution.  This is by design and generally how you will want to work.  If you have ever changed active configurations using the toolbar in Visual Studio you should realize that what you are actually changing is the solution configuration, not the project configuration.  The active solution configuration determines the active configuration for each project it contains.  You also have control over whether the project gets built or not.  In general all projects will be built but if you mix, say, unit tests with your source code you might create a solution configuration that builds all the projects while you have another configuration that builds only the source code.  You configure all that through this dialog.  Here is a macro to enumerate the properties of all the solution configurations.

Public Sub DisplaySolutionConfigurations()
   Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Configurations”)

   ‘Enumerate the solution configurations 
   Dim build As SolutionBuild = DTE.Solution.SolutionBuild
   Dim configs As SolutionConfigurations = build.SolutionConfigurations
   For Each config As SolutionConfiguration In configs
      window.OutputString(“Configuration: “ & config.Name & vbCrLf)

      For Each context As SolutionContext In config.SolutionContexts
         window.OutputString(IndentString(1) &
                String.Format(“{0} – {1} – Build: {2}, Deploy: {3}” & vbCrLf, _
                   context.ProjectName, context.ConfigurationName, _
                   context.ShouldBuild, context.ShouldDeploy))
      Next
   Next
Endd Sub

 When you run this macro you might notice (what appears to be) duplicate entries for the same project.  You will see why shortly.

Platforms

Up to this point we have totally ignored the platform that you see next to each configuration.  It is time to take a look at it now.  A configuration controls what settings to use for building a project (or solution).  A platform determines what target machine a project will be built for.  Take a look again at Configuration Manager in Visual Studio. For .NET code you’ll see the platform is Any CPU.  This means that your code is machine agnostic.  If you run your code on a 32-bit OS then your code will be x86.  If you run it on a 64-bit OS then your code will run as x64.  This is one of the advantages of .NET.  It doesn’t target the machine until you run it.  This is generally what you want so you’ll rarely make any changes to this.

There are exceptions though.  If you are working with native code or mixed mode then you have to make sure that the code is all either x86 or x64.  If you don’t then your code will not run.  You cannot mix x86 and x64 code in the same process.  If you are using .NET to interop to unmanaged code then you have to ensure the platform for both sets of code are the same otherwise the code might run on a 32-bit OS but fail on a 64-bit OS.

Update: Starting with Visual Studio 2010 all executables will target the x86 platform rather than Any CPU.  The reasoning behind this (reasonable) decision is discussed here.

This is where platforms come in.  By default only a couple of platforms are associated with a solution or project.  Like configurations a solution can have one set of platforms while a project has another but it is generally a good idea to keep them in sync.  You will again use Configuration Manager to modify the platforms in a solution or project.  There are no configurable properties for a platform other than determing what platform will be targeted for each solution configuration.

Let us take a look at a more real-world situation.  Say you are migrating an application from C++ to .NET.  Your .NET code is, by default, platformed to Any CPU since you do not care what the architecture is.  However your C++ code is x86 only.  In order to support both 32-bit and 64-bit OSes you have no choice but to either build your .NET code as 32-bit only or create two different versions of your C++ code, one for x86 and one for x64.  Generally speaking it is easier to target x86 in .NET.  You would therefore leave the default platforms alone (to save time when you eliminate the C++ code later) and create a new platform called x86.  Under this context, for each solution configuration, you will build your projects for the same configuration. For each project you will also modify the platform to target x86.  In the VS toolbar next to the solution configuration you will also need to select the solution platform (x86).  Your built code will now run as x86 whether it is running under a 32-bit OS or a 64-bit OS..

An important point to remember about platforms is that each solution configuration/platform combination represents a different solution context.  Yes this is the exact same context the macro we wrote earlier displays.  As an example it is possible to have a Debug/x86 solution context in which project A Debug/x86 is built and project B Debug/x86 is not built.  At the same time you can also have the Debug/x64 solution context in which both project A and B Debug/x64 is built.  Each context is distinct.  The project configuration/platforms work the same way.  In general you don’t realize this because the platform is automatically determined but in reality it is the combination of configuration/platform that determines the project (and solution) configuration settings.

Here is an updated macro to include the platform information.

Public Sub DisplaySolutionConfigurations()

   ‘Get the output window
   Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Configurations”)

   ‘Enumerate the solution configurations
   Dim build As SolutionBuild = DTE.Solution.SolutionBuild
   Dim configs As SolutionConfigurations = build.SolutionConfigurations
   For Each config As SolutionConfiguration In configs
      window.OutputString(config.Name & vbCrLf)
      For Each context As SolutionContext In config.SolutionContexts
         window.OutputString(IndentString(1) & context.PlatformName & vbCrLf)
         window.OutputString(IndentString(2) & String.Format(“{0} – {1}|{2} – Build: {3}, Deploy: {4}” & vbCrLf, _
                            context.ProjectName, context.ConfigurationName, _
                            context.PlatformName, context.ShouldBuild, context.ShouldDeploy))
      Next
   Next
End Sub

If you got duplicate entries again then your code is correct.  The problem is that there doesn’t seem to be any way, at this time, to get the platform associated with each solution configuration.  The solution context information refers to the project for which it is associated.  Unfortunately I am not aware of a way around this limitation at this time.