WizWiz - C# Wizard Builder for VS.NET

WizWiz - C# Wizard Builder for VS.NET                    Author:  Willem J Fourie

Source Code for this Article

The following HTML Help file can be downloaded:


Contents

Introduction

The WizWiz Wizard User Interface

The WizWiz Wizard Features

Visual Studio File Installation Requirements for Wizard Add-ins

Visual Studio Folders for Wizard Add-ins

Visual Studio Files for Wizard Add-ins

VS Automation Extensibility Interface Programming

Debugging a C# Wizard

Customizing the Wizard after Creation

Conclusion

Appendix 1: Source Code for WizWiz.cs

 

 

Introduction

WizWiz is a Visual Studio wizard written in C#. Its function is to easily generate 'blank' new wizards for Visual Studio (VS) which provide a Microsoft Setup (MSI) look-and-feel and features multiple page with 'Next' and 'Prior' wizard page functionality.  It was written to illustrate the basic requirements for implementing a wizard for VS, and includes the following aspects:

 

The WizWiz Wizard User Interface

The WizWiz wizard is executed by VS from its 'New Project' dialog as shown in Figure 1 below:

 

 

Figure 1: VS New Project dialog showing the WizWiz icon

 

Selecting the icon and then the OK button fires up the wizard as shown in Figure 2 below:

 

 

Figure 2: The WizWiz wizard displaying its first page

The WizWiz Wizard Features

WizWiz will create a new VS solution with the following features:

 

Visual Studio File Installation Requirements for Wizard Add-ins

There is a lot of information on the correct implementation of wizard add-ins.  The following summarizes the easiest and least disruptive technique which is implemented by WizWiz and WizWizSetup71 .  Assuming that you want to create a wizard with the name 'CSharpWizard1', then VS requires the following files to be installed for a wizard to be recognized and used correctly:

 

Visual Studio Folders for Wizard Add-ins

The following folders are used for the installation of the wizard, again assuming a wizard with the name 'CSharpWizard1', and the installation is for VS.NET 7.1 (Version 2003):

 

C# Device Install Folder

Assuming a standard install of VS in the 'C:\Program Files' folder, the actual folder is:

 

VS.NET Install Folder

Assuming a standard install of VS in the 'C:\Program Files' folder, the actual folder is:

 

Wizard Template Folder

Assuming a standard install of VS in the 'C:\Program Files' folder:

 

Visual Studio Files for Wizard Add-ins

The following files are required by VS in order to recognize and correctly use a wizard add-in:

 

Project Control File (.vsz)

The project control file for WizWiz (WizWiz.vsz) contains the following lines:

VSWIZARD 7.0
Wizard=Bware.Wizards.CSharpWizWiz
Param="WIZARD_NAME = CSharpWizWiz"
Param="WIZARD_UI = FALSE"
Param="PROJECT_TYPE = CSPROJ"
[ProgId("Bware.Wizards.CSharpWizWiz")]
 public class Builder : IDTWizard
{
...
}

Display Control File (.vsdir)

This file allows VS to display the information for the wizard correctly in the 'New Project' dialog.  WizWiz.vsdir contains the following single line:

WizWiz.vsz| |C# Wizard Builder|1|A project for a customizable Visual Studio C# Wizard|WizWizRes|1| |CSharpWizard

 

Wizard Implementation File (.dll)

This file is the component that is loaded by VS to provide the wizard functionality. WizWiz.dll implements the following:

 

Wizard Icon Provider File (.dll)

This file is the component that is loaded by VS to provide the icon for the VS 'New Project' dialog. WizWizRes.dll implements the following:

 

Wizard Template Files

These files are installed in the Wizard Template Folder and are used by the wizard to build project files. The files may be project files themselves (.csproj, .vcproj, .vdproj), code files (.cs, .cpp), resource files (.resx, .rc) or any file which is required by the wizard.  The set of template files used by WizWiz is found in the 'VS Template Files' folder of the WizWiz project.

The files are usually parsed for replaceable tags which the wizard replaces using the correct contextual names. For example, the tag '[!output SAFE_NAMESPACE_NAME]' is replaced with VS passed-in namespace name for a code assembly, while the tag '[!output SAFE_CLASS_NAME]' is replaced with the contextual name for the class being built.

Since WizWiz is responsible for tag replacement using the Builder.RenderTemplate method, custom template tags are also used to make the file generation process a lot easier.

 

VS Automation Extensibility Interface Programming

 

Tip: Try and do as little of this as possible.

The DTE programming environment (namespace: EnvDTE) is very powerful, but difficult to use.  The main reason for this is that the Microsoft documentation is either poor, non-existent or totally out of date. Another reason is that because the DTE objects essentially wrap objects which have to be extensible (by definition) and are very generic, you find yourself wading through sets of DTE Collections, Properties and Configurations objects which are difficult to use if you are unfamiliar with what specific information you are looking for.

 

