An Alternative Method for Creating a .NET C++ Custom AppWizard

Introduction

Visual Studio .NET exposes an entirely new IDE code model. If you’ve tried to create a custom AppWizard, you know exactly what I mean. What was once a relatively easy task is now burdened by JavaScript and dynamic HTML. If you’re comfortable with JavaScript and HTML, this article may not be for you. If, however, you want to learn an alternative method for creating a custom C++ AppWizard, read on.

Background

In migrating from Visual Studio 6 to .NET, I discovered a subtle change in the IDE framework. My custom C++ AppWizard would no longer work because Microsoft redesigned the internals of the IDE. The ‘old’ wizard framework created a .awx file that allowed interaction with a dialog/property page resource. The new framework interfaces with dynamic HTML and JavaScript files. The prospect of rewriting my AppWizard using dynamic HTML and JavaScript did not thrill me. Although I’m comfortable with HTML and JavaScript, I did not want to build an AppWizard that uses HTML as its interface. More importantly, I wanted to address two important issues:

  1. The interface for the AppWizard needed to be dialog based and edited by the resource editor. Most programmers are comfortable with this.
  2. I did not want to use HTML because some programmers are not comfortable with this. After all, we speak C++.

Preliminary MSDN and Web research showed that I would need to rewrite my existing C++ code so that it would interface with the new .NET wizard framework. After reading the MSDN article, Creating a Wizard, I found that there are basically two methods to create a .NET custom C++ AppWizard. The first is to use the Custom Wizard project template that’s provided by the .NET framework. This template will create a wizard that uses JavaScript and dynamic HTML. The second method involves implementing the IDTWizard interface. The IDTWizard interface provides one method, Execute(). Execute() is called by the IDE to implement a wizard when the user clicks on a project template in the New Project dialog box. This article addresses the second method. The following will detail the required steps to build a .NET custom C++ AppWizard:

Implementation

Create the necessary files

  1. Using Visual Studio .NET, create a new ATL project. This provides a housing for the wizard code. When the ATL Project Wizard dialog displays, go to the Application Settings page and remove the “Attributed” check box and add MFC support.
  2. From Class View, add a simple ATL object to the project. This class object will be used to implement the IDTWizard interface.
  3. Right-click on the class you just added and select Add->Implement Interface… Use the “Available type libraries” list box to navigate to Microsoft Development Environment 7.0. Scroll the “Interfaces” list box until you find IDTWizard. CLick the ‘>’ add interfaces button and click Finish.

These steps will provide the necessary skeleton code to create the wizard. The next challenge is to get the IDE to “talk” to the custom wizard within the new wizard framework.

Using Wizard Objects

The Wizard code model provides many Automation objects to create and manipulate source code. These objects are invaluable because without them, you would need to write extra wizard code to process the template code, interrogate project symbols, build a project solution, and so forth. By far, the most important object is the VCWizCtl object. It provides important methods you’ll need to write your own wizard. Unfortunately, after numerous forum searches and conversations with Microsoft Developer Support, I learned that the IDE does not provide run time access to this object within C++. Although Microsoft provides the IVCWizCtlUI interface, the object is dynamically created by the IDE and has a limited lifetime. To get around this, we’ll need to “hook” the Wizard code model.

Hooking the Wizard code model

The Wizard code model requires you to create a minimum of three files. They are a jscript file (default.js), an icon file (mywizard.ico), and a vsz file (mywizard.vsz). The icon file is displayed in the New Project dialog and is what the user will see when selecting the wizard template. If one is not provided, a default one is used. The .vsz file is a project control file that is the starting point for all wizards. It’s a text file that tells the IDE what wizard to call and the information to pass to the wizard. The default.js file is called by the IDE when you click Ok in the New Project dialog box. To get access to the VCWizCtl object, the default.js file should resemble the following:

// JScript source code
// Called when Ok is clicked in the New Project dialog box.
function OnFinish(selProj, selObj)
{
   try
   {
      var strProjectPath = wizard.FindSymbol("PROJECT_PATH");
      var strProjectName = wizard.FindSymbol("PROJECT_NAME");

      var params = new ActiveXObject("Scripting.Dictionary");
          params.Add(0, "{87296BBA-EF70-4C16-9ED1-94A4F060D413}");
          params.Add(1, strProjectPath);
          params.Add(2, strProjectName);
          params.Add(3, wizard);
          dte.LaunchWizard("C:\\MyWizard\\CustomWizard\\
                                CustomWizard.vsz", params.Items());
   }
   catch(e)
   {
   }
}

The default.js file creates an array of four parameters. The fourth parameter is a reference to the VCWizCtl object. The script then launches another wizard (our custom wizard) via the CustomWizard.vsz file. The contents of the CustomWizard.vsz should contain the following:

VSWizard 7.0
Wizard={your-wizard-GUID-goes-here}

The “Wizard=” line above causes the Execute() method of your custom wizard to be called and passes the four parameters from the default.js file. The VCWizCtl object is passed via the fourth parameter and can be accessed with the following code within the custom wizard:

CComVariant var[4];

for (long i=0; i<4; ++i)
   HRESULT hr = SafeArrayGetElement(*ContextParams, &i, &var[i]);

m_wizard.AttachDispatch(var[3].pdispVal);

Once you have access to the VCWizCtl object, you can gain access to the other code model objects to create a solution, render template files, and so on.

Conclusion

With a little bit of indirection, you can write your own custom C++ (or C#) wizard. I’ve included a zip file with a sample wizard for further study. It demonstrates how everything fits together. Pay special attention to the code comments and the ReadMe file.

Special Thanks

Special thanks goes to Mike Wong, Microsoft Developer Support. Without Mike’s guidance, I would still be researching a solution.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read