Basics of Extending Your Working Environment in Visual Studio


All of us have used Macros in Microsoft Office and Add-ins in Visual Studio 6 and I am very sure all of us have been impressed as well by their power to control their own working environment. Visual studio.net offers a wide variety of options, which enable the user to create custom and personalized Visual Studio working environments. These several options include 

  • Macros
  • Add Ins
  • Wizards
  • Custom Tool Windows.

The one that interested me most is the Wizard. This is very similar to an Add-In. Today we will take a look at how to create a wizard in a few easy steps.

Why Wizards

Lets first look at what we can accomplish using these wizards. The concept of Wizard is now common knowledge. Wizards take different forms, from guiding the user to accomplish tasks to providing templates to create documents. There are built in Wizards provided by VS.Net, which help user to create classes, datasets, XSD Schemas, create different types of solutions and projects, and much more.

In many situations wizards help the user to create custom templates, required to implement architectural solutions. Wizards can be used to create classes in different layers of a technical architecture. This helps to achieve higher productivity, reduce manual errors and produce greater standardization. This also helps technical architects to produce the pre-designed code templates for different layers in application architecture.

Wizard signifies the knowledge we have gained in past and the helps us reapply it quickly and efficiently.

Wizards

Building Wizards in VS.Net is made very easy. We need to

  • Develop a Wizard 
  • Register the Wizard
  • Use the Wizard

Lets look at each step closely 

Develop a Wizard

All the documentation available in MSDN and .Net has code samples in VB.Net. We will develop our Wizard in C#. Our wizard will help the user to create ca lass with different functions and properties.

Wizards are COM objects and should be registered as COM libraries.

We are going to create a class library project. We may create Windows Application project as well. To ensure that the project is registered as COM object, we should change the project property on the build tab Register for COM Interop to true.

We should have at least one public class in the project.

1. Create Class Library Application



2. Change the Register for COM Interop to true  



3. Create Public class

Now lets create the User interface to create the wizard. This interface will help the user to specify the functions and properties of the class being created. To create the UI lets add a windows form to the project.

using System;
namespace My_Wizard_Sample
{
/// <summary>
/// Class to create Wizard
/// </summary>
public class WizardSample
{
public WizardSample()
{
//
// TODO: Add constructor logic here
//
}
}
}

4. Create UI 



Later we will see how to write the code to create the class.

Register Wizard

Wizard registration is completed in two steps.

Register Wizard as COM Object

We are half way there!

To register the wizard, we will build the project and create the DLL. We need to create the GUI ID for the COM registration and use the ID in the class attribute.

5. Create GUI ID.

To create GUID go to Tools/Create GUID menu. It will open up a dialog box .



Copy the GUID by clicking on the Copy button. Now we will modify the code for class as:

using System;
using System.Runtime.InteropServices;
namespace My_Wizard_Sample
{
/// <summary>
/// Class to create Wizard
/// </summary>
[GuidAttribute("78BD68BD-26C5-4188-804D-9E29C2E6BC1C")]
public class WizardSample:IDTWizard
{
public WizardSample()
{
//
// TODO: Add constructor logic here
//
}
}
}

You can see that we have added GUIDAttribute to create GUID for the COM object.

6. Build project

This will ensure that the Wizard is registered as COM Object.

Register Wizard as VS.Net template

This is a very important step. This step will decide how the user should use our wizard.

Before we look into how to deploy the wizard lets look at the location where VS.Net wizards are deployed.

You can go to $Program Files\Microsoft Visual Studio .NET\VC#. In this folder you will see various folders such as CSharpContextItems, CSharpProjectItems, CSharpProjects, etc. Each of these folders represent different types of wizards in VS.Net for C# language. You will notice similar types of folders in $Program Files\Microsoft Visual Studio .NET\Vb7. 

Lets look at the content of $Program Files\Microsoft Visual Studio.NET\VC#\CsharpProjectItems. 

This folder contains various .VSZ files. Each of the VSZ file represents the options to create different types of project items such as Class, Windows Form, Dataset, etc.

