Enforcing Source Code Standards using Visual Studio: enhancing the C++ code wizards.

It has been a long while that I was planning on writing an article about the Visual Studio wizards and more particular, the c++ wizards. It was even before I started this web log. At that time not that much information was available, so I had to dig deep into the wizard code of Microsoft. But this helped me learn a lot to.
Today, there are some interesting articles available about the code wizards. But I still found some information at that time that I haven’t found anywhere else yet. So I would like to share it with you.
And that is why I still wrote my article, all though it is somewhat shorter than planned because I didn’t feel like repeating the available information.

Information on the Internet

Types of wizards

There are basically four types of wizards:

  • Project Wizards: which create projects (of course).
  • Item Wizards: which create project items, which are mostly files like a cpp file, or a header file.
  • Class Wizards: they are used to create new classes in your project.
  • Context Wizards: which create class members like methods, variables, etc…

Context wizards, like for member functions, can not be added. However, you can replace the existing one as I will show in a moment.

Making your wizard available in Visual Studio

How the wizards are structured on your harddrive

Visual Studio uses two types of files to make wizards available in the IDE: files with an extension of vsdir and others with an extension of vsz. I will not explain the exact layout here of these files, you can check the above links to find out more about them, what their layout is, etc..
In short, this is what they are for:
First, there is the vsdir type of files. These are a sort of directory files which contain the following:

  • The sub items that exist and their display properties.
  • The wizards that exist and their display properties.

Next, there are the vsz type of files. They define the wizards that exist in the subitems, and the properties of those wizards used to find that wizard.

Depending on where these files are placed they provide functionality for project, item, class or context wizards. In the above mentioned article Inside Visual C++ Wizards you can find where to place it to have what functionality.

Deploy your own wizards

While experimenting I found not everything to be possible that I thought would be possible.

What I didn’t get to work

REMARK: Following was all tried with the project wizards, I did not have the courage to also try it with class wizards so you might have more luck there.

If you look in the MSDN documentation, it suggests that you use the vsdir files to make wizards available to visual studio. This could let you think that you need vsdir files for a wizard. I found this not to be true. If you simply drop a *.vsz file in the “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\vcprojects” folder then that wizard will be selectable in visual studio.
The documentation also suggests that you should be able to provide a string that will then be used as the itemname in the IDE. If you do this, make sure to put a zero for the package clsid, I forgot this and then the IDE simply shows the name of the folder. If you do not put this zero then it simply uses the foldername. What’s more, if you put a folder under the vcprojects folder on your harddrive, that name is automaticaly used in the “add project” dialog box under the c++ projects without any entry in the vsdir files.
If you look in the visual studio documentation you should be able to put your wizards in their own folders which should not be necesarily under the vcprojects folder. This is true for your wizard itself but it did not work for itemfolders. They must be in the “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\vcprojects” folder or a subfolder of it.

What I did get to work

If you do not need any localization:
To create a new section for wizards, simply define a subfolder in the “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\vcprojects” folder. That folder will show up with it’s name. If you want a prettier name displayed, then set the clsidPackage to zero and enter the required displayname in the appropriate field:

MyWizards|0|My own wizards|60

If you do need localized strings:
Make a resouce only DLL per localization with a string table and add your localized strings there. Remember the id’s of the strings you added.
Make a registry entry corresponding to the following (use your own GUID):

[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\
   VisualStudio\\7.1\\Packages\\
   {6E113686-BCD2-4054-A2E2-99684636F6C6}]
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\
   VisualStudio\\7.1\\Packages\\
   {6E113686-BCD2-4054-A2E2-99684636F6C6}\\SatelliteDll]
"DllName"="ResourceOnlyDll.dll"
"Path"="G:\\Experiment\\ResourceOnlyDll\\Release"


Put your resource DLL in a subfolder of the path you set in the registry according to the locale. This subfolder will typically have a name like 1033 for US English.

Also add entries in the your vsdir file, like following (use the GUID you used in the registry):

pvs|{6E113686-BCD2-4054-A2E2-99684636F6C6}|#1|60

If you want to add wizards to your newly created itemfolder, then add a vsdir file to this folder with entries for the vsz files of your wizard. The location of these vsz files can be everywhere on your harddrive.

Well, this is about it for registering your own wizards.

Changing the existing wizards

I will not completely describe the files that together make-up a wizard because you can find all necessary information in the articles referenced above.
I will tell you the main concepts and how you can use them to personalize the existing wizards.

The view of the wizard

The dialogs that together make the wizard are actually html files. You can open them in your browser and you will get a (allbeit distorted) view of what the wizard will eventually look like.

Using spy++ you can see that the wizard windows that shows the wizard pages is a window of class “Internet Explorer_Server”. And searching the Internet you will find that this is actually the Internet Explorer control. In other words, Visual Studio uses IE to show the pages of your wizard. Which is of course not that surprising.

Communicating values from the user interface to the wizards happens by means of symbols, which are basically just key/value pairs. For this, you define keys by using the tag in the section of your HTML page. To get the value of a control into the symbol, you must add an ID attribute to the html tag of the control with the name (or key) of the symbol. So, this is what you get:

<HTML>
   <HEAD>
   <SYMBOL NAME="SUPPORT_UNITTEST" TYPE="checkbox"></SYMBOL>
   </HEAD>
   <BODY>
      <!-- more attributes removed for clarity -->
      <INPUT TYPE="checkbox" ID="SUPPORT_UNITTEST">
   </BODY>
</HTML>

Using the user entered data in your wizard code

The code for the wizards used by Microsoft is written in JScript. Inside the javascript the variable “wizard” is omnipresent, which means it is always available. This variable is of type VCWizCtl and it allows you, among other things, to get at the symbols defined in your wizard.

// Following call returns the value of the symbol with name SUPPORT_UNITTEST
wizard.FindSymbol("SUPPORT_UNITTEST")


So, this is what happens:
When you clicked the Finish-button of your wizard, the javascript document object is passed from the HTML to the wizard engine. The wizard engine eventually calls the OnFinish function in your wizards default.js file which is given two parameters.
I could not find any documentation on these parameters, nor on what the wizard engine is doing, but some experimentation gave me following values for the parameters:

wizardtype param1 param2
project null null
item project null
class project null
member projet class

Once you are inside the wizards OnFinish function you can use the omnipresent wizard variable to get at the complete object model inside Visual Studio. Together with the parameters handed to your function you can now start adding files to your project or adding code elements.

You can also use the functions in the file common.js available in the folder (of a standard installation) “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCWizards\\”.

Include your own code library

If you look into the HTML files which make up the wizard user interface, you will see following construct at the bottom:

<SCRIPT ID="INCLUDE_SCRIPT" LANGUAGE="JSCRIPT"></SCRIPT>
<SCRIPT ID="INCLUDE_COMMON" LANGUAGE="JSCRIPT"></SCRIPT>
<SCRIPT>
   var strPath = 
      window.external.FindSymbol("PRODUCT_INSTALLATION_DIR");
   strPath += "VCWizards/";
   strPath += window.external.GetHostLocale();
   var strScriptPath = strPath + "/Script.js";
   var strCommonPath = strPath + "/Common.js";
   document.scripts("INCLUDE_SCRIPT").src = strScriptPath;
   document.scripts("INCLUDE_COMMON").src = strCommonPath;
</SCRIPT>


When I noticed this I immediately thought: “Great, so with a similar construct I will be able to include my own libraries for use in the HTML and default.js”. So I extended the construct like this:

<SCRIPT ID="INCLUDE_SCRIPT" LANGUAGE="JSCRIPT"></SCRIPT>
<SCRIPT ID="INCLUDE_COMMON" LANGUAGE="JSCRIPT"></SCRIPT>
<SCRIPT ID="INCLUDE_MYLIB" LANGUAGE="JSCRIPT"></SCRIPT>
<SCRIPT>
   var strPath = 
      window.external.FindSymbol("PRODUCT_INSTALLATION_DIR");
   strPath += "VCWizards/";
   strPath += window.external.GetHostLocale();
   var strScriptPath = strPath + "/Script.js";
   var strCommonPath = strPath + "/Common.js";
   var strMylibPath = strPath + "/Mylib.js";
   document.scripts("INCLUDE_SCRIPT").src = strScriptPath;
   document.scripts("INCLUDE_COMMON").src = strCommonPath;
   document.scripts("INCLUDE_MYLIB").src = strMylibPath;