Tip: Build projects using DTE only if they have to be strongly customized for a particular set of context names.

In the WizWiz solution, only the C# project is built using a VS default template (empty project template with default settings).  For this project references, project files with specific build-actions, customized build-configurations have to be coded.  The other projects were built from templates taken from actual project files, and the replaceable parameters filled in afterwards to make templates.  This reduces the amount of coding required to define the project within the correct context, and this is evident in the WizWiz source code.

 

WizWiz.cs provides code that does the following:

 

Debugging a C# Wizard

 

 

Customizing the Wizard after Creation

It is inevitable that the wizard will be modified after it is created. Instead of re-generating the wizard, a few lines of code can quickly customize some of the wizard actions:

Conclusion

Coding a wizard in C# is the obvious answer for tool and wizard developers that spend most of their time working in C#.  Re-using the code featured in this article provides a means to quickly produce VS wizard extensions to assist developers to build complex applications directly from the VS IDE.

 

 

 

 

Appendix 1: Source Code for WizWiz.cs

 

Listing 1: Source Code for WizWiz.cs
 

using System;
using EnvDTE;
using VSLangProj;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Xml;
using Bware.Wizards.WizWiz;

namespace Bware.Wizards.CSharpWizWiz
{
  
/// <summary>
  /// 
This class implements the required VS automation
  
/// interface for integrated wizards.
  
/// </summary>
  
[ProgId("Bware.Wizards.CSharpWizWiz")]
  
public class Builder : IDTWizard
  {
    
/// <summary>
    /// 
Default Constructor.
    
/// </summary>
    
public Builder()
    {
    }
    
/// <summary>
    /// 
Parameter class used by the file creation functions.
    
/// </summary>
    
public class TemplateParameter
    {
      
/// <summary>
      /// 
Template filepath.
      
/// </summary>
      
public string TemplateFilePath = "";
      
/// <summary>
      /// 
Namespace name.
      
/// </summary>
      
public string NamespaceName = "";
      
/// <summary>
      /// 
Class name.
      
/// </summary>
      
public string ClassName = "";
      
/// <summary>
      /// 
Wizard title.
      
/// </summary>
      
public string WizardTitle = "";
      
/// <summary>
      /// 
Page Title name.
      
/// </summary>
      
public string PageName = "";
      
/// <summary>
      /// 
Page number.
      
/// </summary>
      
public int PageNo = 0;
      
/// <summary>
      /// 
Total number of pages.
      
/// </summary>
      
public int NoOfPages = 0;
      
/// <summary>
      /// 
Full path of destination file created from template.
      
/// </summary>
      
public string DestFullPath = "";
      
/// <summary>
      /// 
String representing the C# project GUId in
      
/// the format {XXXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX}.
      
/// </summary>
      
public string CSProjectGuid = "";
      
/// <summary>
      /// 
String representing the C# project GUId in
      
/// the format {XXXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX}.
      
/// </summary>
      
public string VCProjectGuid = "";
      
/// <summary>
      /// 
Collection of Page Title Names.
      
/// </summary>
      
public System.Windows.Forms.ListBox.ObjectCollection PageNames = null;
      
/// <summary>
      /// 
Returns a string in the format 'Page X/Y' (as in Page X of Y)
      
/// </summary>
      
public string PageOf
      {
        
get 
        

          
return "Page " + PageNo.ToString() + "/" + 
              NoOfPages.ToString();
        }
      }
      
/// <summary>
      /// 
Returns a safe page name which can be used as a filename.
      
/// </summary>
      
public string SafePageName
      {
        
get 
        
{
          
string s = PageName;
          
if( (PageName == null) || (PageName == "") )
          {
            s = "Page" + PageNo.ToString("000") + ".cs";
          }
          
return s;
        }
      }
    }
    
/// <summary>
    /// 
WizWiz C# Project project sub-folder.
    
/// </summary>
    
private const string _projFolder = @"VS Project Files\";
    
/// <summary>
    /// 
WizWiz C# Project template sub-folder.
    
/// </summary>
    
private const string _templateFolder = @"VS Template Files\";
    
/// <summary>
    /// 
Safety area start tag.
    
/// </summary>
    
private const string _safetyStartTag = "<<!\r\n";
    
/// <summary>
    /// 
Safety area end tag.
    
/// </summary>
    
private const string _safetyEndTag = "!>>\r\n";
    
/// <summary>
    /// 
Replaces strings when creating a project file from
    
/// a template file.
    
/// </summary>
    /// <param name="SrcString">
String to perform replacement on.</param>
    /// <param name="OldString">
String to be replaced.</param>
    /// <param name="NewString">
String to used for replacement.</param>
    /// <param name="NoReplace">
Number of replacements to execute.</param>
    /// <returns>
The modified string.</returns>
    /// <remarks>
    /// <para>
    /// 
This method will not perform replacement in the safe area. The
    
/// safe area is a single section in the file that starts with the
    
/// tag '&lt;&lt;!' (ignore the single-quote characters) on its own 
    
/// line, and ends with the tag '!&gt;&gt;' (ignore the single-quote 
    
/// characters) on its own line.
    
/// </para>
    /// <para>
    /// 
If NoReplace = -1, then all identified tags are replaced.
    
/// </para>
    /// </remarks>
    
private string SafeReplace(string SrcString, string OldString,
      
string NewString, int NoReplace)
    {
      
int startIndex = -1;
      
int replaced = 0;
      
// Iterate through all occurrences of 'OldString'.
      
while( (startIndex = this.SafeIndexOf(SrcString, 
          OldString, startIndex + 1)) >= 0)
      {
        
// Check if replacement rule causes a break.
        
if( (NoReplace >= 0) && (replaced >= NoReplace) ) break;
        
// Do the replacement.
        
string leftString = SrcString.Substring(0, startIndex);
        
string rightString = 
          SrcString.Substring(startIndex + OldString.Length);
        SrcString = leftString + NewString + rightString;
        ++replaced;
      }
      
return SrcString;
    }
    
/// <summary>
    /// 
Gets the ProjectGuid attribute from a project file.
    
/// </summary>
    /// <param name="FilePath">
Full file path of the project file.</param>
    /// <returns>
A string representing the GUID value.</returns>
    /// <remarks>
    /// <para>
    /// 
The GUID is returned in the format:
    
/// '{XXXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX}'.
    
/// </para>
    /// <para>
    /// 
If the file has a .csproj extension, the file is assumed
    
/// to be a C# project file. If the file has a .vcproj extension,
    
/// the file is assumed to be a VC++ project file.
    
/// </para>
    /// </remarks>
    
private string GetProjectGuid(string FilePath)
    {
      
string guid = null;
      
using(StreamReader sr = new StreamReader(FilePath))
      {
        
try
        
{
          XmlTextReader xr = 
new XmlTextReader(sr);
          
// Check if the filepath is a C# project
          
string filePath = FilePath.ToLower();
          
if(filePath.LastIndexOf(".csproj") ==
            filePath.Length - 7)
          {
            
// Move to the <VisualStudioProject> tag.
            
xr.MoveToContent();
            
// Move to the <CSHARP> tag.
            
xr.Read();
            xr.MoveToContent();
            
// Get the ProjectGuid attribute value.
            
guid = xr.GetAttribute("ProjectGuid", null);
          }
          
// Check if the filepath is a VC project
          
if(filePath.LastIndexOf(".vcproj") ==
            filePath.Length - 7)
          {
            
// Move to the <VisualStudioProject> tag.
            
xr.MoveToContent();
            
// Get the ProjectGUID attribute value.
            
guid = xr.GetAttribute("ProjectGUID", null);
          }
          xr.Close();
          sr.Close();
        }
        
catch
        
{
        }
      }
      
return guid;
    }
    
/// <summary>
    /// 
Returns the index of the first occurrence of Value.
    
/// </summary>
    /// <param name="SrcString">
String to search.</param>
    /// <param name="Value">
Value to search for in SrcString.</param>
    /// <param name="StartIndex">
Zero-based index to start search at.</param>
    /// <returns>
Zero-based index of the string if found, else
    
/// -1.</returns>
    /// <remarks>
    /// 
This method ignores all occurrences of Value found in the safe
    
/// area.
    
/// </remarks>
    
private int SafeIndexOf(string SrcString, string Value, int StartIndex)
    {
      
int startIndex = StartIndex - 1;
      
// Iterate through all occurrences of 'OldString'.
      
while( (startIndex = SrcString.IndexOf(Value, startIndex + 1)) >= 0)
      {
        
// Check for safe area. If inside ignore.
        
int safeStart = SrcString.IndexOf(_safetyStartTag);
        
int safeEnd = SrcString.IndexOf(_safetyEndTag);
        
if(  (safeStart < 0) || (safeEnd <= safeStart) ||
          ( (startIndex < safeStart) || (startIndex > safeEnd) ) )
        {
          
break;
        }
      }
      
return startIndex;
    }
    
/// <summary>
    /// 
Renders the template file to a project file using string
    
/// replacement.
    
/// </summary>
    /// <param name="Parm">
Parameter object.</param>
    /// <returns>
The destination file full filepath.</returns>
    /// <remarks>
    /// <para>
    /// <see cref="TemplateParameter">
Parm</see> is used to
    
/// simplify the passing of multiple parameters in one 
    
/// structure.
    
/// </para>
    /// <para>
    /// 
The method uses Microsoft template standard, and customized
    
/// string replacement tags in order to create the output file.
    
/// </para>
    /// </remarks>
    
public string RenderTemplate(TemplateParameter Parm)
    {
      
// Read the file into a string.
      
FileStream fs = new FileStream(
        Parm.TemplateFilePath, FileMode.Open, FileAccess.Read);
      StreamReader sr = 
new StreamReader(fs);
      
string strFile = sr.ReadToEnd();
      sr.Close();
      fs.Close();
      
// Standard string replacement.
      
strFile = this.SafeReplace(strFile, "[!output SAFE_NAMESPACE_NAME]", 
        Parm.NamespaceName, -1);
      strFile = 
this.SafeReplace(strFile, "[!output SAFE_CLASS_NAME]", 
        Parm.ClassName, -1);
      
// Custom output string replacement.
      
strFile = this.SafeReplace(strFile, "[!output WIZWIZ_WIZARD_TITLE]", 
        Parm.WizardTitle, -1);
      strFile = 
this.SafeReplace(strFile, "[!output WIZWIZ_PAGE_NAME]", 
        Parm.SafePageName, -1);
      strFile = 
this.SafeReplace(strFile, "[!output WIZWIZ_PAGE_NO]", 
        Parm.PageOf, -1);
      
// Build the page creation code section to replace the
      // '!output_WIZWIZ_INSERT_PAGES' tag
      
const string insertPageTag = "[!output WIZWIZ_INSERT_PAGES]";
      
if(strFile.IndexOf(insertPageTag) >= 0)
      {
        
// Create and add each wizard page
        
string code = "";
        
// For each page name, create the code line that creates
        // the page object.
        
for(int i = 0; i < Parm.NoOfPages; i++)
        {
          code +=
            "\t\t\twizard.InsertPage(" +
            i.ToString() +
            ", new " + Parm.PageNames[i].ToString() + 
            "(wizard));";
          
if(i < Parm.NoOfPages - 1)
          {
            code += "\r\n";
          }
        }
        strFile = 
this.SafeReplace(strFile, insertPageTag, code, -1);
      }
      
// Replace the GUID for the C# project as the output
      // GUID (usually only in the Setup project)
      
const string wizwizOutputGuid = "[!output WIZWIZ_OUTPUT_GUID]";
      
while(this.SafeIndexOf(strFile, wizwizOutputGuid, 0) >= 0)
      {
        strFile = 
this.SafeReplace(strFile, wizwizOutputGuid, 
          Parm.CSProjectGuid, 1);
      }
      
// Replace the GUID for the VC++ project as the output
      // GUID (usually only in the Setup project)
      
const string wizwizResOutputGuid = "[!output WIZWIZRES_OUTPUT_GUID]";
      
while(this.SafeIndexOf(strFile, wizwizResOutputGuid, 0) >= 0)
      {
        strFile = 
this.SafeReplace(strFile, wizwizResOutputGuid, 
          Parm.VCProjectGuid, 1);
      }
      
// Generate a new GUID string for the projects that
      // require a GUID in the format {XXXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX}
      
const string insertGuidTag = "[!output NEW_GUID_STRING]";
      
while(this.SafeIndexOf(strFile, insertGuidTag, 0) >= 0)
      {
        Guid guid = Guid.NewGuid();
        strFile = 
this.SafeReplace(strFile, insertGuidTag, 
          guid.ToString("B").ToUpper(), 1);
      }
      
// Remove the safety tags
      
strFile = strFile.Replace(_safetyStartTag, "");
      strFile = strFile.Replace(_safetyEndTag, "");
      
// Create the destination file.
      
string dirPath = Directory.GetParent(Parm.DestFullPath).FullName;
      
if( ! Directory.Exists(dirPath))
      {
        Directory.CreateDirectory(dirPath);
      }
      
// Write the string to the file.
      
FileStream fs2 = new FileStream(Parm.DestFullPath, 
        FileMode.CreateNew, FileAccess.Write);
      StreamWriter sw = 
new StreamWriter(fs2);
      sw.Write(strFile);
      sw.Close();
      fs2.Close();
      
return Parm.DestFullPath;
    }
    
/// <summary>
    /// 
Outer method to create a project file from a template file.
    
/// </summary>
    /// <param name="Wizard">
Wizard object.</param>
    /// <param name="Project">
Project object.</param>
    /// <param name="Parm">
Template parameters.</param>
    /// <param name="DestDirPath">
Destination full directory-path.</param>
    /// <param name="OutputFilename">
Output file name.</param>
    /// <param name="TemplateFilename">
Template file name.</param>
    /// <param name="NamespaceName">
Namespace name.</param>
    /// <param name="ClassName">
Class name.</param>
    /// <param name="AddFile">
True if the project file is to be added to 
    
/// the project, else false if the file is only to be copied to the
    
/// destination directory.</param>
    /// <param name="AsResource">
True if the file is to be added to the
    
/// project as an embedded resource, else false.</param>
    /// <param name="CopyFile">
True if the file is to be copied only.
    
/// False, if the file is to be parsed and tags replaced.</param>
    /// <returns>
The project item object representing the created
    
/// file.</returns>
    /// <remarks>
    /// <para>
    /// 
If the file is not added as a file (AddFile == false) or
    
/// as a resource (AsResource == false), then the return object is null.
    
/// In this case the file is copied to the destination 
    
/// directory and the tags are replaced, but the project is not modified.
    
/// If this is the case, then then the passed in value for Project may
    
/// be null.
    
/// </para>
    /// <para>
    /// 
If CopyFile is true, then the above paragraph applies, but the 
    
/// <see cref="RenderTemplate"/> is not called to render the file - the
    
/// file is copied without modification to the destination folder.
    
/// </para>
    /// </remarks>
    
private ProjectItem CreateFile(Wizard Wizard, EnvDTE.Project Project,
      TemplateParameter Parm, 
string DestDirPath, string OutputFilename, 
      
string TemplateFilename, string NamespaceName, string ClassName, 
      
bool AddFile, bool AsResource, bool CopyFile)
    {
      ProjectItem item = 
null;
      
string destDirPath = DestDirPath;
      
if( ! destDirPath.EndsWith(@"\"))
        destDirPath += @"\";
      
// Initialize parameters.
      
Parm.ClassName = ClassName;
      Parm.DestFullPath = destDirPath + OutputFilename;
      Parm.NamespaceName = NamespaceName;
      Parm.TemplateFilePath = Wizard.TemplatePath + TemplateFilename;
      
// Create the output file
      // If copy only do not parse file or attempt to add it to the project.
      
string outputFilePath = Parm.DestFullPath;
      
if(CopyFile == true)
      {
        File.Copy(Parm.TemplateFilePath, Parm.DestFullPath, 
true);
      }
      
else
      
{
        
// Parse file and add it to the project.
        
outputFilePath = this.RenderTemplate(Parm);
      }
      
if(AddFile == true)
      {
        
if(AsResource)
        {
          
// .resx file usually.
          
item = Project.ProjectItems.AddFromFileCopy(outputFilePath);
        }
        
else
        
{
          
// .cs and other files usually.
          
item = Project.ProjectItems.AddFromFile(outputFilePath);
        }
      }
      
return item;
    }
    
private void SetRegisterForCOMInterop(Project Project, string ConfigName,
      
bool Register)
    {
      
try
      
{
        Configurations configs = Project.ConfigurationManager.
          ConfigurationRow(ConfigName);
        
foreach(Configuration config in configs)
        {
          Property prop = 
            config.Properties.Item("RegisterForComInterop");
          prop.Value = Register;
        }
      }
      
catch
      
{
      }
    }
    
// Creates the Wizard C# project.
    
private EnvDTE.Project AddWizWizProject(Wizard Wizard, Solution Solution,
      _DTE IDEObject, 
string SolutionName, string SolutionPath)
    {
      
string vsBaseFolderPath = SolutionPath + @"\" + SolutionName + @"\";
      
string vsProjFolderPath = vsBaseFolderPath + _projFolder;
      
string vsTemplateFolderPath = vsBaseFolderPath + _templateFolder;
      
// Create the project directory
      
if( ! Directory.Exists(vsBaseFolderPath))
      {
        Directory.CreateDirectory(vsBaseFolderPath);
      }
      
// Create the WizWiz project.
      
EnvDTE.Project project = Solution.AddFromTemplate(
        Wizard.WizardPath + @"\DefaultDll.csproj", vsBaseFolderPath, 
          SolutionName, 
false);
      VSProject vsProject = project.Object 
as VSProject;
      
// Add project references
      
vsProject.References.Add("envdte");
      vsProject.References.Add("System");
      vsProject.References.Add("System.Data");
      vsProject.References.Add("System.Drawing");
      vsProject.References.Add("System.Windows.Forms");
      vsProject.References.Add("System.XML");
      vsProject.References.Add("VSLangProj");
      
// Add files to project
      // Initialize template parameters
      
DefineWizardPage wizInfoPage = Wizard.Page(1) as DefineWizardPage;
      TemplateParameter parm = 
new TemplateParameter();
      parm.WizardTitle = wizInfoPage.WizardTitle;
      parm.NoOfPages = wizInfoPage.NumberOfPages;
      parm.PageNames = wizInfoPage.PageNames;
      
// Create the project folder
      
if( ! Directory.Exists(vsProjFolderPath))
      {
        Directory.CreateDirectory(vsProjFolderPath);
      }
      
// Add WizWiz.vsdir
      
ProjectItem item = this.CreateFile(Wizard, project, parm,
        vsProjFolderPath, SolutionName + ".vsdir", "WizWiz.vsdir",
        SolutionName, SolutionName, 
truefalsefalse);
      
// Ensure file is set as Build Action = Content
      
item.Properties.Item("BuildAction").Value = 
        prjBuildAction.prjBuildActionContent;
      
// Add WizWiz.vsz
      
item = this.CreateFile(Wizard, project, parm, vsProjFolderPath, 
        SolutionName + ".vsz", "WizWiz.vsz", SolutionName, 
        SolutionName, 
truefalsefalse);
      
// Ensure file is set as Build Action = Content
      
item.Properties.Item("BuildAction").Value = 
        prjBuildAction.prjBuildActionContent;
      
// Add AssemblyInfo.cs
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "AssemblyInfo.cs", "AssemblyInfo.cs", SolutionName, 
        SolutionName, 
truefalsefalse);
      
// Add CancelWizard.cs
      
this.CreateFile(Wizard, project, parm,
        vsBaseFolderPath, "CancelWizard.cs", "CancelWizard.cs",
        SolutionName, SolutionName, 
truefalsefalse);
      
// Add CancelWizard.resx as embedded resource
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "CancelWizard.resx", "CancelWizard.resx", SolutionName, 
        SolutionName, 
truetruefalse);
      
// Add PageName.cs
      
this.CreateFile(Wizard, project, parm,
        vsBaseFolderPath, "PageName.cs", "PageName.cs",
        SolutionName, SolutionName, 
truefalsefalse);
      
// Add PageName.resx as embedded resource
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "PageName.resx", "PageName.resx", SolutionName, 
        SolutionName, 
truetruefalse);
      
// Add Win32.cs
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "Win32.cs", "Win32.cs", SolutionName, 
        SolutionName, 
truefalsefalse);
      