To deploy our wizard we also need to create the VSZ files and a directory. Creating directory under $Program Files\Microsoft Visual Studio .NET\VC#\ is optional and we may create VSZ file for our wizard in $Program Files\Microsoft Visual Studio .NET\VC#\ folder it self. 

Lets look at the content of the VSZ file  

VS.Net uses to VSZ files to recognize and launch the Wizards. VSZ files are used to register a wizard with VS.Net. This file contains various Name Value pairs and looks very similar to an INI file.

This file has 3 parts.

  1. VSWizard - This is the version number of the template file format, which for Visual Studio .NET will be "VSWizard 7.0." No other numbers are valid, and using them results in an "Invalid Format" error.

  2. Wizard - This is the ProgID or alternately a string representation of the CLSID of the Wizard that should be co-created by Visual Studio.

  3. Param This section is optional. You can add as many parameters as your wizard requires. They are used to allow the .vsz file to pass additional custom parameters into the Wizard. Each value is passed as a string element in an array of variants to the Wizard. Later we will take a look at how to pass the parameters to Wizard.

To create VSZ file, open Notepad, copy following text into the file and save it as MyWizard.VSZ at $Program Files\Microsoft Visual Studio .NET\VC#\ CSharpProjectItems\LocalProjectItems path.

The VSZ file for our wizard will look like:

  

Look at the Wizard section in the MyWizard.VSZ file. It contains the GUID we generated for COM registration. Instead of GUID, we can register the class in ProgID.ClassId format as well. In that case ProgId is same as NameSpace name.

Now we have registered our Wizard with VS.Net. However in order to run the wizard we need to add some code. 

Writing Code

Till now we have just written the skeleton of the wizard. Lets add some functionality:

  1. Add reference to envdte.dll which is located at $\WINNT\Microsoft.NET\Framework\v1.0.3705\
  2. The wizard class must implement EnvDTE.IDTWizard interface. This interface will help us access VS.Net environment. This parameters to access the environment will be passed as parameters to the method.

This interface has Execute method. This method must be implemented. The signature of the interface as per the document is 

void Execute( object Application, int hwndOwner, object[] ContextParams,
object[] CustomParams, ref wizardResult retval);

However while implementing use

Execute(
object Application,
int hwndOwner,
ref object[] ContextParams,
ref object[] CustomParams,
ref wizardResult retval
)

signature otherwise you will get compilation errors.

Lets look at parameters of our interest

Parameter Name Description
Application This is object of type EnvDTE.DTEClass. We need to cast it to an interface of type _DTE. In a short while we will see the details of _DTE interface.
ContextParams

This parameter gives all the information about the context such as the:

  • Type of the item (Project Item/New Project/ Sub Project)
  • Name of the Project
  • Item Name, etc
CustomParams If you remember the Params section in the VSZ file, all the params you set in the file, will be available to you in this array of objects.
WizardResult You can specify the result of the wizard. This result can be decided based on the user actions.

Lets add code in Execute method to display the form.

public class WizardSample:IDTWizard
{
private WizardSampleUI myForm;
public WizardSample()
{
//
// TODO: Add constructor logic here
//
}
void IDTWizard.Execute( object Application, int hwndOwner, ref object[] ContextParams, ref object[] CustomParams, ref wizardResult retval )
{
myForm = new WizardSampleUI();
myForm.ShowDialog();
}
}

Lets add code to the Form. Add code to command button to close Wizard UI.

private void button1_Click(object sender, System.EventArgs e)
{
this.Close();
}

Now we can run our wizard.

Using the Wizard

  1. Open VS.Net instance.
  2. Select New Project or open an existing project.
  3. Right click on the project in Solution Explorer window and select Add NewItem.

The dialog box will look like:



Open the Wizard template and you will see form as



What we have accomplished

  • We have been able to create wizard
  • We have registered it as COM object and registered it as VS.Net wizard
  • We have added code to run the wizard.
  • We have successfully run the wizard.

_DTE Interface

Now lets take a look at _DTE interface. DTE stands for Development Tools Extensibility.

