visual Visual Studio .NET supports a number of built in project types and wizards that generate skeleton projects and solutions depending on the type of application the developer intend to build. These project templates are an excellent way to automate the basic plumbing required for very common project types, such as Windows Applications, Class Libraries and Web Services. However, Visual Studio .NET supports only those basic project types that are extremely common across the developer community. Given the wide diversity and disparity of developer needs, Visual Studio .NET (and for that matter, previous versions of Visual Studio as well) expose an extensible architecture, where developers can write their own custom project types and wizards. This is an excellent feature that allows developers to write canned skeleton code that can be generated automatically, without having to resort to the infamous "copy-paste" anti-pattern. One of the strongest motivations for writing custom wizards is to write skeleton code once, and have it automatically customized, generated, and added to further projects. In this article, we will explore Visual Studio .NET's support for building custom wizards, and discuss how developers can create their own wizards and project templates. Further, we will build a sample wizard that generates a class that maintains a thread pool holding a specified number of threads. Each of these threads will call a user specified method via the use of
delegates. All the thread management will be handled by the generated code.
Note: this article is based on Visual Studio .NET Beta 2 and Windows 2000.
In this article, we use the words
Wizards and
Templates interchangeably.
Existing Projects Types and Wizards
The following screen shot shows some of the project templates and wizards that are supported by Visual Studio .NET by default.
When a project type and template is chosen, Visual Studio .NET creates a solution and project based on the user's selection, and generates some files that contain the skeleton code. Some project templates, like the
Class Library template, generate very simple code that contains one class and a stub for its constructor, while templates such as
Visual Studio .NET Add-ins or
ATL Server generate quite a few files, each containing substantial code. This generated code is usually stored in a file with placeholders for user input values, such as the class and project name. When a specific project type is chosen, Visual Studio .NET invokes a component that reads these canned (also known as templated) files, and substitutes the placeholders with the user input values. Next, this component creates the required projects and solutions using the
Visual Studio .NET Extensibility model. Visual Studio .NET looks for specific files in a fixed location that contain information about the available project templates, and the components that will be responsible for creating new projects based on that template. We will cover this architecture in the next section.
Visual Studio .NET Project Template Architecture
Visual Studio .NET uses a component based model to support the project templates. Each project template has a corresponding component that handles requests for the given template. A single component can handle requests for multiple templates as well. Visual Studio .NET uses two types of files that are used to identify the project templates, and the components that handle requests for these project templates. The first file (identified by a .vsz extension) contains information about the component, and any custom parameters that the component handling the project template might need. The second file (identified by a .vsdir extension) contains information about the description of the project type, and display properties in the
New Project dialog box. Let us now explain these two files in detail.
- VSZ File: This file contains information for a single project template. The name of the file corresponds to an entry in the .vsdir files (explained below), and the contents of the file contains the ProgId or the ClassId of the component that will be invoked when this specific project template is chosen. Further, this file can hold custom parameters that will be passed to the component, when invoked. These parameters are typically used when a single component handles multiple template types. The Visual Studio .NET IDE is responsible for parsing this file and passing the parameters to the handler component.
A sample .vsz file that handles
C# Console Projects is shown below. The first line is used for versioning, the Wizard tag is used to point to the ProgId of the component that will handle requests for C# console projects, and the multiple Param tags contain information that will be passed to the VsWizard.VsWizardEngine component. Note that this component handles multiple templates, so the parameters specify the specific template (console project) and the selected language (C#).
VSWIZARD 7.0
Wizard=VsWizard.VsWizardEngine
Param="WIZARD_NAME = CSharpConsoleWiz"
Param="WIZARD_UI = FALSE"
Param="PROJECT_TYPE = CSPROJ"
All components that act as handlers for project templates need to support the IDTWizard interface from the EnvDTE namespace. This wizard contains a single method, Execute, which is called when the template is invoked. Visual Studio .NET passes the base DTE object (used to access the entire Visual Studio .NET object model, please refer to the links section for articles that cover this), a parameter list containing information such as the name and path of the project as specified by the user, and another parameter list that contains the custom parameters from the .vsz files as shown above. This information is sufficient for the component to process the template request, and since the method has access to the root DTE object, the component can create solution and projects by invoking methods on this object.
- VSDIR File: This file contains information about the individual .vsz files explained above. This information relates mainly to the display of the template in the New Project dialog box displayed by Visual Studio .NET. This display information includes a description for the template that is displayed when the template is selected, information about the icon that will be displayed in the New Project dialog box, and flags that control certain elements on this dialog box. A .vsdir file typically contains a single line entry for multiple .vsz files. The different fields are separated by a "|" character, and the different lines are separated by a new line character. Multiple .vsdir files are supported by Visual Studio .NET.
A sample .vsdir file containing two entries is shown below.
CSharpEXE.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|#2318|10|#2319|
{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4554| |WindowsApplication
CSharpWebApp.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|#2326|40|#2327|
{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4551|1|WebApplication1|web
Some of the main fields in this file are described below. For a complete description of these fields and their relative orders, please refer to the links section at the end of this article.
- RelPathName: This field contains the path for the .vsz file.
- SortPriority: This field defines the order in which the different templates will be displayed in the New Project dialog box.
- Description: This field contains a descriptive string that displays information about the specific template.
- DLLPath: This field points to the DLL that contains an icon that will be displayed for this template in the New Project dialog box.
When the
New Project dialog box is invoked, Visual Studio .NET reads all the .vsdir and the .vsz files to display the available project templates. For the C# project type, this is read from the
Microsoft Visual Studio.NET\VC#\CSharpProjects folder as shown below. This folder contains all the files that represent the C# templates that are available to the
New Project dialog box. Later in the article, when we create our custom wizard, we will have to copy the .vsz and .vsdir files into this folder.