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.
‘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 Nothing) Then
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.
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 Nothing) Then
For Each prop As [Property] In 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.
‘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 Nothing) Then
‘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.
window.OutputString(“Project: “ & proj.Name & vbCrLf)
‘ Get the configuration manager
Dim cm As ConfigurationManager = proj.ConfigurationManager
If Not (cm Is Nothing) Then
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.
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.
‘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.