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.



Downloads

Comments

  • An exeption error

    Posted by barbapapaz on 02/13/2006 07:05pm

    Thanks for you job because the first method (html template) is very poor I don't understand an exeption at the and of execution after the destructor of the CComObject() virtual ~CComObject() throw() { m_dwRef = -(LONG_MAX/2); FinalRelease(); #ifdef _ATL_DEBUG_INTERFACES _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown()); #endif _pAtlModule->Unlock(); } I don't uderstand becaus i use your project and i supposed it be safe thanks

    • problem resolved

      Posted by barbapapaz on 02/15/2006 05:27pm

      Your concils are just. I have also correct the file in vc7\vcprojects with the path of #2
      
      thanks for your help

      Reply
    • Problem(s) found

      Posted by Mike Harnad on 02/14/2006 04:24pm

      I found some problems with your code. Once I corrected them, your wizard appeared. Here's what I found: 1. Your pspwizard.vsz file in the source directory appeared to have the wrong guid. I changed it to: Wizard={92CBBA93-2D7F-4547-895B-BF4EF3F04E07} It should match the guid in the .idl file. 2. The default.js file had the wrong path to launch the wizard. I changed it to: dte.LaunchWizard("C:\\barbapapaz\\MyWizard\\pspwizard\\pspwizard\\pspwizard.vsz", params.Items()); It should point to the .vsz file in #1 above.

      Reply
    • thank for you fast answer

      Posted by barbapapaz on 02/14/2006 03:07pm

      I put my code and your code on my web server, you can download and test. http://www.barbapapaz.com/extras/MyWizard.rar

      Reply
    • Exception error

      Posted by Mike Harnad on 02/14/2006 08:46am

      Can you send me the generated code? I have had no other reported problems with this code. Please make sure you meet the specified requirements.

      Reply
    • test for help you

      Posted by barbapapaz on 02/13/2006 08:23pm

      hi about the exeption i have make a new project with your instruction but i have the same error thanks

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • 10 Rules that Make or Break Enterprise App Development Projects In today's app-driven world, application development is a top priority. Even so, 68% of enterprise application delivery projects fail. Designing and building applications that pay for themselves and adapt to future needs is incredibly difficult. Executing one successful project is lucky, but making it a repeatable process and strategic advantage? That's where the money is. With help from our most experienced project leads and software engineers, …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds