The SettingsMaster Add-In


After all the fun I had on SuperSaver, I really wasn't looking forward to the next add-in I had to write for this chapter. In the end, SettingsMaster was not only problem-free but one of the most useful tools I've ever written. I certainly hope you find it useful as well.

As the name implies, SettingsMaster's purpose in life is to get all your settings straight. By settings, I mean all your build settings. Many of the bugs I've worked on over the years have come down to build problems, so I wanted some way, once and for all, to ensure the proper settings were actually in a project. Additionally, Visual Studio .NET is pretty poor when it comes to team development; the only way to set the build settings for multiple projects worked on by multiple developers is manually. This is a huge and very troubling hole in Visual Studio .NET. I wanted to solve these two build settings problems for both .NET and native C++ projects.

SettingsMaster adds two commands to the IDE. The first, SettingsMaster. CorrectCurrentSolution, uses the default configuration file (more on this file later) to automatically apply the settings you want. The second command, SettingsMaster. CustomProjectUpdate, prompts you with the Open File dialog box to select a configuration file for updating the project currently selected in Solution Explorer.

The idea is that you'll put all your common team settings in the common files, and whenever anyone creates a new project, he or she can click the SettingsMaster.CorrectCurrentSolution button and immediately start the project with the correct team settings. The SettingsMaster. CustomProjectUpdate command is for custom updating a project as you're prompted for the input file that contains the changes you want to apply. For example, if you decide to have a new define value in your C# projects, you can easily add that define value to them all.

The SettingsMaster property page in the Options dialog box, shown earlier (in Figure 9-5), allows you to set the default language files for each supported language. The initial version I provide supports C#, Visual Basic .NET, and native C++. It's an excellent exercise for the read to add J# to the mix. You can also choose to have the SettingsMaster commands automatically save the projects it updates after it fixes the build settings.

The configuration files that contain the settings are relatively simple XML files for ease of parsing and to be buzzword-compliant. Because the nature of the project systems for the .NET languages and native C++ is so different, each has different schemas. The basic idea is that the configuration files are language-specific and define the individual settings for each project configuration for that language.

For .NET projects, the basic schema is as follows. Table 9-2 lists the individual fields and what they mean.

<Configurations>     <ProgLanguage></ProgLanguage>     <Configuration>         <ConfigName></ConfigName>         <Properties>             <Property>                 <PropertyName></PropertyName>                 <PropertyType></PropertyType>                 <PropertyValue></PropertyValue>             </Property>         </Properties>     </Configuration> </Configurations> 
Table 9-2: .NET Project Configuration Schema

Node

Description

<Configurations>

The main element that contains one or more configurations.

<ProgLanguage>

Contains the string that describes the GUID string for the programming language supported by this file.

Example:

<ProgLanguage>    {B5E9BD34-6D3E-4B5D-925E-8A43B79820B4} </ProgLanguage>

<Configuration>

The collection of properties for a single build configuration.

<ConfigName>

The name of the configuration. Corresponds to a target configuration in the Visual Studio .NET IDE configuration manager.

Example:

<ConfigName>Debug</ConfigName>

<Properties>

The collection of properties for this configuration.

<Property>

The description of an individual property.

<PropertyName>

The name of a Project object property. This property must exist in the specific language's Project automation object.

Example:

<PropertyName>CheckForOverflowUnderflow</PropertyName>

<PropertyType>

Indicates the type for the property name. This can be only Boolean, String, or Enum. If the type is String, you must include an attribute type OpType, either Overwrite or Append, which determines how the string value will be changed. If the type is Enum, you must include an attribute type Name, which is the name of the enumerated type utilized by the specific Project property.

Example:

<PropertyType>Boolean</PropertyType>

Example:

<PropertyType Name="prjWarningLevel">    Enum</PropertyType>

<PropertyValue>

The value you want the property to have. For Boolean types, this is either 1 or 0. For String types, it is the string you want either appended or overwritten. For Enum types, it is the numeric value of the enumeration.

Example:

<PropertyValue>1</PropertyValue>

Probably the easiest way to illustrate what a .NET configuration looks like is to show two stripped-down examples. Listing 9-5 shows the minimal configuration file necessary to turn on incremental building in a debug build and turn it off for a release build of a Visual Basic .NET project. Listing 9-6 shows how to set the warning level to prjWarningLevel4 in a C# release build project only.

Listing 9-5: Visual Basic .NET SettingsMaster project for turning on incremental linking in a debug build and off in a release build