This interface is the key to the VS.Net run time environment. It exposes several properties and methods to access and manipulate different components of VS.Net runtime. Lets look at few of the properties and use them as well.

Add the following code to the Form

/// <summary>
///This property is set by the Wiaard Sample class. Form is now able to access
///various components of DTE.
/// </summary>
public _DTE DTE
{
set
{
dte =
value;
lblSolutionFullName.Text = dte.Solution.FullName;
txtSolutionPath.Text = dte.Solution.FileName;
PrintAllProjecNames();
}
}

We have added a SET property to form. This property will be set by the Wizard Template class so that form can access various properties and method of DTE object.

Now that we can access _DTE interface in form lets look at various properties of _DTE interface.

  1. Print the solution name and its path.
  2. Print all projects in solution and items in each project.
  3. Print the type of each project.

/// <summary>
/// Adds various projects opened in the solution to List Box.
/// </summary>
private void PrintAllProjecNames()
{
foreach(Project prj in this.dte.Solution.Projects)
{
lstProjects.Items.Add(prj.Name);
}
}
/// <summary>
/// Adds all the project item for selected project to the list box.
/// </summary>
private void PrintAllProjecItems()
{
foreach(Project prj in this.dte.Solution.Projects)
{
if(prj.Name == lstProjects.SelectedItem.ToString())
{
foreach(ProjectItem prjItem in prj.ProjectItems)
{
lstProjectItems.Items.Add(prjItem.Name);
}
break;
}
}
}
/// <summary>
/// Displays the project item data for selected project.
/// </summary>
private void getProjectItemData()
{
foreach(Project prj in this.dte.Solution.Projects)
{
if(prj.Name == lstProjects.SelectedItem.ToString())
{
foreach(ProjectItem prjItem in prj.ProjectItems)
{
if(lstProjectItems.SelectedItem.ToString() == prjItem.Name)
{
switch(prjItem.Kind)
{
case EnvDTE.Constants.vsProjectItemKindMisc:
lblItemKindText.Text = "Misc";
break;
case EnvDTE.Constants.vsProjectItemKindPhysicalFile:
lblItemKindText.Text = "PhysicalFile";
break;
case EnvDTE.Constants.vsProjectItemKindPhysicalFolder:
lblItemKindText.Text = "PhysicalFolder";
break;
case EnvDTE.Constants.vsProjectItemKindSubProject:
lblItemKindText.Text = "SubProject" ;
break;
}
break;
}
}
break;
}
}
}

Add the following code to Execute method of the WizardSample class.

void IDTWizard.Execute(object Application,int hwndOwner,ref object[] ContextParams,ref object[] CustomParams,ref wizardResult retval)
{
//Get running DTE instance.
myDTE = (_DTE)Application;
//Create instance of UI.
myForm = new WizardSampleUI();
//Set DTE instance in the UI form.
myForm.DTE = myDTE;
//Show UI form as modal dialog.
myForm.ShowDialog();
}

What we have accomplished

  • We have been able to create wizard
  • We have registered it as COM object and registered it as VS.Net wizard
  • We have added code to run the wizard.
  • We have successfully run the wizard.
  • We have been able to study few properties and methods of DTE object.

DTE Object Model Overview

DTE Object model exposes various components of VS.Net runtime in a structured way. You can look at this object model at MSDN as well. The objects that we have studied are Solution, Projects and project items.

When we go deeper into this object model, we will find classes that allow us to access project files, the code/text in the project files.

How to debug a Wizard project

You can debug the wizard project in the same way we debug any two VS.Net projects. Follow the steps given below:

  1. Open the wizard project.
  2. Build the project.
  3. Set break points.
  4. Open another project in another instance of VS.Net.
  5. Attach DevEnv process running the test project to Wizard project DevEnv process.
    a. In Wizard project go to Debug/Processes.
    b. Select DevEnv process running the testing project.
    c. Attach that process.
  6. In the testing project, open the wizard by selecting Add New Item. Open Wizard.

Once you have run the test project, close DevEnv process running the testing project otherwise you will not be able to build your Wizard project again.

Happy Wizarding!!!!


Similar Articles