The following HTML Help file can be downloaded:
The WizWiz Wizard User Interface
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
Customizing the Wizard after Creation
Appendix 1: Source Code for WizWiz.cs
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 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
WizWiz will create a new VS solution with the following features:
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:
CSharpWizard1.vsz and CSharpWizard1.vsdir in the '<C# Device Install Folder>\CSharpProjects'. The .vsz and .vsdir files used by WizWiz are found in the 'VS Project Files' folder of the WizWiz project.
CSharpWizard1.dll (primary output of the C# project) and CSharpWizard1Res.dll (primary output of the C++ project) in the '<VS.NET Install Folder>'.
All content or template files that may be required by the wizard when it generates the project files for VS. These are placed in the '<Wizard Template Folder>'. Template files are those program files that contain replaceable tags which are copied to the destination folder by the wizard and the tags are replaced by variables such as the namespace or class name. This action customizes the files so that they can be built in the correct project and solution context. The template files used by WizWiz are found in the 'VS Template Files' folder of the WizWiz project.
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:
'C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\CSharpProjects'.
The folder 'C:\Program Files\Microsoft Visual Studio .NET 2003\VC#' is set in the registry as the string value at 'HKLM\SOFTWARE\Microsoft\VisualStudio\7.1\InstalledProducts\Smart Device Extensions\CSharpDeviceInstallDir'. This registry entry is used by the Installer to install the .vsz and .vsdir in the correct folder.
VS.NET Install Folder
Assuming a standard install of VS in the 'C:\Program Files' folder, the actual folder is:
'C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE'.
The folder is set in the registry as the string value at 'HKLM\SOFTWARE\Microsoft\VisualStudio\7.1\InstallDir'. This registry entry is used by the Installer to install the primary output files for the C# and C++ project in the correct folder.
Wizard Template Folder
Assuming a standard install of VS in the 'C:\Program Files' folder:
The VS Wizard root folder is: 'C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards'.
The CSharpWizard1 root folder is 'C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards\CSharpWizard1'.
The CSharpWizard1 template folder is 'C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards\CSharpWizard1\Templates\1033' to conform to the VS standard.
The VS Wizard root folder is derived using the registry settings for the 'C# Device Install Folder' above and adding the 'VC#Wizards' sub-folder string.
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"
The first line shows the version number of the template file format. In the case of WizWiz this is not important, as the template handling is done entirely by the wizard itself.
The second line is the ProgID of the wizard. This is specified in WizWiz.cs in the class Builder which implements the required VS automation interface for integrated wizards, namely IDTWizard. The following lines sets the ProgID in WizWiz.cs:
[ProgId("Bware.Wizards.CSharpWizWiz")]
public class Builder : IDTWizard
{
...
}
The following lines are a set of optional parameters that allow the .vsz file to pass additional custom parameters to the wizard. Each value is passed as a string element in an array of variants in the Builder.Execute method. WizWiz does not use these parameters.
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
'WizWiz.vsz' refers to the applicable project control file.
'C# Wizard Builder' is the name that appears in the 'New Project' dialog box.
'1' is the order of appearance of project items in the dialog. A value of '1' means it will appear first (usually).
The next field is the description of the wizard as it appears in the 'New Project' dialog.
'WizWizRes' is the DLL (generated by the C++ project) that contains the icon to display in the 'New Project' dialog.
'1' refers to the IconResourceId of the icon in the DLL to display.
'CSharpWizard' provides the default name for the new base solution that will be created by the wizard. In reality, a number is normally appended to this name starting with '1'.
Wizard Implementation File (.dll)
This file is the component that is loaded by VS to provide the wizard functionality. WizWiz.dll implements the following:
Registers as a COM component (COM Interop).
Implements the IDTWizard interface.
Provides meaningful wizard-like functionality. WizWiz builds a wizard by programming the DTE interface, converting templates into project files and building VS project structures.
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:
Provides a C++ (Win32) resource-only DLL that contains a single icon. This DLL is referenced by the .vsdir file, as mentioned above, for display purposes.
Is used mainly because currently this author is unable to get VS to reference an icon in the C# implementation DLL, as an embedded resource or otherwise. An alternative is to provide the icon in a Win32 DLL, as is currently implemented by WizWiz.
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.
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:
copy /Y "$(TargetPath)" "$(DevEnvDir)$(TargetFileName)"
This will copy the C# DLL into the VS.NET install folder.
On successful build
Program
public override void OnShowPage()
{
base.OnShowPage();
this.Wizard.Form.PageTitle = " C# Wizard Builder Page 2/3"; // Set the page title here...
}
// Create and add the Wizard pages. wizard.InsertPage(0, new IntroPage(wizard)); wizard.InsertPage(1, new DefineWizardPage(wizard)); wizard.InsertPage(2, new FinishPage(wizard));
Notes:
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 '<<!' (ignore the single-quote characters) on its own
/// line, and ends with the tag '!>>' (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, true, false, false);
// 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, true, false, false);
// 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, true, false, false);
// Add CancelWizard.cs
this.CreateFile(Wizard, project, parm,
vsBaseFolderPath, "CancelWizard.cs", "CancelWizard.cs",
SolutionName, SolutionName, true, false, false);
// Add CancelWizard.resx as embedded resource
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"CancelWizard.resx", "CancelWizard.resx", SolutionName,
SolutionName, true, true, false);
// Add PageName.cs
this.CreateFile(Wizard, project, parm,
vsBaseFolderPath, "PageName.cs", "PageName.cs",
SolutionName, SolutionName, true, false, false);
// Add PageName.resx as embedded resource
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"PageName.resx", "PageName.resx", SolutionName,
SolutionName, true, true, false);
// Add Win32.cs
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"Win32.cs", "Win32.cs", SolutionName,
SolutionName, true, false, false);
// Add Wizard.cs
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"Wizard.cs", "Wizard.cs", SolutionName,
SolutionName, true, false, false);
// Add WizardControl.cs
this.CreateFile(Wizard, project, parm,
vsBaseFolderPath, "WizardControl.cs", "WizardControl.cs",
SolutionName, SolutionName, true, false, false);
// Add WizardControl.resx as embedded resource
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"WizardControl.resx", "WizardControl.resx", SolutionName,
SolutionName, true, true, false);
// Add WizardForm.cs
this.CreateFile(Wizard, project, parm,
vsBaseFolderPath, "WizardForm.cs", "WizardForm.cs",
SolutionName, SolutionName, true, false, false);
// Add WizardForm.resx as embedded resource
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
"WizardForm.resx", "WizardForm.resx", SolutionName,
SolutionName, true, true, false);
// 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, true, false, true);
// 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, true, false, true);
// 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, true, false, true);
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, true, false, false);
// 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, true, false, false);
// Copy WizardPage.resx
this.CreateFile(Wizard, project, parm, vsBaseFolderPath,
parm.SafePageName + ".resx", "WizardPage.resx", SolutionName,
parm.SafePageName, true, true, false);
}
// 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, false, false, false);
// Add WizWiz.rc (do not add to vcproj-already added from template)
this.CreateFile(Wizard, null, parm, vsBaseFolderPath,
projectName + ".rc", "WizWiz.rc", projectName,
SolutionName, false, false, false);
// 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, false, false, true);
// Add the vcproj file into destination directory
this.CreateFile(Wizard, null, parm, vsBaseFolderPath,
projectName + ".vcproj", "WizWizRes.vcproj", projectName,
SolutionName, false, false, false);
// 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, false, false, false);
// 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());
}
}
}
}