start example
<Configurations>     <ProgLanguage>{B5E9BD33-6D3E-4B5D-925E-8A43B79820B4}</ProgLanguage>     <Configuration>         <ConfigName>Debug</ConfigName>         <Properties>             <Property>                 <!--Turn on (/incremental+)-->                 <PropertyName>IncrementalBuild</PropertyName>                 <PropertyType>Boolean</PropertyType>                 <PropertyValue>1</PropertyValue>             </Property>         </Properties>     </Configuration>     <Configuration>         <ConfigName>Release</ConfigName>         <Properties>              <Property>                 <!--Turn off (/incremental-)-->                 <PropertyName>IncrementalBuild</PropertyName>                 <PropertyType>Boolean</PropertyType>                 <PropertyValue>0</PropertyValue>             </Property>         </Properties>     </Configuration> </Configurations> 
end example

Listing 9-6: C# SettingsMaster project for turning on warning level 4 in a release build

start example
<Configurations>     <ProgLanguage>{B5E9BD34-6D3E-4B5D-925E-8A43B79820B4}</ProgLanguage>     <Configuration>         <ConfigName>Release</ConfigName>             <Properties>                 <Property>                     <!--Turn on to level 4-->                     <PropertyName>WarningLevel</PropertyName>                     <PropertyType Name="prjWarningLevel">Enum</PropertyType>                     <PropertyValue>4</PropertyValue>                 </Property>         </Properties>     </Configuration> </Configurations>
end example

For native C++ applications, the configuration file schema is similar but has to take into account that native applications specify the tool you want to work on. The following code shows the basic schema, and Table 9-3 lists the individual schema fields and what they do.

<Configurations>     <ProgLanguage></ProgLanguage>     <Configuration>         <ConfigName></ConfigName>         <Tools>             <Tool>                 <ToolName></ToolName>                 <Properties>                     <Property>                         <PropertyName></PropertyName>                         <PropertyType></PropertyType>                         <PropertyValue></PropertyValue>                     </Property>                 </Properties>             </Tool>         </Tools>     </Configuration> </Configurations> 
Table 9-3: Native C++ Project Configuration Schema

Node

Description

<Configurations>

The main element that contains one or more configurations.

<ProgLanguage>

Contains the string that describes the GUID string for native C++. This will always be the value in the following example.

Example:

<ProgLanguage>    {B5E9BD32-6D3E-4B5D-925E-8A43B79820B4} </ProgLanguage>

<Configuration>

The collection of properties for a single build configuration.

<ConfigName>

The name of the configuration. Corresponds to a target configuration in the Visual Studio .NET IDE configuration manager.

Example:

<ConfigName>Debug</ConfigName>

<Tools>

The collection of tools for this configuration.

<Tool>

The properties for an individual tool.

<ToolName>

The name of the particular tool. This can be any of the specified tool objects supported by the VCProject object: VCAlinkTool, VCAuxiliaryManagedWrapperGeneratorTool, VCCLCompilerTool, VCCustomBuildTool, VCLibrarianTool, VCLinkerTool, VCManagedResourceCompilerTool, VCManagedWrapperGeneratorTool, VCMidlTool, VCNMakeTool, VCPostBuildEventTool, VCPreBuildEventTool, VCPreLinkEventTool, VCPrimaryInteropTool, VCResourceCompilerTool, or VCXMLDataGeneratorTool. You can also specify the special VCProject object VCConfiguration to access the general options for projects.

Example:

<ToolName>VCCLCompilerTool</ToolName>

<Properties>

The collection of properties for this tool configuration.

<Property>

The description of an individual property.

<PropertyName>

The name of a project property. This property must exist in the VCProject automation object. If the property for the tool applies only to a DLL, add a Type attribute and set the value to "DLL", and if the property applies only to EXEs, set the value to "EXE". If the property applies to both EXEs and DLLs, do not include the Type attribute.

Example:

<PropertyName>BasicRuntimeChecks</PropertyName>

Example:

<PropertyName Type="EXE">    OptimizeForWindowsApplication</PropertyName>

<PropertyType>

Indicates the type for the property name. This can be only Boolean, String, or Enum. If the type is String, you must include an attribute type OpType, either Overwrite or Append, which determines how the string value will be changed. If the type is Enum, you must include an attribute type Name, which is the name of the enumerated type utilized by the specific Project property.

Example:

<PropertyType>Boolean</PropertyType>

Example:

<PropertyType Name="basicRuntimeCheckOption">    Enum</PropertyType>

<PropertyValue>

The value you want the property to have. For Boolean types, this is either 1 or 0. For String types, it is the string you want either appended or overwritten. For Enum types, it is the numeric value of the enumeration.

Example:

<PropertyValue>4</PropertyValue>

As with the .NET configuration, I want to show a couple of simple examples of native C++ project configurations. Listing 9-7 shows how to turn on whole program optimization for a release build. Listing 9-8 shows setting the .DEF file for debug and release builds.

Listing 9-7: Native C++ SettingsMaster project for turning on whole program optimizations in release builds

start example
<Configurations>     <ProgLanguage>{B5E9BD32-6D3E-4B5D-925E-8A43B79820B4}</ProgLanguage>     <Configuration>         <ConfigName>Release</ConfigName>         <Tools>             <Tool>                 <ToolName>VCConfiguration</ToolName>                 <Properties>                     <Property>                         <!--Turns on /GL and /LTCG.-->                         <!--(Whole program optimization!)-->                         <PropertyName>WholeProgramOptimization</PropertyName>                         <PropertyType>Boolean</PropertyType>                         <PropertyValue>1</PropertyValue>                     </Property>                 </Properties>             </Tool>         </Tools>     </Configuration> </Configurations> 
end example

Listing 9-8: Native C++ SettingsMaster project to set the .DEF file in debug and release builds

start example
 <Configurations>   <ProgLanguage>{B5E9BD32-6D3E-4B5D-925E-8A43B79820B4}</ProgLanguage>   <Configuration>     <ConfigName>Debug</ConfigName>       <Tools>         <Tool>           <ToolName>VCLinkerTool</ToolName>             <Properties>               <Property>                 <!--Sets the .DEF file for the project.-->                 <PropertyName Type="DLL">ModuleDefinitionFile</PropertyName>                 <PropertyType OpType="Overwrite">String</PropertyType>                 <PropertyValue>.\$(ProjectName).DEF</PropertyValue>               </Property>             </Properties>           </Tool>       </Tools>   </Configuration>   <Configuration>     <ConfigName>Release</ConfigName>       <Tools>         <Tool>           <ToolName>VCLinkerTool</ToolName>             <Properties>               <Property>                 <!--Sets the .DEF file for the project.-->                 <PropertyName Type="DLL">ModuleDefinitionFile</PropertyName>                 <PropertyType OpType="Overwrite">String</PropertyType>                 <PropertyValue>.\$(ProjectName).DEF</PropertyValue>                 </Property>               </Properties>             </Tool>         </Tools>     </Configuration> </Configurations>
end example

If you want to look at complete examples for any language, the common files I've included with the SettingsMaster project set up your projects based on all the recommendations I discussed in Chapter 2. You can use the .NET projects as is, but you might want to change some of the defaults for the native C++ projects. In the C++ projects I turn on Unicode character strings and a few other settings I personally like, but these might cause problems for your projects. I marked each node that you might want to change with comments in the XML files.

SettingsMaster Implementation Highlights

Many of you might be happy just using SettingsMaster, but the implementation is moderately interesting. When I first started thinking about SettingsMaster, I started the work in a macro because it's so much simpler to deal with than a full add-in. I included the original macro with this book's sample files in the SettingsMaster\OriginalMacro directory if you want to look at it. Once I had the macro working, I didn't want to translate all that code into C#, so I went ahead and just implemented the add-in in Visual Basic .NET. Since Visual Basic .NET is simply C# without semicolons, it's trivial to slide between languages.

The hardest part of SettingsMaster was defining the XML schema. Since the files are relatively small, I could use the wonderful XmlDocument class to make navigating through the document trivial. If I were to do it all over again, I would look at the XML schema to see whether I could combine everything into one file. When you look at the code, you'll see that I have slightly more duplicated code in processing the two project types than I wanted.

The real magic to SettingsMaster is the wonderful trick of .NET reflection. The ability to create a class on the fly and call methods or set and get properties is one of the greatest features of .NET. Since I had the configurations and projects, I used reflection to create the individual tools and properties. I put a ton of comments in the code, so you should have no trouble following how everything works.

The biggest hurdle I faced writing SettingsMaster was creating an enumerated type value. Since .NET is strongly typed, if I couldn't create a specific Enum value, it was going to be hard to set the numerous settings both the Project and VCProject object exposed. After numerous false starts, I finally had to ask for help. Francesco Balena came through and reminded me that the System.Enum.Parse method does the job just fine. Once I had that method, everything else was simple grunt work through the XML files.

SettingsMaster Future Enhancements

SettingsMaster is very useful as is, but if you're looking for a project, there are plenty of enhancements you could add to SettingsMaster to make it even better:

  • There is no configuration editor for the XML configuration files. Since the property grids are fairly easy to program, you might want to consider writing a configuration file editor so that you don't have to edit the configuration files by hand. This configuration editor should be accessible from the SettingsMaster command bar as well as the Options dialog box property page.

  • One feature that would be relatively easy to add would be an event handler that watches when projects load and automatically updates the project settings.

  • The VCPlatform object is where you can set some of the global options that pertain to native C++ projects. A neat feature to add would be support for this object so that users could set include directories and other properties to help with team development.

  • A nice feature would be to add a command to write out the current project settings to a SettingsMaster configuration file so that you could apply those settings to other projects.

  • To provide additional user feedback, you could also write out changes made by SettingsMaster to the Output window.




Debugging Applications for Microsoft. NET and Microsoft Windows
Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
ISBN: 0735615365
EAN: 2147483647
Year: 2003
Pages: 177
Authors: John Robbins

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net