How to read/write the new Visual C++ project properties

Andrew Arnott

Visual C++ 2010 introduces several new project and tool properties, and deprecates some old ones.  If you’re writing a project template or Visual Studio extension that needs to programmatically read and write the new properties, you will find that these properties are not available on the standard VCConfiguration, VCCLCompilerTool, etc. set of interfaces.  These are COM interfaces and cannot be changed.  Rather than create new interfaces for the new properties, we’ve introduced a single new interface that will provide access to all project and tool properties: IVCRulePropertyStorage.

Before we dive into IVCRulePropertyStorage, you should take a few minutes to learn about Visual C++ “Rule” XAML files, as Pavan describes in his post: Platform Extensibility – Part 1.

One new property in Visual C++ 2010 is TargetExt, which specifies the filename extension of the primary output of a Visual C++ project.  This property isn’t available on VCConfiguration.  Let’s read and write it using IVCRulePropertyStorage assuming you’ve got a DTE object for the project.  While we’re in there, we’ll read and write a few other properties at the project and file level.  I’ve written this code as it might be seen in the IDE Macro Editor.  You can try copying and pasting this code into the Macro Editor yourself and running it while you have a new C++ console application open in the IDE.

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    Public Sub RulePropertyAccess()
        Dim vsPrj As VSProject = CType(DTE.Solution.Projects.Item(1).Object, VSProject)
        Dim vcproj As VCProject = vsPrj.Project.Object
        Dim vcconfigs As IVCCollection = vcproj.Configurations
        Dim vcfiles As IVCCollection = vcproj.Files
        For Each vcconfig As VCConfiguration In vcconfigs
            ' Start by accessing a configuration-level property: TargetExt
            ' The "ConfigurationGeneral" string comes from the 
            ' %PROGRAMFILES%\msbuild\Microsoft.Cpp\v4.0\1033\general.xml
            ' file's <Rule Name="ConfigurationGeneral"> tag.
            Dim generalRule As IVCRulePropertyStorage = vcconfig.Rules.Item("ConfigurationGeneral")
            Dim oldExtension As String = generalRule.GetEvaluatedPropertyValue("TargetExt")
            generalRule.SetPropertyValue("TargetExt", ".dll")
            Debug.Print("TargetExt before: {0} and after {1}", oldExtension, generalRule.GetEvaluatedPropertyValue("TargetExt"))
            ' Now let's access a tool-specific property at the project level: WarnAsError
            ' This property isn't new, so it's available both through VCLinkerTool and IVCRulePropertyStorage
            ' Note however that its MSBuild property name does not match its name on the interface.
            Dim vctools As IVCCollection = vcconfig.Tools
            Dim cl As VCCLCompilerTool = vctools.Item("VCCLCompilerTool")
            Debug.Print("VCCLCompilerTool::WarnAsError reports {0}", cl.WarnAsError)
            Dim clRule As IVCRulePropertyStorage = cl ' we only need to QI for this interface on the same object
            clRule.SetPropertyValue("TreatWarningAsError", Not cl.WarnAsError) ' invert the value
            Debug.Print("VCCLCompilerTool::WarnAsError now reports {0}", cl.WarnAsError)
            ' Now let's set a property for a very specific .cpp file.
            Dim stdafxFile As VCFile = vcfiles.Item("stdafx.cpp")
            Dim stdafxFileConfigs As IVCCollection = stdafxFile.FileConfigurations
            Dim stdafxFileConfig As VCFileConfiguration = stdafxFileConfigs.Item(vcconfig.Name)
            Dim fileCL As VCCLCompilerTool = stdafxFileConfig.Tool
            Debug.Print("StdAfx.cpp VCCLCompilerTool::InlineFunctionExpansion reports {0}", fileCL.InlineFunctionExpansion)
            Dim fileCLRule As IVCRulePropertyStorage = fileCL
            fileCLRule.SetPropertyValue("InlineFunctionExpansion", "OnlyExplicitInline")
            Debug.Print("StdAfx.cpp VCCLCompilerTool::InlineFunctionExpansion reports {0}", fileCL.InlineFunctionExpansion)
    End Sub

So you see that IVCRulePropertyStorage provides very simple property read/write access.  And exactly where those properties are read from or written to depends on where you acquired the IVCRulePropertyStorage object.  As shown above, you can get this object from the VCConfiguration, a VC*Tool at the VCConfiguration level, or a VC*Tool at the VCFileConfiguration level.  Not shown above is that you can also get this object from the VCPropertySheet::get_Rules collection, similarly to how we show above using VCConfiguration::get_Rules.  As you might expect, IVCRulePropertyStorage objects obtained from VCPropertySheet objects will read and write to that property sheet instead of the project.

The IVCRulePropertyStorage interface can only be used to read and write properties for which a Rule XAML file exists that is imported into the project.  If your software requires custom properties that do not exist in a Rule file, you need to create a Rule file that describes those properties and then import that file into your project, as described in Pavan’s Platform Extensibility – Part 1 and Part 2.  Why do we require these steps?  Because it turns out that project properties have many attributes, such as where they should be persisted in the file, in which file (project or .user file), whether they are project properties or metadata on items or item definitions, etc. etc.  The Rule file allows you to capture all that per-property detail so that your code that invokes IVCRulePropertyStorage can be free of these implementation-specific details.

Every single property available in the Visual C++ Property Pages dialog has a Rule file associated with it already, guaranteeing you convenient programmatic access to read or write any property you find there.

A note on debugger properties

There are a couple of interesting points to note with regard to properties that configure the debugger.

Debugger properties are stored in the project user file (*.vcxproj.user) rather than the project itself.  This doesn’t require any special coding on your part—you just acquire the IVCRulePropertyStorage object from VCConfiguration the same as you would for other properties.  The Rule.DataSource tag in the debugger_*.xml rule files tell the IVCRulePropertyStorage to persist to the .user file instead of the project file so that you don’t have to worry about that detail.

In Visual C++ 2008 and earlier, debugger properties were accessed using VCDebugSettings.  This interface had only a small set of properties, many of which were overloaded to control several different debuggers (local, remote, web browser, etc.) so these properties could not be set for a particular debugger without risk of impacting other debuggers.  In Visual C++ 2010 we’ve isolated each debugger’s properties so they are independent of one another.  The VCDebugSettings interface is still present in Visual C++ 2010, but deprecated since it did not have the isolated set of properties per-debugger.  For this reason, we highly recommend you use IVCRulePropertyStorage for getting and setting debugger properties for your Visual C++ 2010 IDE extensions.  As with any other properties, you can discover the set of available properties for each debugger and their names and possible values by inspecting these files: %ProgramFiles%\msbuild\Microsoft.Cpp\v4.0\1033\debugger_*.xml


Discussion is closed.

Feedback usabilla icon