</SCRIPT>


However this doesn’t seem to work, at least not to include code for use in default.js. It looks like Microsoft somehow has hard coded the inclusion of Common.js to make it visible in default.js, and the above lines have nothing to do with it.

So if you do want to include your own library you should copy it inside the common.js file, which is what I ended up doing.

Using the user entered data to create new files

The MSDN article referenced above does provide some information on how this happens, but digging into the actual implementation, I was able to get a more detailed look at what is really happening under the hood, and how it can further be customized by yourself.
The workhorse function for this functionality is the wizard engines “RenderTemplate” method which transforms a file with certain rendering instructions to another file. The available rendering instructions are documented and resemble preprocessor defines in c++ (see Visual C++ Concepts: Creating and Managing Projects … Template Directives).
They let you make decisions and looping constructs.
The following for example will output the TrueText in the rendered file if the symbol with name SYMBOLNAME exists, else the FalseText is rendered in the output.

[!if SYMBOLNAME]
TrueText
[!else]
FalseText
[!endif]


Now, this is what happens:
If you look in the “Templates” sub folder of a wizard you will see a bunch of files. Most of them will probably look familiar as they are the files which eventually will end up in your project after executing the wizard. But one file is special and is used to dispatch the other files. That file is the Templates.inf file. This file is in the format for the RenderTemplate method and basically sums the files which have to be added to the project and what actions to take with them, dependent on the values of the symbols defined in the wizard.
The wizard calls the function CreateInfFile which gives this inf file to the method RenderTemplate. When this file is processed by the RenderTemplate method, it contains all the files which must be added to the project and what must happen with them. It has a format similar to following

stdafx.cpp
OpenFile | root.cpp
resource.h
root.rc
OpenFile | root.h
CopyOnly | small.ico
CopyOnly | root.ico


Or generalized:

<filename>
<processing instructiongt; | <filename>


This resulting inf file is then passed to the function AddFilesToProject (in file “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCWizards\1033\common.js” which is automatically visible by the wizard engine) This function adds the files in the inf file to the project after getting their target filename and executes the processing instructions. It understands following processing instructions (independent of the above for RenderTemplate):

  • {none}: files without processing instructions will be rendered by RenderTemplate and be added to the project.
  • CopyOnly: the file will be copied only, but the RenderTemplate method will not do anything with it. Such files should not contain any rendering instructions.
  • OpenFile: after the files have been added to the project and rendered with RenderTemplate, these files will be opened.
  • ChildOf(): this file will become a child if the file between braces.

The target filename is constructed by a call to the function GetTargetName which is implemented in the wizard. You will find that, if you look at the template files for, as an example, the c++ projectwizard, that some of them have “root” in their name, and those files eventually end up in the project with root replaced by the projectname. This transformation is done in the GetTargetName function of the wizard.

All the above is done by the function AddFilesToProjectWithInfFile

The functions CreateInfFile, AddFilesToProject and AddFilesToProjectWithInfFile are implemented in the file “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCWizards\1033\common.js” which is automatically visible by the wizard engine.
Some of the funcions in common.js are documented by Microsoft, and some of them aren’t. You can find those who are at Visual C++ Concepts: Creating and Managing Projects … JScript Functions for C++ Wizards

Creating and adding code to projects

Like I said above, once you are in your OnFinish function of your wizard you can get at the CodeModel for your wizard and start adding functionality to your project.

How to … Create a project

The function CreateProject is called which is implemented in the Microsoft provided file common.js
This function creates a C++ project in the current or in a new solution depending on the value of the symbol “CLOSE_SOLUTION”.
The function uses the symbol “TARGET” which is probably the solution you are currently in, that is, the newly created one or
the one in which you started the wizard. (google for )
You provide it with the name of the new project in the folder to put it in.

How to … Create multiple projects

How to … Create a class

Adding a class is completely done using the VCCodeModel. Mind however that when you add multiple constructs you wrap them in a transaction. Otherwise you could get a wizard that partialy executed.

var oCM;
try
{
   oCM	= selProj.CodeModel;
   var L_TransactionName_Text = "Wizard Added ";
   oCM.StartTransaction(L_TransactionName_Text + strClassName);

   // Do your stuff here

   oCM.CommitTransaction();
}
catch(e)
{
   if (oCM)
      oCM.AbortTransaction();
}

How to … Create a classmember

Adding a class member is also entirely done using the VCCodeModel and of course inside a transaction.

My opinion on the Microsoft Codemodel for C++

When I started off to create my own versions of the Microsoft provided wizards I expected to be able to implement the code standard we are using at my work. Of course, I wasn’t so lucky. Following are some problems which made me fail.

  • I didn’t like microsoft dictating to me how to write inline methods. The codedom model has no way of stating that the implementation of a method must be written behind the declaration of the class (or at least i didn’t find a way) and puts the implementation of inline functions in the declaration of the class, something i don’t like. This way, MS is enforcing some coding style upon you. The codemodel function for adding methods (AddFunction) does provide a parameter to specify a code element after which to add the method, but unfortunately it doesn’t accept a VCCodeClass code element.
  • Same remark for comments: I didn’t find a way to tell how comments should be generated (using // not /* */)
  • Same remark for access specifiers: Microsoft adds them to each method added and doesn’t divide your class declaration into regions for public, private or protected members. There is also no way to dictate an order for public, private or protected members.

Conclusion

Do not expect to be able to enforce your sourcecode standards completely with wizards. The writing of code to files can not be configured enough to generate coherent code. However, you can get a long way, and there is some inrteresting stuff that can be done, like headers for files, documenting of classes and classmembers.

The sample code

Download sample wizards
Download sample internationalization

What does it do?

The sample code provided with this article contains three wizards:

  • A project wizard: the project wizard is an adapted Win32 standard wizard. It has following adaptatons:
    • An option is added to create a Unittest project if the type of project choosen is a dll or a static library.
    • In our code standard we have a design guideline for executables, dll’s and static library. This design guideline is implemented in this wizard. The design guidelines provides guidelines for output folders of static libraries, include folders for static libraries, how to implement functionallity in a dll (use interfaces and a factory), etc…
    • Comments are added for files, classes and methods in a format which can be read by the Autoduck comment extractor.
  • A Class wizard. This is again the standard class wizard adapted:
    • Add a unittest for the class
    • Add comments understandable by Autodick
    • Provide header excludes according to our code standard instead of the “#pragma once” directive
  • A Method wizard which is once more the Microsoft wizard adapted. It provides:
    • Add comments for parameters and the return value understandable by Autoduck.
    • Add a unittest for the method.

Deploying the sample code

Do the following:

  1. Unzip the sample code. You now have four subfolders:
    • My: this contains in a subfolder 1033 a file Mylib.js. Add the code in this file to the Microsoft provided Common.js file.
    • MyClassWiz: this contains the class wizard.
    • MyContextWiz: this contains the method wizard.
    • MyProjectWiz: this contains the project wizard.
  2. Copy the four subfolders to a folder with the name “C:\Devenv\MyCppWizards”. If you don’t do this, the wizards will not work by smimply copying the necessary files.
  3. The subfolders MyClassWiz, MyContextWiz and MyProjectWiz contain each a subfolder MSVC. Copy the contents of the subfolder in these folders to the corresponding folders under “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7” (for standard installations). Example: copy “C:\Devenv\MyCppWizards\MyClassWiz\MyGeneric\MSVC\VCAddClass” to “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCAddClass”.
    Attention !!
    You might want to make a backup of the file “C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCContextItems\MemFunctionWiz.vsz” because this file, unlike others, will get overwritten.
  4. You should now have the wizards available in Visual Studio. Enjoy!

Trying the sample code

As stated above, the wizard implements our code standard. This means that you have to do some preparation to use the wizards. If makinig a project you will need the make the sure you have the following directory structure:

You should have a folder of your own width two subfolders: “core” and “unittest”. Make sure to add your project to the “core” subfolder. The wizard will make the subfolder with the name of your project, as is standard Visual Studio behaviour.

The code produced by the wizard is not compilable because it uses some headers and constants which are only available in libraries we developed. The aim of this article was to demonstrate wizards which is what the sample code does.

Advertisements

One thought on “Enforcing Source Code Standards using Visual Studio: enhancing the C++ code wizards.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s