// Add Wizard.cs
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "Wizard.cs", "Wizard.cs", SolutionName, 
        SolutionName, 
truefalsefalse);
      
// Add WizardControl.cs
      
this.CreateFile(Wizard, project, parm,
        vsBaseFolderPath, "WizardControl.cs", "WizardControl.cs",
        SolutionName, SolutionName, 
truefalsefalse);
      
// Add WizardControl.resx as embedded resource
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "WizardControl.resx", "WizardControl.resx", SolutionName, 
        SolutionName, 
truetruefalse);
      
// Add WizardForm.cs
      
this.CreateFile(Wizard, project, parm,
        vsBaseFolderPath, "WizardForm.cs", "WizardForm.cs",
        SolutionName, SolutionName, 
truefalsefalse);
      
// Add WizardForm.resx as embedded resource
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "WizardForm.resx", "WizardForm.resx", SolutionName, 
        SolutionName, 
truetruefalse);
      
// Create the template folder
      
if( ! Directory.Exists(vsTemplateFolderPath))
      {
        Directory.CreateDirectory(vsTemplateFolderPath);
      }
      
// Add the banner bitmap file to the template folder.
      
item = this.CreateFile(Wizard, project, parm, 
        vsTemplateFolderPath, SolutionName + ".bmp", "WizWiz.bmp", 
        SolutionName, SolutionName, 
truefalsetrue);
      
