Inside Visual C++ Wizards
Shankar Vaidyanathan, Gabriel Esparza-Romero, Andras Tantos, Boris Jabes
Microsoft Corporation
March 2006
Applies to:
Visual C++ .NET
Visual C++ .NET 2003
Visual C++ 2005
Summary: This article provides an inside look at the architecture of the Visual C++ wizards. From new projects to adding items, you will learn how to customize existing wizards and create your own. (20 printed pages)
Contents
Introduction
Walkthrough of Wizard Execution
Delving into the Wizard Architecture
Writing a Custom Wizard
Conclusion
Introduction
One of the most overlooked features of Visual Studio is its impressive array of wizards. We tend to start any project by running a wizard, and the code that we write afterwards is partly generated by wizards, as well. In the realm of C++ development, wizards automate a variety of tasks. For example, there are wizards that create projects based on MFC or .NET and help users get started and learn about appropriate compiler switches. Beyond these are wizards that automate some common coding tasks such as inserting a new MFC element.
All of the C++ wizards are built on top of a flexible wizard engine in Visual Studio. In other words, they are customizable and users can access the underlying functionality to create their own wizards. Indeed, exposing the internals and the UI of wizards is one of the fundamental design goals of the wizard engine. As such, the bulk of the intelligence resides within the wizards while our engine provides basic framework functionality.
Users can add new wizards to automate repetitive work or as part of a third party add-in to Visual Studio. These customized wizards can perform tasks as simple as filling in some fields, like a company name, or as complex as providing default implementations for custom interfaces.
Table 1. Wizard types
Type | Purpose | Location | Extensible |
---|---|---|---|
New Project | Create a new project | New Project dialog | Yes |
Add New Item | Add files to a project | Add New Item dialog | Yes |
Class | Add classes to a project | Add Class dialog | Yes |
Code | Add code to a project | Right-click on nodes in Class View | No |
In this article we describe the VSWizard engine, which is designed to handle C++ wizards included in Visual Studio. Users can take advantage of this engine to write their own C++ wizards and thus gain productivity at low cost. The goal of this article is to explain how wizards work and provide a starting point to write new custom wizards.
Walkthrough of Wizard Execution
Populating the List of Project Templates Dialog
When a user opens the New Project dialog, the Visual Studio shell looks at all the installed project systems at the current machine under the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects.
The shell then enumerates through all the project types and queries for the ProjectTemplatesDir key to identify the location of all the New Project wizards for each project type. These directories are called template directories. The Visual C++ project templates directory in Visual Studio 2005 is obtained from the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\ProjectTemplatesDir.
The shell then reads all the wizard and directory description files (*.vsz and *.vsdir respectively) found in these directories. Each wizard is represented by a single wizard description file, which contains the basic information about the wizard. Each directory containing wizards must also have at least one directory description file, which describes how to present the wizards to the user. Using all this information, the shell displays a tree of all the available New Project wizards (Figure 1). The structure of the .vsz and .vsdir files is described later in this article.
Figure 1. New Project Dialog
A similar dialog is used for the Add New Item and Add Class wizards. The template directories for these wizards are displayed by querying a different set of registry keys. Table 2 lists these keys and the Visual Studio 2005 template directories that they point to. In other words, these are the directories where .vsz and .vsdir files are stored.
Table 2. Registry keys specifying location of wizards
Type | Registry Key | Default Location |
---|---|---|
New Project | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\ProjectTemplatesDir | <VS Installation Directory>\VC\VCProjects |
Add New Item | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\AddItemTemplates\TemplateDirs\ {F1C25864-3097-11D2-A5C5-00C04F7968B4}\/1\TemplatesDir | <VS Installation Directory>\VC\VCProjectItems |
Class | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\{8BC9CEBA-8B4A-11D0-8D11-00A0C91BC942}\AddItemTemplates\TemplateDirs\{F1C25864-3097-11D2-A5C5-00C04F7968B4}\/3\TemplatesDir | <VS Installation Directory>\VC\VCAddClass |
Code | N/A | <VS Installation Directory>\VC\VCContextItems |
The Visual Studio 2005 Installation Directory is usually:
<Program Files>\Microsoft Visual Studio 8
The New Project, Add New Item, and Add Class dialogs display an icon for each .vsz file that is found at the template directories. The icon is found in one of these two locations:
- From the same directory as the .vsz file. This requires that the icon file be named the same as the .vsz file.
- From the location specified in the .vsdir file.
Launching a Wizard and Initializing the Context
When the user selects a wizard and clicks on the OK button in the relevant dialog, the Visual Studio shell uses the ProgID described in the second line of the corresponding wizard description file to create the wizard engine, which is a COM object. For C++ projects in Visual Studio 2005 the engine is called VSWizard and the ProgID is VsWizard.VsWizardEngine.8.0. This article deals only with this particular wizard engine. Note that this ProgID changes with each version of Visual Studio.
The Wizard Engine
The VSWizard engine provides the framework and utility functions that wizards can use. The wizard's user interface is primarily HTML. The back-end is JScript with full access to the rich object-model of Visual Studio. The VSWizard engine essentially coordinates the UI and runs the back-end script when the user clicks the Finish button on the wizard dialog. It also augments the object model with utility functions to provide functionality that would otherwise be complicated or inaccessible. For instance, the wizard engine exposes an API to bring up the OleDB dialog, which otherwise would not be available from script. Please see VCWizCtl Object Properties, Methods, and Events in the MSDN library for the complete list of all the control's API.
The Symbol Table
The .vsz file can also contain parameters that will get passed to the wizard engine. These parameters can be used to customize the behavior of a wizard; thus the name custom parameters. Refer to the description of the .vsz file structure later in this article for more information.
When the VSWizard engine is instantiated, it creates a name/value pair collection called the symbol table to store these parameters and to keep track of all the user's choices in the UI. Values in this symbol table can be read and modified by the wizards, except for the 'context parameters' described later in this article.
In addition to the custom parameters specified in the .vsz file, the Visual Studio shell passes a few other parameters (listed in Table 6) called context parameters that give details on the current project and the context in which the wizard is being run. They can only be set by the shell, but can be used anywhere in the wizard.
Locating the Wizard Files
Once the engine is initialized, it locates the wizard UI and script files as follows:
It queries the shell to get the registry key for the product.
It opens the setup subkey under this registry path.
It retrieves the value of the PROJECT_TYPE symbol, which is a custom parameter in the .vsz file. For C++, this value is VCPROJ and the wizard engine maps it to the registry key name 'VC.' This subkey is opened under the setup subkey. There is no such automatic mapping for third party languages, and so the PROJECT_TYPE value is directly looked up under the setup subkey.
The engine then queries for the ProductDir string value under this key. This value is saved as the PRODUCT_INSTALLATION_DIR symbol.
The wizard engine looks for the existence of either the RELATIVE_PATH or ABSOLUTE_PATH symbols in the .vsz file. If the RELATIVE_PATH symbol is found, then its value is appended to the product installation directory previously described. The wizard directory is located here. If the ABSOLUTE_PATH symbol is present, then the product installation directory is ignored and the value of this symbol is used instead. If neither symbol is present, the default wizard location directory (VCWizards) name is added to the product installation directory path.
Once the wizard engine has found the wizards directory, it determines the subdirectory in which the files for the wizard in question are located. This is found by appending the value of the WIZARD_NAME parameter to the wizard's directory path. For instance, for the ATL OLEDB consumer class wizard, the ATLConsumer.vsz file contains the following line:
Param="WIZARD_NAME = Consumer"
So "Consumer" is the name of the directory in which the UI and script files are found. After the wizard directory has been determined, it is saved in the START_PATH symbol for the wizard to use.
Layout of the Wizard Files
Inside the START_PATH directory there are usually four directories: Html, Images, Scripts, and Templates. The Html directory is where the HTML files that specify the UI are stored. The Images directory is where the image files used in the UI are stored. The Scripts directory houses the script files used to control the wizard's behavior, and the Templates directory contains the template files that will be used by the wizard to produce its output.
Each wizard can have localized versions of files so that when the wizard is used in different locales (Japanese or German, for example), it will process the corresponding files. Thus, the actual files reside in subdirectories corresponding to each language codepage. Note that the Images directory does not have locale subdirectories, as similar graphics and icons tend to be used across all locales.
In addition, the wizard control uses the contents of <VS Installation Directory>\VC\VCWizards\<locale> for the display and the execution of the wizard. All the common script functions are located in a file called common.js in this directory, as are all the HTML style sheets used to render the UI.
The locale is determined by the product language at installation time, but can be overridden by the /LCID command line option to devenv.exe.
Executing the Wizard
Once the initialization is complete and the symbol table is filled in, the engine needs to determine if the wizard has a UI. This is done by checking the WIZARD_UI parameter in the .vsz file. If it is set to TRUE or not specified at all, the engine opens a dialog and loads the main wizard HTML page (default.htm) from the corresponding wizard directory (<START_PATH >\HTML\<locale>). The page uses the information in the symbol table to initialize its state (that is, default options currently selected) and adds symbols to the table based on the options chosen by the user. New values can be introduced to the symbol table with the <symbol> tag in the HTML file, or through JScript. Here is a quick example:
<SYMBOL NAME="CUSTOM_SYMBOL_1" TYPE="text" VALUE="My value"></SYMBOL> wizard.AddSymbol("CUSTOM_SYMBOL_2", "Another value");
The different values for the TYPE attribute for the SYMBOL tag are: text, checkbox, radio, and bool.
The HTML page can have links to other HTML pages. Before displaying the next page, however, the current one must save its state in the symbol table. All other state information will be lost. The symbol table acts as a repository of whatever the user has chosen thus far.
When the user clicks on a wizard's Finish button, the current HTML page's OnFinish method will be called. This method then usually calls OnWizFinish (declared in Common.js), which in turn calls the wizard engine's Finish method. This method will locate and load the wizard's default.js file in the <START_PATH>\scripts\<locale> directory and call the OnFinish method in it. In short, this long chain of calls means that when users click on Finish, the OnFinish method in default.js gets executed.
In almost all cases, the core functionality of the wizard is in the OnFinish method inside the default.js file. User selection can be communicated to this script through the symbol table, which can be queried and modified using the FindSymbol and AddSymbol methods exposed on the wizard engine interface.
Common OnFinish Behavior
For New Project wizards, the actions in this OnFinish method are fairly standardized. They first create a project and then read the templates.inf file in the templates directory to determine what templates need to be parsed and added to the project. The files mentioned in templates.inf are optionally parsed, rendered, and added to the project based on the symbols set.
Both the templates.inf file as well as the templates themselves can use values in the symbol table to control the generated project. The details of this mechanism are described in the Template Files section; it's sufficient to note here that parts of the templates can be excluded or included from the generated project depending on symbols being defined, and values of symbols can be embedded in the generated output, as well.
After parsing a template file, the function GetTargetName is called. This function is usually implemented in the wizard's default.js file and is used to assign a final name to the file generated from the template. For example, most application wizards have a template called root.cpp. This file, however, will assume the name of the project and will be saved as <project_name>.cpp in the generated project.
After parsing, rendering, and adding the files to the project, the wizards typically set the corresponding project options, once again through script.
The Add New Item wizards use the same mechanism but only add files to existing projects.
Code wizards, on the other hand, are much more diverse and their actions in their OnFinish method highly depends on the purpose of the wizard, and in many occasion the existing project code.
Delving into the Wizard Architecture
Wizard Description File
The .vsz file extension denotes the wizard description files, which contain the information needed to launch a wizard. Each wizard must have one .vsz file.
- The first line is typically "VSWIZARD 7.0." The version here denotes the version of the file format and not the version of the product using the file.
- The second line specifies the wizard's ProgID. In our case the second line is always the VSWizard ProgID: Wizard=VsWizard.VsWizardEngine.8.0.
- The file can also contain one user defined custom parameters per line, with the format Param="<PARAM_NAME> = <PARAM_VAL>. Each custom parameter name and its corresponding value are added to the symbol table before displaying the wizard window or executing the wizard in case of UI-less wizards. "Custom parameters" is actually a misnomer, as some of the parameters, like WIZARD_NAME, are required or have special meaning. The following parameters serve a special purpose in the wizard engine and thus their names are reserved (Table 3).
Table 3. Built-in wizard symbols
Parameter Name | Description |
---|---|
__<ANY NAME> | Symbol names with double underscores at the beginning are reserved for internal use by Microsoft. |
ABSOLUTE_PATH | Identifies the location of the wizard files. The specified path is absolute so it does not depend on the Visual Studio installation directory or other configuration parameters. Note that use of this parameter can make deployment of wizards more cumbersome. |
FALLBACK_LCID | If the wizard doesn't support the current product locale, this parameter can be used to specify a default locale for the wizard. Default is 1033 (English). |
PREPROCESS_FUNCTION | This parameter specifies the name of a function to be called before opening the wizard; if it returns TRUE the wizard will open. The function must be declared in common.js or in the wizard's default.js file. This functionality can be used to determine whether the wizard should run. For example, the ATL class wizards can only be used with ATL projects. |
PROJECT_TYPE | Specifies the type of project to be used. If this parameter is not specified, the engine will default to C++ projects. |
RELATIVE_PATH | Identifies the location of the wizard files, relative to PRODUCT_INSTALLATION_DIR. |
WIZARD_NAME | The wizard's directory name in the corresponding wizard storage directory. Specifying this custom parameter is mandatory. |
WIZARD_UI | If this parameter is set to FALSE, then the wizard has no UI, and the OnFinish method in default.js gets called immediately. If this parameter is not specified, the default is to have user interface. |
Directory Description File
The .vsdir files are used to describe how wizard information will be presented to the user. They describe the wizards that will appear under the same branch of the New Project or New Item dialog. Each of these dialogs has a corresponding directory in which the .vsdir and .vsz files are stored.
For instance, when the user opens the Visual C++ New Project Dialog, the environment usually reads all the .vsdir files under the VCProjects directory to get information on which wizards are available. Please see table 2 for more information.
Each line of the .vsdir file identifies a wizard and must be terminated by an end of line character, including the last line of the file. Parameters must be in the order shown in Table 4 and must be separated by a pipe character (|). Optional parameters have to be replaced by a space if they are not specified.
Table 4. Parameters in a vsdir entry
Field | Description | Optional |
---|---|---|
Vsz File Name | The wizard's .vsz file path and name. | No |
Package Identifier | The GUID that represents a Visual Studio Package that can contain wizard localized resources. This field is optional. Most custom wizards will have this field blank. | Yes |
Name | The wizard's name. It can be a string or a VS package resource identifier (denoted by a number sign (#) as the first character). | Yes |
Priority | A numeric value for the wizard's priority. Those wizards with the smallest number will be sorted and displayed first. | No |
Description | A string or VS package resource identifier (of the form #ResID) that describes the purpose of the wizard. | No |
Icon Container Identifier | The path and name to an executable or DLL file that contains the icon resource used to display the wizard. It can also be a VS package GUID. Most custom wizards will leave this field blank and install an icon file (.ico) in the same directory as the corresponding .vsz file. | Yes |
Icon Identifier | The resource identifier within the Icon Container Identifier. Most custom wizards will not use this field. | Yes |
Flags | These bitwise flags are used to specify different user interface options when the wizard is selected. The value of this parameter is one or more values described in Table 5, OR-ed together. | No |
Suggested Name | The name that will be used to construct a suggested project name. It can be a string or a resource identifier of the form #ResID. The environment will append a consecutive number in order to make the name unique. | No |
Table 5. VSDIR User Interface Flags
Value | Description |
---|---|
1 | Uses non-local user interface and save mechanisms. |
2 | Used to create an empty solution not a project. |
4 | Disables the browse button. |
8 | Does not append an extension to the generated name. |
32 | Disables the location field. |
4096 | Does not initialize the name field. |
8192 | Disables the name field. |
You can find more information on the VSDir Files page in the MSDN library.
Symbol Table
The symbol table, as described previously, acts as a dictionary of all the information that the wizard will gather and use throughout its execution. Each symbol is made up of a case-sensitive name and a value. When the wizard is created, the symbol table contains the custom parameters specified in the .vsz file and a set of parameters based on the type of project. The wizards may then add more symbols depending on the user actions.
Table 6. Symbols populated automatically by the wizard engine
Parameter Name | Description |
---|---|
CLOSE_SOLUTION | This symbol indicates whether the solution needs to be closed on the invocation of the wizard. For Project wizards its value depends on the options that the user specified in the New Project dialog; it is false for any other wizard types. |
DOCUMENT_FIRST_LOAD | This symbol is set to true when wizard HTML page is loaded for the first time. HTML pages use this symbol to determine when to set its controls' value to its default state. This symbol is only set for wizards that have an HTML user interface. |
HTML_PATH | The directory in which the wizard's HTML pages reside. Its value is formed based on the value of START_PATH. It includes the appropriate LangID subdirectory (the LangID is the product's language identifier, the engine retrieves it from the registry key HKCU\Software\Microsoft\VisualStudio\8.0\General\UILanguage). |
IMAGES_PATH | The location at which the wizard's image files are stored. Its default value is <START_PATH>\Images. |
ITEM_NAME | For Add New Item wizards, it stores the name of the item to be added. It is undefined for other wizard types. |
PARENT_NAME | When a Code wizard is invoked from a Class View code element, such as the Add Member Function wizard, this symbol stores the name of the parent object. For Add New Item wizards, it contains the name of the file to be added. It is undefined for all other wizards. |
PRODUCT_INSTALLATION_DIR | The directory in which the product is installed. The product is determined by the PROJECT_TYPE custom parameter in the .vsz file. The value is obtained from the following registry key, where <Product> is any of the languages that are part of Visual Studio: HKLM\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\<Product>\ProductDir. |
PROJECT_NAME | Stores the name of the current project (or the project to be generated in the case of the Project wizards). |
PROJECT_PATH | For Project wizards, this symbol represents where the project should be created; for all the other wizards, it represents the location at which the current project is located. |
PROJECT_TEMPLATE_PATH | The directory in which the project templates exist. The project files for a particular language exist at this location. For instance, Visual C++ includes three different project templates (default.vcproj, defaultsql.vcproj, DefaultTest.vcproj) that the wizards use as a starting point to generate projects. Its value for Visual C++ is <Program Files>\Microsoft Visual Studio .NET\VC\VCWizards. |
SCRIPT_COMMON_PATH | The directory in which the common wizard files are found (style sheets, images, and Common.js). Its value depends on the value of PRODUCT_INSTALLATION_DIR and includes the language identifier. For instance, its value for the English version of Visual C++ is <Program Files>\Microsoft Visual Studio .NET\VC\VCWizards\1033. |
SCRIPT_PATH | This symbol identifies the directory in which the wizard's script files are located. The default value is <START_PATH>\Scripts\<LangID>, where LangID is the user interface language identifier. |
START_PATH | The directory in which the wizard files reside. The wizard engine forms this directory based on the value of PRODUCT_INSTALLATION_DIR and RELATIVE_PATH or ABSOLUTE_PATH, as described previously. |
START_PATH | This is the directory from which the wizard was started. |
TEMPLATES_PATH | The directory in which the templates are located. Its default value is <START_PATH>\Templates\<LangID>. A wizard can have templates in more than one language like the MFC Application wizard; in this case the wizard must change the default value in order to use the correct set of templates. |
VS_INSTALLATION_DIR | Visual Studio is installed at this location. It is only defined for Project wizards. Its value is usually <VS Installation Directory>\Common7\IDE\. |
WIZARD_TYPE | This symbol identifies the type of wizard that is being invoked. Its value can be:
|
Template Files
Template files provide a flexible way for wizards to generate files. The grammar for the templates is very simple and its directives can be found here. The templates.inf file is the master list for all other template files while also being a template itself. Thus, the content of generated files as well as the list of files to generate can both be customized via the template grammar.
The format of templates.inf is as follows:
- Each line in the file (unless it's a template directive) identifies a template file to be added to the project.
- Each line (including the last one in the file) must be terminated by a new-line character.
- Filenames in the file are relative to the location of the templates.inf file.
- Filenames can be preceded by optional flags. If these flags are present they are separated from each other and from the filename with a pipe ('|') character. The available flags are listed in table 7.
Table 7. Optional file flags
Flag Name | Description |
---|---|
ChildOf(<PARENT>) | The generated file will be placed in the generated project as a child node of the denoted parent file. Note that <PARENT> should reference the parent template file name and not the name of the generated file in the project. |
CopyOnly | The corresponding template file will be copied (and optionally renamed) to the target directory without modification. No symbol replacement and template directive processing will be applied to the file. The codepage of the template file will not be changed. |
OpenFile | The generated file in the project will be opened by its default editor after project creation is completed. |
Writing a Custom Wizard
Visual C++ 2005 provides an extensibility model with which you can create your own wizards. There are two simple ways of creating a custom wizard. One is to take an existing wizard and modify it to your needs; the other is to use the 'Custom Wizard' wizard to generate a wizard skeleton and start from there.
Figure 2. Custom Wizard
After clicking on the Finish button you will get a project that includes the files listed in Table 8.
Table 8. Custom Wizard files
Wizard information files | Files used by the New Project dialog to display information about the wizard. These include the wizard information file (.vsz), which tells Visual Studio how to open it; a directory information file (.vsdir), which contains a description of the wizard; and an icon. |
User interface files | HTML pages, images, and cascading style sheets. |
Default.js script file | This JScript file includes the functions used to generate the project. |
Templates.inf templates list file | Lists all the files that the wizard must add to the generated project. |
Template files | Files that the wizard can use to generate a project. By default, there are two text files and a basic Visual C++ project file (.vcproj). |
Once your wizard skeleton is created (or you have made a copy of an existing wizard to modify) you will likely want to do some of the following tasks:
- Modify the .vsdir and .vsz files to point to your wizard.
- Add or modify the icon associated with your wizard.
- Author the HTML pages that make up your wizard.
- Create the template files that your wizard will add to the project.
- Write the JScript code that performs the actions in your wizard.
- Test and debug your wizard.
- Localize your wizard to different locales.
- Deploy your wizard.
We provide some guidelines and tips for each of these activities in this section.
Modifying .vsdir and .vsz files
The contents of these files were described in detail in the previous sections. Here we include some additional notes.
If you modified an existing wizard, the .vsdir file most likely refers to resources in a binary package that ships with Visual Studio. Make sure that you have created a new line in your own .vsdir file that references your resources. Adding a new line to the existing .vsdir files makes deployment much more difficult.
You must also decide whether to use the RELATIVE_PATH or the ABSOLUTE_PATH parameter in the .vsz file to locate your wizard. Each of these options has its own deployment benefits and problems. Note that the default wizard generates a .vsz file with an ABSOLUTE_PATH directive.
The default wizard puts the .vsz, .vsdir, and icon files into the product install directory in such a manner that your custom wizard shows up in the main Visual C++ tab in the New Project dialog. All other generated files are located in your Custom Wizard project directory.
If you decide to delete a custom wizard, you must remove the .vsz, .vsdir, and Icon files from your product install directory, as well, or your New Project dialog will be polluted with broken references to deleted wizards.
Add or Modify Icon Files
The icon for a custom wizard is usually located by the .vsz file and has the same name as the .vsz file. You can use any icon editor to edit these files, including Visual Studio's built-in resource editor. Note that the New Project dialog has two listing modes, each of which uses a different icon size. Therefore, you should make sure to create two icon resolutions.
Author HTML Pages
Visual Studio has an HTML editor, which supports a design view as well as a source view, and gives you a practical way to edit the visual components of your wizard. Wizards can utilize any number of HTML features and we include a stylesheet, which you can also customize. You can include custom elements in your pages and reference external sites; however, you should keep security in mind. The wizard pages run in a custom zone of Internet Explorer. This zone allows execution of JScript code and access to safe-for-scripting ActiveX controls, but it disallows many other operations. For example, accessing a non-safe-for-scripting ActiveX control will result in a 'gold bar' showing up on your wizard.
Make sure to chose the right set of symbols and that all required information is saved in the symbol table. It is the only permanent data structure, which is passed to the OnFinish function in default.js once the user presses the Finish button.
Creating Template Files
Template files are the easiest way of creating New Project and New Item wizards. Make sure that you use the correct set of symbols in them. Take special care of the flags you set on individual files in templates.inf, as they can have implications on localization and project deployment.
Write JScript Code for Your Wizard
This is where the core functionality of the wizard will reside. As a minimum, you will have to write the OnFinish method in default.js. If your wizard uses template files, you must also implement the GetTargetName method to associate project file names to template file names. However, since the actual project generation is done in Jscript, you can choose to implement your own template file parsing, name generation, and so on.
Scripts in your wizard are executed in two distinct environments: inside Internet Explorer during the UI portion of the wizard, and in a hosted scripting environment after the user clicks the Finish button. In both cases common.js is included before your code gets loaded by the wizard engine so all its functionality is available to you.
The wizard engine is a COM object, exposing some utility functions for script code. The way you access this object in the two environments is slightly different. Inside the HTML pages you use the window.external object, while inside default.js you use the wizard object. So, for example, you can find out a value of a symbol by calling window.external.FindSymbol("PROJECT_NAME") in an HTML page, and wizard.FindSymbol("PROJECT_NAME") in default.js. If you want to write unified code that can be used in both environments, use the following trick:
var oWizard; try { oWizard = wizard; } catch(e) { oWizard = window.external; } oWizard.FindSymbol("PROJECT_NAME")
For a complete list of all the APIs that you can call, you can open the typelib for VSWizard.dll or refer to MSDN documentation on the VCWizCtl Object Properties, Methods, and Events page in the MSDN library.
Error Reporting
One of the important facilities provided by the wizard engine is the capability to report errors to the user. The SetErrorInfo method in the wizard engine is used to set the error message that you want to be displayed upon exiting the wizard. This method takes an Error object as the parameter. Another method in the wizard engine, called ReportError, immediately displays the error message and returns control to the wizard code. ReportError takes a string as the parameter, but it can also be called with no parameter to emit the error message already set by SetErrorInfo.
The DTE Property
The DTE property of the wizard engine object gives you access to all the rich object models inside Visual Studio. You can find detailed documentation about each of these object models on the Visual C++ Reference page in the MSDN library.
Two of the available object models are of particular interest when designing Visual C++ wizards:
The Project Model
The Project Model is part of Visual Studio's object model. It lets you programmatically interact with a project, change its properties (like compiler options), and add or remove project items, such as code or resource files. The main interfaces are listed in table 9.
Table 9. Project Model interfaces
Project | This interface represents a Visual C++ project. It can be used to change its properties—like its name or build configurations—or to perform an action such as saving it. |
ProjectItem | This interface represents individual project child elements. It can be used to change its properties or perform an action on it. Note that ProjectItems can themselves have nested project items. |
The project creation wizards get a reference to the created project when they call the CreateProject method. All other wizards get a reference to the Project instance in question as an argument to the OnFinish function declared in the default.js file.
For a complete explanation of the Project Model refer to the Visual C++ Project Model topics in MSDN.
The Code Model
With the wizard control functions it is easy to create new projects and add new files to existing projects. However, sometimes you need to add code to existing files or modify code that already exists. In order to do this, your wizard must have a way to understand code. In these situations, you can make use of the Code Model. It parses the code and provides an easy way for your wizard to understand and modify existing code. It is an object model for code files, consisting of different objects and collections that provide information on language constructs that exist within a project. The properties and methods on these objects allow you to create new code elements or modify existing ones. The main interfaces are listed in table 10.
Table 10. Code Model interfaces
CodeModel | This object is the root of a project's code tree. Its CodeElements property is a collection that contains all the code elements (such as classes, functions, variables, and so on) that are declared at the project's global namespace level. |
FileCodeModel | This object is very similar to CodeModel but it represents the root of a file's code tree (as opposed to that of the entire project). |
CodeElement | This interface represents one particular code element declared in a project file. Each code element can have nested children; for example, a namespace can contain nested classes or even nested namespaces. Its Children property provides a collection of all the nested children (note that each child can have children of its own that will not show in the Children collection of the common ancestor). |
Language construct interfaces | This is a group of interfaces that derive from CodeElement and provide functionality specific to each code element type. Examples of these interfaces are CodeClass, CodeStruct, CodeVariable, and so on. |
C++-specific interfaces | This is a group of objects, providing access to language-specific aspects of various elements in the source-code. Examples of these interfaces are VCCodeClass, VCCodeFunction, and so on. |
For an introduction to the Code Model refer to the MSDN topic Discovering Code with the Code Model.
For a reference of object, interfaces, methods and properties in this rich object-model, refer to the MSDN topic Visual C++ Extensibility Object Model.
Debugging
To debug the script in the wizard's HTML and script files, you must first enable script debugging. This can be done by the following steps:
- In Internet Explorer, click the Tools menu and choose Internet Options.
- Click the Advanced tab.
- Under the Browsing category, clear the Disable Script Debugging checkbox.
Once you do this, you can attach another Visual Studio process to your current Visual Studio process as a script debugger. You can set breakpoints in your script blocks within your HTML files and default.js and other script files.
In some cases the chain of events can be rather confusing, so knowing when to attach and where to put a breakpoint can be challenging. In those cases putting some pop-up dialogs into your script code can help. You can use the ReportError method of the wizard engine object to add pop-ups to your code:
var oWizard; try { oWizard = wizard; } catch(e) { oWizard = window.external; } oWizard.ReportError("Break Me!")
Once your pop-up appears, you can attach a debugger, set a break-point and dismiss the dialog.
Localizing
The default wizard templates were designed to make localization simple and straightforward. For this reason, all the wizards have templates, scripts, and HTML files in their respective locale directories. It is thus relatively easy to take the English version of the files in the 1033 subdirectory and provide localized versions in the appropriate locale directory. Visual Studio will automatically access the files under the right locale subdirectory if they exist. If they do not exist, the system reads files under the directory associated to the locale identified by the FALLBACK_LCID symbol. For example, you may ship English (1033) and German (1031) versions of your wizard, and mark your FALLBACK_LCID to be 1033. When a user in a locale other than English and German starts up the wizard, the engine will detect that it is missing the localized files for that locale and bring up the wizard in English.
Deployment
Deploying your wizards to customer machines is usually no more complicated than copying the right set of files to the right locations. You will need your .vsdir and .vsz files, along with the icon for the wizard, put in the right product folder. You will also have to copy all the HTML, JScript, template, and other files that make up your wizard to the target machine. You can figure out the right location for the .vsdir, .vsz, and icon files by looking at the registry key values mentioned in table 2. If you use the RELATIVE_PATH parameter to locate your wizard files, then these registry values can help you determine the target location for the wizard files. On the other hand, the ABSOLUTE_PATH parameter works consistently on all machines. However, if you cannot put the wizard files at the exact same location on all machines, you may have to re-write your .vsz file during deployment.
Conclusion
At first glance, the Visual Studio wizard architecture may seem too complex. Indeed, wizards are composed of JScript, HTML, templates, images, and even some custom files. This apparent complexity is an artifact of our desire to make the wizard engine as flexible as possible. On the one hand, the wizards we ship can be fully customized simply by modifying their files according to the guidelines in this document. On the other hand, creating new wizards is quite straightforward using the custom wizard generator. Furthermore, shipping international products that include custom wizards is much easier thanks to our support for localization. Thus, with this document as a guide, working with wizards in Visual Studio should be highly rewarding.
About the authors
Shankar Vaidyanathan is the development lead for the Visual C++ IDE. Amongst other things, he was instrumental in shipping .Net designers and IntelliSense & Design-Time browser (reference information) over the last several product releases. He has filed for a dozen patents; half of them have already been awarded and the remaining are under review. He has trained his vocal chords for classical music, but he has no hopes of quitting his day job for that.
Gabriel Esparza-Romero is originally from sunny Oaxaca Mexico. After attending college at ITESM he joined Microsoft. In the last 7 years he has held different positions in the C++ and C# QA teams. He is currently working on the next version of the C# editor.
Andras Tantos is a software developer in the Visual C++ IDE team. He has spent 4 years at Microsoft and is currently responsible for the improvements in the wizards infrastructure and the CodeModel. He holds an MSEE degree from the Technical University of Budapest.
Boris Jabes is a program manager on the Visual C++ IDE team. He is responsible for features spanning areas such as Intellisense, Browsing and the editor. He holds degrees from the University of Waterloo and Carnegie Mellon University.