Configurations and Platforms in Visual Studio
Created: 7 June 2008
Contents
- Solutions
- Projects
- Configurations
- Configuration Manager
- Platforms
Get the source code for this article here.
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 2008. 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 nd 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 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.
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 Nothing) Then
For Each prop As [Property] In props
target.OutputString(IndentString(level) & prop.Name & ": ")
Try
target.OutputString(prop.Value)
Catch ex As Exception
target.OutputString("<" & ex.Message & ">")
End Try
target.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()
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.
'List configuration properties
Dim configs As Configurations =
cm.ConfigurationRow(configName)
For Each config As Configuration In
configs
ListProperties(config.Properties, window, 2)
Next
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
End Sub
When you run this macro you might notice (what appears to be) duplicate entries
for the same project. You will see why shortly.
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.
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()
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.
|