// Ensure file is set as Build Action = Content
      
item.Properties.Item("BuildAction").Value = 
        prjBuildAction.prjBuildActionContent;
      
// Add the setup bitmap file to the template folder.
      
item = this.CreateFile(Wizard, project, parm, vsTemplateFolderPath, 
        SolutionName + "Setup.bmp", "WizWizSetup.bmp", SolutionName, 
        SolutionName, 
truefalsetrue);
      
// Ensure file is set as Build Action = Content
      
item.Properties.Item("BuildAction").Value = 
        prjBuildAction.prjBuildActionContent;
      
// Add WizWiz.ico to the template folder
      
item = this.CreateFile(Wizard, project, parm, vsTemplateFolderPath, 
        SolutionName + ".ico", "WizWiz.ico", SolutionName, 
        SolutionName, 
truefalsetrue);
      project.Properties.Item("ApplicationIcon").Value = 
        parm.DestFullPath;
      
// Ensure file is set as Build Action = Content
      
item.Properties.Item("BuildAction").Value =
        prjBuildAction.prjBuildActionContent;
      
// Add WizWiz.cs
      
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
        "WizWiz.cs", "WizWiz.cs", SolutionName, 
        SolutionName, 
truefalsefalse);
      
// Create and add each wizard page
      
for(int i = 0; i < wizInfoPage.NumberOfPages; i++)
      {
        parm.PageName = wizInfoPage.PageNames[i].ToString();
        parm.PageNo = i + 1;
        
// Add WizardPage.cs
        
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
          parm.SafePageName + ".cs", "WizardPage.cs", SolutionName, 
          parm.SafePageName, 
truefalsefalse);
        
