January 17, 2021
Hot Topics:

Controlling Project and File Properties with C++ Macros

  • By Kate Gregory
  • Send Email »
  • More Articles »

Getting to the Project Model

My macro, like the earlier ones, has one line of VB that calls into the C++ DLL:

Sub MakeNative()
End Sub

I added a MakeNative method to the Utilities class I showed in earlier columns, and after each build I copied it to C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\PublicAssemblies to make it available to the macro project system as a reference.

That just leaves the MakeNative function to write. It needs access to the project system, and that means a new namespace to work with: Microsoft::VisualStudio::VCProjectEngine. By getting a ProjectItem, I can get a VCProjectItem and, from there, a VCFile. Unlike the file code model I used in the macro that stubs in interfaces, the Microsoft::VisualStudio::VCProjectEngine::VCFile class represents the file of code from the point of view of the project system: the properties and options as set for this particular file. Here's how I got it:

// get the active window
EnvDTE::ProjectItem *pi = DTE->ActiveWindow->ProjectItem;
VCProjectItem* vcpi = 
   dynamic_cast<VCProjectItem *>(pi->Object);
VCProject* proj = 
   dynamic_cast<VCProject *>(vcpi->project);
IVCCollection* files = 
VCFile* file = 
if (file)
   // do the work of the macro

(Just as with my other macros, you need to be sure that the right kind of file—in this case, a code file that's part of a project—has focus when you run the macro. This piece of code blows up with null pointer problems if you are looking at online help or some other non-project information when you run the macro. For readability, I'm not testing each of these return values but just assuming they all work.)

If you think about changing properties for a file or for a whole project, I hope you think quickly of configurations. While it makes very little sense for your Debug build to be managed and your Release build to be unmanaged, there are of course some property differences between the builds. The project model calls builds "configurations" and gives you access to them once you have the file. Here's what I put inside that if block above:

IVCCollection* fconfigs =
for (int i=1; i<fconfigs->Count+1; i++)
   VCFileConfiguration* fconfig =
   // work with the configuration

If you want to make your changes only to a single configuration, take a look at the Name property of the fconfig object. In this macro, I'm going to make the same change to each configuration I find, so I don't care about their names.

While this is certainly not production-level code, I have included a bit of error checking. If you invoke this macro while you're editing a header file, you won't be able to change compiler options, because header files don't have compiler options. When I'm working in managed code, I often find myself putting more and more into header files, leaving implementation files that are just shells to include the header files. That's not a problem, as long as I remember the other purpose of the implementation file is to carry the compiler options.

This code establishes whether the configuration obtained from the file has compile-link options or not:

VCCLCompilerTool* tool =
if (tool)    // .h's won't have the cl tool associated, nor other
             // files
   // set file level compile options

For once, all that casting is making things simpler. If the tool associated with this configuration isn't the CL compile-link, the dynamic cast will fail, and tool will be a null pointer.

All that remains is to see whether the file is compiling as managed or not, and then adjust properties accordingly:

if (tool->CompileAsManaged == compileAsManagedOptions::managedNotSet)
   // file is already unmanaged in this config, nothing to do
   //file is managed in this config, get to work
   tool->CompileAsManaged = compileAsManagedOptions::managedNotSet;
   // no pre compiled headers
   tool->UsePrecompiledHeader = pchOption::pchNone;

There you go! You can flip the /clr flag on and off simple C++ classes to experiment with the effects of moving the boundary between managed and unmanaged code. Of course, don't assume you can write a managed class (with __gc keywords) and turn off /clr with success. The freedom to compile to native code or IL is for classic C++ with no managed classes. Besides, I'm just using this macro as an example to show you another namespace in the Microsoft::VisualStudio part of the tree. Perhaps it will inspire you to create some macros of your own. As long as you don't mind casting (you'll do a lot of casting), there's nothing you can't do.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software development with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

Page 2 of 2

This article was originally published on June 16, 2004

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date