// Copy WizardPage.resx
        
this.CreateFile(Wizard, project, parm, vsBaseFolderPath, 
          parm.SafePageName + ".resx", "WizardPage.resx", SolutionName, 
          parm.SafePageName, 
truetruefalse);
      }
      
// Set the configurations to Register for COM-Interop.
      
this.SetRegisterForCOMInterop(project, "Debug", true);
      
this.SetRegisterForCOMInterop(project, "Release", true);
      
// Save the project
      
project.Save(vsBaseFolderPath + SolutionName + ".csproj");
      
return project;
    }
    
// Creates the Wizard C++ resource project.
    
private EnvDTE.Project AddWizWizResProject(Wizard Wizard, Solution Solution,
      _DTE IDEObject, 
string SolutionName, string SolutionPath)
    {
      
string vsBaseFolderPath = SolutionPath + @"\" + SolutionName + @"Res\";
      
string projectName = SolutionName + "Res";
      
// Create the project directory
      
if( ! Directory.Exists(vsBaseFolderPath))
      {
        Directory.CreateDirectory(vsBaseFolderPath);
      }
      
// Use projectName as the Namespace name and the
      // and SolutionName as the Class Name in the CreateFile(..) method.
      // Add resource.h (do not add to vcproj-already added from template)
      
TemplateParameter parm = new TemplateParameter();
      
this.CreateFile(Wizard, null, parm, vsBaseFolderPath, 
        "resource.h", "resource.h", projectName, 
        SolutionName, 
falsefalsefalse);
      
// Add WizWiz.rc (do not add to vcproj-already added from template)
      
this.CreateFile(Wizard, null, parm, vsBaseFolderPath, 
        projectName + ".rc", "WizWiz.rc", projectName, 
        SolutionName, 
falsefalsefalse);
      
// Create the resource directory
      
string resourcePath = vsBaseFolderPath + "res";
      
if( ! Directory.Exists(resourcePath))
      {
        Directory.CreateDirectory(resourcePath);
      }
      
// Add WizWiz.ico (do not add to vcproj-already added from template)
      
this.CreateFile(Wizard, null, parm, resourcePath, 
        projectName + ".ico", "WizWiz.ico", projectName, 
        SolutionName, 
falsefalsetrue);
      
// Add the vcproj file into destination directory
      
this.CreateFile(Wizard, null, parm, vsBaseFolderPath, 
        projectName + ".vcproj", "WizWizRes.vcproj", projectName, 
        SolutionName, 
falsefalsefalse);
      
// Create the WizWizRes project from the existing vcproj file
      
EnvDTE.Project project = Solution.AddFromFile(
        vsBaseFolderPath + projectName + ".vcproj", 
false);
      
return project;
    }
    
// Creates the wizard setup project.
    
private EnvDTE.Project AddWizWizSetupProject(Wizard Wizard, Solution Solution,
      _DTE IDEObject, 
string SolutionName, string SolutionPath)
    {
      
string vsBaseFolderPath = SolutionPath + @"\" + SolutionName + @"Setup71\";
      
string projectName = SolutionName + "Setup71";
      
// Create the project directory
      
if( ! Directory.Exists(vsBaseFolderPath))
      {
        Directory.CreateDirectory(vsBaseFolderPath);
      }
      TemplateParameter parm = 
new TemplateParameter();
      parm.CSProjectGuid = 
this.GetProjectGuid(
        SolutionPath + @"\" + SolutionName + @"\" + SolutionName + ".csproj");
      parm.VCProjectGuid = 
this.GetProjectGuid(
        SolutionPath + @"\" + SolutionName + @"Res\" + SolutionName + "Res.vcproj");
      
// Use projectName as the Namespace name and the
      // and SolutionName as the Class Name in the CreateFile(..) method.
      // Add the vdproj file into destination directory
      
this.CreateFile(Wizard, null, parm, vsBaseFolderPath, 
        projectName + ".vdproj", "WizWizSetup.vdproj", projectName, 
        SolutionName, 
falsefalsefalse);
      
// Create the WizWizSetup project from the existing vdproj file
      
EnvDTE.Project project = Solution.AddFromFile(
        vsBaseFolderPath + projectName + ".vdproj", 
false);
      
return project;
    }
    
/// <summary>
    /// 
The project creation method.
    
/// </summary>
    /// <param name="Wizard">
Wizard object.</param>
    /// <param name="IDEObject">
DTE Automation object.</param>
    /// <param name="SolutionName">
Solution name. 
    
/// Also the solution file name.</param>
    /// <param name="SolutionPath">
Full directory-path of the 
    
/// solution (.sln) file</param>
    
public void Create(Wizard Wizard, _DTE IDEObject, string SolutionName, 
      
string SolutionPath)
    {
      
// Suppress UI output during execution
      
bool suppressUI = IDEObject.SuppressUI;
      IDEObject.SuppressUI = 
true;
      
// Get the current solution
      
Solution solution = IDEObject.Solution;
      
// If its open, close it. (A new solution is always created).
      
if(solution.IsOpen == true)
      {
        solution.Close(
true);
      }
      
// Create a new solution.
      
solution.Create(SolutionName, SolutionPath);
      
// If the solution directory does not exist, create it.
      
if( ! Directory.Exists(SolutionPath))
      {
        Directory.CreateDirectory(SolutionPath);
      }
      
// Solution Path
      
string solutionPath = SolutionPath + @"\" + SolutionName + ".sln";
      
// Create the WizWiz C# project.
      
EnvDTE.Project wizwizProj = this.AddWizWizProject(Wizard, solution, 
        IDEObject, SolutionName, SolutionPath);
      
// Save the solution before adding the VC resource project
      // Avoids the VS 'Class View NCB file not created' error dialog.
      
solution.SaveAs(solutionPath);
      
// Close and reopen the solution.
      
solution.Close(false);
      solution.Open(solutionPath);
      
// Create the WizWizRes C++ project.
      
EnvDTE.Project wizwizResProj = this.AddWizWizResProject(Wizard, solution,
        IDEObject, SolutionName, SolutionPath);
      
// Create the WizWizSetup deployment project.
      
EnvDTE.Project wizwizSetupProj = this.AddWizWizSetupProject(Wizard, solution,
        IDEObject, SolutionName, SolutionPath);
      
// Save the solution.
      
solution.SaveAs(solutionPath);
      
// Close and reopen the solution.
      
solution.Close(false);
      solution.Open(solutionPath);
      
// Build the solution if the Build Checkbox set.
      
FinishPage finishPage = Wizard.Page(2) as FinishPage;
      
if(finishPage.BuildWhenCreated == true)
      {
        solution.SolutionBuild.Build(
true);
      }
      
// Restore the UI settings
      
IDEObject.SuppressUI = suppressUI;
      EnvDTE.Window solutionExplorer = 
        IDEObject.Windows.Item(Constants.vsWindowKindSolutionExplorer);
      solutionExplorer.Visible = 
true;
      solutionExplorer.Activate();
    }
    
/// <summary>
    /// 
Automation object entry point.
    
/// </summary>
    /// <param name="Application">
A dispatch pointer to the highest-level 
    
/// automation object for the Visual Studio .NET environment.</param>
    /// <param name="hwndOwner">
The hWnd handle for the parent of the 
    
/// wizard's window.</param>
    /// <param name="ContextParams">
An array of elements that vary,
    
/// depending on whether your wizard is launched from the 
    
/// <b>Add New Item</b> or <b>New Project</b> dialog box.
    
/// </param> 
    /// <param name="CustomParams">
An array of user-defined parameters, 
    
/// determined by the <b>param=</b> statements in the wizard's .vsz file. 
    
/// </param>
    /// <param name="retval">
Who knows?</param>
    
public void Execute(object Application, int hwndOwner, 
      
ref object[] ContextParams, ref object[] CustomParams, 
      
ref EnvDTE.wizardResult retval)
    {
      _DTE IDEObject = Application 
as _DTE;
      
// Create a new Wizard application.
      
Wizard wizard = new Wizard();
      
// Create and add the Wizard pages.
      
wizard.InsertPage(0, new IntroPage(wizard));
      wizard.InsertPage(1, 
new DefineWizardPage(wizard));
      wizard.InsertPage(2, 
new FinishPage(wizard));
      
// Start with the first page.
      
wizard.ShowPage(0);
      
// Show the wizard.
      
if(wizard.Form.ShowDialog() == DialogResult.OK)
      {
        
// If the wizard is completed successfully, create the solution objects
        
Create(wizard, IDEObject, ContextParams[1].ToString(), 
          ContextParams[2].ToString());
      }
    }
  }
}