Chapter 4. Community Content and VSTemplates

Microsoft® Visual Studio® was designed to be an upgradeable and evolving program. In this chapter, we'll show you how to use some of the new features of Visual Studio to make your programming more powerful by sharing code with other users.

Community Content

In the early hobbyist days of programming, people would often gather in small groups to share ideas about how to program or to share code. These user groups would often have a shared goal in a program that they needed to write. Maybe they wanted to write a program to balance their checkbooks, keep track of recipes, or maybe share software algorithms that would make software development easier and faster. This community of software developers still exists today, but because of tools such as the Internet, the community has grown to a global scale. Today, sites such as Microsoft's own GotDotNet.com, WindowsForms.com, and Asp.net, have sprung up, letting a user in Pittsburgh collaborate on a software project with a developer in Tokyo. You can also use the tools built into Visual Studio on the Community | Search menu to open the Microsoft Developer Network (MSDN®) help browser, where you can search for content.

Visual Studio makes collaboration between developers easier with tools designed specifically to allow you to share code and ideas. If you need help, simply go to one of your favorite programming community Web sites and make a request. Another user might have experience with the problem you are trying to solve and have some code that is similar to what you need. He or she can package the code and distribute it, either by sending the content to you directly through e-mail or by posting it to a Web site. Sharing code is the focus of the Community Content Installer—a tool that takes code created by another user and places it on your computer so that you can start using it right away.

Note

The Content Installer also gives you an opportunity if you are in the business of selling components for Visual Studio. You could package controls for the Toolbox, code snippets, and other items, and then sell those components from your Web site. Customers can then use the Content Installer to install the components they purchased.

Installing Content

Content that you install on your computer using the Content Installer is contained in a file with the extension .vsi. This file is a compressed file containing all the files necessary to correctly install data onto your computer. The data that can be installed includes controls that you can drag and drop from the Toolbox onto a WinForm or a Web form; code snippets that make writing blocks of code as simple as pressing a few keys on the keyboard; templates for creating new projects and project items; or add-ins and macros, which are small programs that customize the Visual Studio integrated development environment (IDE). When you receive content from another user, installing that content for use is as easy as working through a wizard and selecting which items to install. After you receive a package of content enclosed in a .vsi file, you can open the file by either double-clicking the file if it is stored on disk, or, if you are downloading the file directly from the Internet, you can click the Open button on the download dialog box from your Web browser. Figure 4-1 shows the first page of the Content Installer showing a .vsi file that contains a control for the Toolbox.

The Content Installer with a content file open and ready to install

Figure 4-1. The Content Installer with a content file open and ready to install

Controls do not have any configuration information to display to the user, so clicking the Next button will take you directly to the final page of the wizard. Some content elements, such as code snippets, do allow you to configure how they are to be installed, so the wizard includes a configuration page for them. After you have configured the installation, if applicable, you are ready to begin installing the content. The page indicating that content is ready to install is shown in Figure 4-2.

Ready-to-install content

Figure 4-2. Ready-to-install content

Click the Finish button to install the files. During installation, there might be a conflict between a file to be installed and an existing file of the same name. If that happens, you are given the chance to overwrite the file, skip installing the file, or rename the file to a suggested file name. When installation is completed successfully or with errors, you will be presented with a dialog box like the one in Figure 4-3.

The content Installer after items have been installed

Figure 4-3. The content Installer after items have been installed

After installing an item, a hyperlink will appear next to the item that was successfully or unsuccessfully installed. You can click this link to see the Install status report, which consists of a list of files installed for that content item as well as any status message returned. If installation of any item failed, you can use the status information to correct the problems, and then return to the wizard to click the Finish button again to try to reinstall the unsuccessfully installed items.

Security

As with any file that you place on your computer, you should first make sure that you trust the files and know what you are installing; items installed by means of the Content Installer are no different. The first page of the wizard has a hyperlink labeled "View files in Windows Explorer…." Clicking this hyperlink will open a Microsoft Windows® Explorer window displaying the files that are within the .vsi file, allowing you to view the files and inspect the contents of those files to verify that they will do no harm. Also on the first page of the Content Installer is an area that displays information about the .vsi file. If the .vsi file to install was signed with an Authenticode® signature, the name of the company that created the content, as well as a link to more information about that content, will appear. If the file has not been signed, then this information will not be available.

Note

If you click the Review button to view the files to be installed, you should be careful not to delete any of the files from within the Windows Explorer window. If you do delete any files, installing the content might fail.

Creating Downloadable Content

You can also easily create your own content to share with others. A .vsi file contains one or more files that are compressed using the .zip file format with the extension renamed from .zip to .vsi. In addition to the installable content files, the .vsi file contains a manifest file with the .vscontent extension. The .vscontent file uses the XML format and specifies not only which files to install, but also how they are to be installed. When the Content Installer opens the .vsi file, it first decompresses the file and searches for the first available file with the .vscontent extension. If such a file is present, it is opened, the XML within it is read, and then the Content Installer wizard appears, ready to start installing.

The VSContent File Format

The .vscontent file schema was designed to be very simple and easy to create. The XML file for content starts with the tag <VSContent>. This tag has one attribute that determines the XSD schema used not only for ensuring that the XML is correct, but also by the Visual Studio XML editor to give statement completion information when the file is opened for editing. The contents of the most basic .vscontent file are as follows:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
</VSContent>

Next we need to specify the items that will be installed. Each content item is enclosed within a tag named <Content> and lists the name and description of the item to display to the user with the <DisplayName> and <Description> tags, respectively. These tags contain any text that you want to display to the user identifying the content in the first page of the Content Installer—but keep it short because there is not much space available for display. To identify the type of content to the Content Installer, the <FileContentType> and <ContentVersion> tags are used. <FileContentType> can contain one of the following values: Toolbox Control, Macro Project, Addin, VSTemplate, or Code Snippet. <ContentVersion> specifies the version of the content to be installed and is generally the string "1.0." The last required tag (or tags, because you can specify more than one of them) is the <FileName> tag. This tag lists the files to install for the content item. The file names listed in <FileName> tags are relative to the location of the .vscontent file. If a file to install (let's suppose the file name is File. xyz) is zipped from the same directory as the .vscontent file, the file name is File.xyz; if the file is stored in a subdirectory of the folder containing the .vscontent file, the file name is SubDirectoryNameFile.xyz.

When all of these tags are put together with a list of the files that are to be installed, you have a .vscontent file that can be loaded by the Content Installer to install on your computer or somebody else's. An example of a .vscontent file, which can be used to install controls onto the Toolbox, is given here:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
 <Content>
  <FileName>CompanyNameControl.dll</FileName>
  <DisplayName>Test control</DisplayName>
  <Description>A control to test with</Description>
  <FileContentType>Toolbox Control</FileContentType>
  <ContentVersion>1.0</ContentVersion>
 </Content>
</VSContent>

Figure 4-1 showed a screenshot of the Content Installer with this .vscontent file open. Here, the Control.dll file is stored in a folder named CompanyName, whereas the .vscontent file is assumed to be in the same folder containing the folder CompanyName. You can combine multiple Content items within a .vscontent file, allowing you to distribute multiple items at one time or to group items that are related and dependent on one another.

Installing Templates and Starter Kits

Creating new projects or new files that go within projects is one of the most common, and yet more complicated, tasks that a developer may perform. If you needed to create a file with a simple class within that file, the task isn't overly complex—you simply add a new blank file to the project and add the class. However, imagine having to create a Windows form from scratch without a starting file generated by a wizard, and you can see how hard it would be to create the WinForm. Templates and starter kits are packages of files that you can use to quickly generate a new file or project. We will discuss how to generate these packages later in this chapter, but for now, let us examine the Content Installer XML necessary to install a template or starter kit.

A .vscontent file used to install a template file looks very much like the sample .vscontent file that we saw earlier. The <FileContentType> name for a template installer is VSTemplate. This content type installs files only with the .zip extension. Three different attributes are required to specify a content item for a template. The .vscontent XML schema allows attributes to be attached to a content item to provide custom data to the installer, directing the installer on how the item should be installed. Attributes use a name and value pair not only to name the data, but also to contain the data. The three attributes that a VSTemplate content item requires are TemplateType, ProjectType, and ProjectSubType. The value for a TemplateType attribute can be one of Project or ProjectItem, and it declares where the template can be used. If this value is Project, the template is copied into a location so that you can create the project through the New Project dialog box. If this value is ProjectItem, the file is copied into a location so that you can right-click in the Solution Explorer on a project or a folder within a project, choose New Item from the Add menu, and choose the template to add to an existing project. The second attribute necessary within a .vscontent file for a VSTemplate is the ProjectType attribute. This attribute specifies the programming language used for the template. Suppose you have a Microsoft Visual C#® project open; you would not want your Microsoft Visual Basic® template to appear within the Add New Item dialog box. And if you have the New Project dialog box open with the Visual Basic project type selected, you do not want your Microsoft Visual J#® project to appear. With the ProjectType attribute, you can create a filter that determines in which dialog box the template will appear. The possible values for this attribute are Visual C#, Visual Basic, Visual J#, and Web. (Although Web is not a programming language, it is a type of project.) The final attribute is the ProjectSubType attribute, which gives either the platform of the template if ProjectType is Visual C#, Visual J#, or Visual Basic; or the programming language of the project if ProjectType is Web. If the ProjectType is Web, the possible values for ProjectSubType are Visual C#, Visual Basic, or Visual J#. If the ProjectType is not Web, possible values for ProjectSubType are Windows, Smart Device, Database, or starter kits.

When the Content Installer copies the template file to disk it examines the values of these three attributes and uses them to construct a destination path where the template is placed. The installer begins with the Visual Studio user data directory, which is C:Documents and SettingsusernameMy DocumentsVisual Studio 2005, and then the installer appends Templates to this path because this is the storage location for all installed templates. Next the installer looks at the TemplateType attribute. If it is Project, it adds ProjectTemplates to the path, or, if the attribute value is ProjectItem, it adds ItemTemplates to the path. Next the value of ProjectType is added, and finally the ProjectSubType attribute value is added.

The following .vscontent file defines a Visual C# console application template named MyConsoleApplication.zip for Windows. Based on the values given in the XML, the .zip file for the template is installed into the location C:Documents and SettingsusernameMy DocumentsVisual Studio 2005TemplatesProjectTemplatesVisual C#Windows.

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
  <Content>
    <FileName>MyConsoleApplication.zip</FileName>
    <DisplayName>My Console Application</DisplayName>
    <FileContentType>VSTemplate</FileContentType>
     <ContentVersion>1.0</ContentVersion>
     <Attributes>
       <Attribute name="ProjectType" value="Visual C#"/>
       <Attribute name="ProjectSubType" value="Windows"/>
       <Attribute name="TemplateType" value="Project"/>
     </Attributes>

 </Content>
</VSContent>

When the Content Installer has installed this .vscontent file, you will be able to create a console application within the new project dialog box. The template will appear under the node Visual C#Windows in the tree on the left side of the dialog box and in the My Templates section of the list on the right side of the dialog box.

Installing Controls to the Toolbox

Controls are DLLs that are inserted into the Toolbox, which the user can drag and drop onto a WinForm or ASPX Web page designer to quickly create the user interface for an application. These pre-built components save users a lot of time by allowing them to quickly design a program without needing to write lots of code. You can use the Content Installer to install controls in the Toolbox. To install a control, a DLL containing the control must be copied into a place where it can be found, and then Visual Studio must be started so that it can load and then add the control to the Toolbox.

Placing the control into a place where Visual Studio can find it is easy—at least with the Content Installer. When the Content Installer installs a control for the Toolbox, the control is placed into the directory My DocumentsVisual Studio 2005ControlsCompanyName, where CompanyName is the name of your company, group, or any other unique name that you want to use. When the Content Installer installs controls, it will use this CompanyName to create a new tab in the Toolbox (if one with that name does not already exist), and then the controls contained within the DLLs in this directory will be added to that Toolbox tab. This procedure allows you to create a tab in the Toolbox that distinguishes your controls from the default ones installed by Visual Studio or other companies.

Note

You can also place controls into the directory My DocumentsVisual Studio 2005Controls. If you place a DLL here, the controls the DLL contain will be placed on a tab named My Controls.

The second step to install the control is to invoke Visual Studio, Visual Basic Express, Visual C# Express, and so on to allow those programs to install the control. Installing controls can be costly in terms of startup performance, so the control installer will start each of these applications to place the controls on the Toolbox with the command programname /Command Tools.InstallCommunityControls where programname is the full path to the executable for the various editions of Visual Studio. The command Tools. InstallCommunityControls invokes code within Visual Studio to search for, and then install the controls in the Toolbox. (We will more fully discuss commands in Chapter 7.) You can also use the command Tools.InstallCommunityControls outside of the control installer to reinitialize any controls you might have installed but that do not appear in the Toolbox. Suppose you install Visual C# Express, and later you decide to upgrade to Visual Studio; do you need to reinstall all the controls you might have downloaded? No. Simply start Visual Studio, open the Command Window tool window, type Tools. InstallCommunityControls, and then press Enter. Visual Studio will look for any controls that have previously been installed with the Content Installer and place them on the Toolbox.

When we were discussing the .vscontent file format, we saw a sample .vscontent file containing the information necessary to install a control. The name of the content type for a control is Toolbox Control, which should be placed within the <FileContentType> tag. In this example, a tab named CompanyName will be created within the Toolbox, and any controls in the file Control.dll will be added to that tab.

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
 <Content>
  <FileName>CompanyNameControl.dll</FileName>
  <DisplayName>Test control</DisplayName>
  <Description>A control to test with</Description>
  <FileContentType>Toolbox Control</FileContentType>
  <ContentVersion>1.0</ContentVersion>
 </Content>
</VSContent>

This also means that you need to create a folder named CompanyName in the folder containing the .vscontent file, and you would place the Control.dll file within the CompanyName folder.

Installing Code Snippets

Code Snippets are a source code editor productivity enhancer. If you are editing a C# file and you need to create, for example, a for loop, you could manually enter the text for that for loop, or you could use a snippet to create the loop for you. To use a snippet, first type the word for; this will cause the IntelliSense® window to appear. If you were to press the Tab key twice with the for keyword selected in the IntelliSense window, Visual Studio will automatically generate the basic structure of a for loop, as shown in Figure 4-4.

The basic structure of a for loop, as generated from a snippet

Figure 4-4. The basic structure of a for loop, as generated from a snippet

The control variable is named i, but you might not always want this variable to have that name. To change this variable name, just type in the new name. When you are finished typing, press the Tab key, and the variable name i, as well as all uses of the variable i within the snippet code, will be renamed, as shown in Figure 4-5 where the variable name is changed to j.

The basic for loop, with the control variable renamed to j

Figure 4-5. The basic for loop, with the control variable renamed to j

The variable name length is highlighted next, and it, too, can be edited. After typing the number 10 (creating a loop over the numbers 0 to 9, inclusive), the for loop appears like the one in Figure 4-6.

The basic for loop, with the upper limit set to 10

Figure 4-6. The basic for loop, with the upper limit set to 10

Although this is not a lot of text to type, imagine typing this bit of code over and over, just as you do during your development work, and you can see the amount of keystrokes you will save. Snippets are stored in an XML format and can be installed in the same manner as other content items.

A .snippet file can contain many different snippets, but each file can contain snippets for only one particular programming language. The Visual Basic, Visual C #, Visual J #, and XML editors currently support snippets. When installing a snippet, the <Content> tag must contain an <Attribute> tag indicating which programming language the snippet supports. The name of this attribute is lang, and the possible values are vb, csharp, jsharp, and xml. The following .vscontent file contains two items to install: a Visual Basic and a Visual C# snippet:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
 <Content>
  <FileName>VBSnippets.Snippet</FileName>
  <DisplayName>Visual Basic snippets</DisplayName>
  <Description>Visual Basic snippets to install.</Description>
  <FileContentType>Snippet</FileContentType>
  <ContentVersion>1.0</ContentVersion>
  <Attributes>
   <Attribute name="lang" value="vb"></Attribute>
  </Attributes>
 </Content>
 <Content>
  <FileName>CSSnippet.Snippet</FileName>
  <DisplayName>C# snippets</DisplayName>
  <Description>C# snippets to install. </Description>
  <FileContentType>Snippet</FileContentType>
  <ContentVersion>1.0</ContentVersion>
  <Attributes>
   <Attribute name="lang" value="csharp"></Attribute>
  </Attributes>
 </Content>
</VSContent>

Installing Add-ins and Macros

In Chapters Chapter 5 and Chapter 6, we'll see how to create macros and add-ins, but while we are discussing installing files, we can show you how to install these types of files. An add-in (at least an add-in written using a .NET Framework programming language) consists of at least one file, and possibly more. The required file is a file with the .addin extension. This file describes the add-in to Visual Studio and provides information such as the name and description of the add-in. The second possible file is a .dll that contains the code for the add-in. The content type for an add-in is Addin, and this is one of the few content types that will accept more than one <FileName> tag to specify more than one file to install. This content type does not need any <Attribute> tags. Here is a simple .vscontent file used to install an add-in:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
 <Content>
  <FileName>MyAddin.Addin</FileName>
  <FileName>MyAddin.dll</FileName>
  <DisplayName>An add-in to install.</DisplayName>
  <Description>This is a test add-in.</Description>
  <FileContentType>Addin</FileContentType>
  <ContentVersion>1.0</ContentVersion>
 </Content>
</VSContent>

A macro, much like an add-in, is a file that contains program code that the user can run to customize and modify data within Visual Studio. The difference between a macro and an add-in is that the user can edit and modify the macro code, and there is an editing and debugging environment devoted to just this type of code. The XML used to install a macro has the <FileContentType> of Macro Project, and the only file types that can be installed by the macro installer are files that end with a .vsmacros extension:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
 <Content>
  <FileName>Samples.vsmacros</FileName>
  <DisplayName>A macro to install.</DisplayName>
  <Description>A macro project to install.</Description>
  <FileContentType>Macro Project</FileContentType>
  <ContentVersion>1.0</ContentVersion>
 </Content>
</VSContent>

Add-in and macro content installers are a bit different than the other installer types in that these content types will install only for Visual Studio. The Express versions of Visual Studio do not support macros or add-ins, so although templates, snippets, and controls can be installed for all those versions of Visual Studio, add-ins and macros will be available only from within the Enterprise, Pro, or other premium versions of Visual Studio.

Zipping

After you have created the content you want to distribute, the next step is to package the file for distribution. You do this by zipping up the files—you can use either the zip functionality built into Windows XP or any other zipping tool to do this—then rename the file to have the extension .vsi rather than .zip. If you are using Windows Explorer to zip the files, simply select the files to zip, right-click each file, and from the shortcut menu, choose Send To and then Compressed (zipped) Folder from the submenu. If you are using another zipping tool, such as WinZip, you need to make sure that you do not use some of the more advanced options these zip utilities provide. For example, you shouldn't use encryption or a nonstandard compression algorithm, and you shouldn't save files with full path information to zip your files. If you do, then extraction could fail within the Content Installer when the file is opened because the zip utility that the Content Installer uses supports only basic zipping functionality.

Note

The extension .vsi has its roots in the file extension .msi, which stands for Microsoft installer. VSI is short for Visual Studio installer. The two technologies, however, should not be confused with one another. The Content Installer cannot be used to install .msi files, and the Microsoft installer cannot be used to install .vsi files.

After you have renamed the .zip file to have the extension .vsi, you can either e-mail or post the file to a Web site so that friends or colleagues can install the content that you have created. But before you make the .vsi file available to others for use, you will want to test the file to make sure it works. You could just double-click the .vsi file from within Windows Explorer, but if you find a bug in your content, you would need to correct the problem, then re-zip and rename the extension, and repeat. Rather than taking the time to go through the zip-rename process, you can just double-click the .vscontent file. Both the .vsi and .vscontent extensions are associated with the Content Installer. You can also use the Content Installer to browse to a .vsi, .zip, or .vscontent file. From the Run dialog box in the Windows Start menu, navigate to C:Program FilesCommon FilesMicrosoft SharedMSEnv and run the command vscontentinstaller.exe /browse. This will display the standard Windows Open File dialog box, where you can browse to any file you need to open and install the content the file contains.

Signing Your Content

Some people might be wary of installing content on their computers from unknown sources, but if content has been identified as coming from a trusted source, they might not be as concerned about installing that content. You can purchase Authenticode certificates from certificate authorities such as Thawte.com or VeriSign.com. But there is a problem: you cannot sign .zip files (or .zip files renamed to have the .vsi extension)—you can sign only .dll, .exe, or .cab files. To enable you to sign your .vsi files, Visual Studio has a utility named MakeZipExe, which will take a .zip file and create a self-extracting .exe that you can then sign by using the SignCode tool, a utility that is part of the .NET Frameworks software development kit (SDK). To create a .vsi file that you can sign, first generate your .zip file by using your favorite zip utility just as you would for unsigned content, but do not rename the file extension. Then, from the command line, run the following command:

MakeZipExe -zipfile:"path to your .zip file"

This command will create an .exe file in the same location as the zip with the same file name except for the extension. Next you will need an Authenticode certificate and the signcode.exe utility. Because the command line arguments to the signcode.exe tool can vary between the different certificate authorities, you should search the Web site of your certificate issuer for assistance in using the Signcode.exe tool. After you have signed the .exe file, you can rename the file to have the .vsi extension. When the Content Installer opens the .vsi file, it will examine the file to determine if the file is an unsigned .zip file or a signed .exe file and extract the content appropriately.

When signing code, you should keep two things in mind. First, certificates are not cheap. Certificates can cost from a few hundred dollars to thousands of dollars depending on the certificate, so you will need to decide if signing your code is worth the cost. If your target audience is consumers buying controls from your Web site, you probably will want to sign your .vsi. If you are only sharing your files with friends, you probably will not need to sign. The second thing about signing is that the Content Installer knows how to read self-extracting .exe files generated by using only the MakeZipExe tool; other tools, such as WinZip, do not produce .exe files compatible with the Content Installer.

Implementing Your Own Downloadable Types

As we have seen, the Content Installer offers different content types that you can install: templates, add-ins, macros, code snippets, and controls. However, you might have an idea for another kind of content that could help users better collaborate with one another. The Content Installer is extensible, allowing you to create your own content-type installers. In this section, we'll show you how you can extend the Content Installer with your own installer types.

Creating the Project

A custom installer is simply a .NET class library DLL that the Content Installer will load and run when a .vscontent file has a <FileContentType> tag value that corresponds to the name you give to your own custom installer. The easiest way to get started creating your content installer is to use a starter kit. Accompanying the samples for this book are two starter kits named Content Installer UI-Less Page and Content Installer UI Page. Both appear in the Visual C# section of the New Project dialog box. The first of these starter kits is used to create a project that does not have any configuration options in the Content Installer wizard. The second starter kit is used to create a project that displays user interface (UI) for configuration. When you create a project using the starter kit, the name of the project as entered in the New Project dialog box will become both the name of the installer and the text that you use within the <FileContentType> tag in a .vscontent file. The following sections describe how the code generated by these starter kits work, and how you can write code to interact with the Content Installer.

Interface Implementation

An installer is simply a class library that implements the interface IImportCommunityContent. This and other interfaces that you will use to program the Content Installer are defined in the assembly Microsoft.VisualStudio.VSContentInstaller.dll. The IImportCommunityContent interface has the following signature:

public interface IImportCommunityContent
{
    bool AddContentItem(IContentItem[] contentItems, IContentInstallerSite site);
    string Import(IContentItem contentItem);
    bool SupportsImportUI { get; }
    IImportPageData[] GetImportPages();
    void UpdateContentItemInstallStatus(IContentItem[] contentItems);
}

When the Content Installer loads your custom installer, the first method called is the IImportCommunityContent.AddContentItem method. When this method is called, all the data necessary for your installer to know which files and how they are to be installed is contained within the IContentItem array. One IContentItem object is passed to the AddContentItem method for each <Content> tag in the .vscontent file for the <FileContentType> that matches the type your installer installs. If you were to open a .vscontent file with two Addin content types and one VSTemplate content type, and if you were implementing the add-in installer, you would be handed two IContentItem objects, one for each Addin content item in the .vscontent file. IContentItem has a set of methods and properties for getting to data for the content in the .vscontent file. The properties ContentVersion, Description, DisplayName, and FileContentType on the IContentItem interface each map to the tag of the same name in the .vscontent file. The property IContentItem.AttributePairs returns an object of type System.Collections.Specialized.StringDictionary with one element for each Attribute tag within the .vscontent file. Your installer specifies any attributes that the installer requires, as well as the names and values of those attributes. IContentItem also exposes two methods, GetFileNames and GetRootFileNames. When the Content Installer reads the <Content> section of a .vscontent file, it gathers all the <FileName> tags together and stores them for later use. When you call the GetRootFileNames method, the list of file names as given in the .vscontent file is returned. GetFileNames also returns the list of file names, except that these file names are prepended with the directory in which the files are placed and can be used as the source of a copy operation. Assuming that you have a variable named destinationPath containing the path in which you are copying items, you can use code such as this to copy the files into the correct location:

foreach(VSContentInstaller.ContentItem
  contentItem in contentItems)
{
  string []sourceFileNames =
    contentItem.GetFileNames();
  string []rootFileNames =
    contentItem.GetRootFileNames();
  for (int i = 0; i < sourceFileNames.Length ; i++)
  {
    string combinedDirectory = Path.Combine(
      destinationPath, rootFileNames);
    Directory.CreateDirectory(
      Path.GetDirectoryName(combinedDirectory));
    File.Copy(sourceFileNames[i], combinedDirectory);
  }
}

The reason for these two methods is quite simple. Suppose the path given in a <FileName> tag contains a directory, MySubDirectoryFile.ext, for example. If you were given only the source directory to copy the file from, you would not know how to re-create the directory MySubDirectory in the destination location. GetRootFileNames returns the path exactly as specified in the XML, and you can use this to recreate the necessary destination path.

The second parameter is an IContentInstallerSite object, which is an object implemented by the Content Installer that you call into to copy files, set status, and perform other operations. We will see the methods on this interface shortly.

After calling your AddContentItem method, the Content Installer will then call the SupportsImportUI property. Not all installers need to display a user interface to the user. In fact, the only installer that does show UI among the installers that ship with Visual Studio is the snippet installer. If your installer does need to display UI, then you should return false from this property, and if you do need to display UI, then return true. If you do return true, the Content Installer will call the GetImportPages method next. This method creates an array of class objects implementing the IImportPageData interface containing one element for each page your installer needs to display in the UI; it then returns this array. The IImportPageData interface has two different values you can set. HeadlineText is the text displayed in the banner at the top of the wizard when your UI page is active. Page is set to an instance of a class that derives from the UserControl class, and it is to be displayed when your page becomes active when the user is traversing the steps in the wizard. The UserControl that you create for your UI should have a size of 470 x 305 and can contain any UI elements that you want.

One thing to keep in mind is that there is not a 1:1 correspondence between the number of IContentItem objects passed to the AddContentItem method and the number of elements returned from the GetImportPages method. For example, a .vscontent file may contain 10 different snippet content items to install, so 10 different IContentItem objects are passed to the AddContentItem method. The code snippet installer takes these IContentItem objects, sorts them based on the programming language the snippet is written in, and then displays one page for each of the languages supported. If the .vscontent file contains 10 C# snippets, one IImportPageData object is returned. If the .vscontent file contains one C# snippet, one XML snippet, and eight Visual Basic snippets, three IImportPageData objects are returned.

After calling these methods and properties, the Content Installer is ready to show the first page of the content installer UI, such as that displayed in Figure 4-1 (on page 56). The first page displays a list of items, one for each IContentItem object, both handled by your installer and by others, that are to be installed. There is also a check box next to each content item, all selected by default. Your installer's UpdateContentItemInstallStatus method is called as the user selects and clears items in the Content Installer UI. This method call provides you with an array of IContentItem objects. Only content items that are to be installed will be passed, so if the user decides not to install one or more content items, you can add or remove them from the list of items to display within your user interface.

After the user has reached the last page of the Content Installer wizard, the Next button changes to the Finish button. When the user clicks the Finish button, the Content Installer starts informing each installer that it should install content with a call to the Import method. For each item that was selected on the first page of the Content Installer, the installer's Import method is called with the IContentItem that is to be installed. This is where you call the code that we saw earlier to copy the files into the correct location. An error may occur in any setup operation. If any exception is unhandled by your installer, or if you throw an exception indicating that you detected an error, the Content Installer will catch that exception and indicate to the user that an error occurred. If an error did not occur, your Import method needs to return a string indicating that installation was successful.

The Site Interface

IContentInstallerSite is an interface exposed by the Content Installer to provide you a way to control the UI of the Content Installer and make development of your installer easier. The first method of this interface is the CopyFile method. Earlier we showed you some code to copy files to disk by using the System.IO.File.Copy method. But if you use the CopyFile method, the Content Installer will store a list of the files being installed for a content item, and the Content Installer will use this list for display within the Install status window. In addition, the CopyFile method handles problems such as when the file to copy already exists on disk. CopyFile accepts four parameters: the source file path, the destination file path, a value of type DuplicateFileCase, and an out parameter into which the path the file was placed is copied. The DuplicateFileCase parameter allows you to control how the file is copied if the destination file exists. If this value is anything other than DuplicateFileCase.None, and the destination file exists, a dialog box is given to the user allowing him to overwrite the existing file, to skip copying the file to disk, or to use a new file name suggested by the Content Installer. The values within the DuplicateFileCase enumeration will enable or disable the corresponding UI options within this dialog box.

When calling CopyFile, you should specify only the EnableRename or EnableAll enumerated values to the allowRenameOfFile if the file name is not referenced by another file. For example, if you are copying the files of a project, and the name SomeFile.cs exists on disk, and you are also copying a project file (SomeProject.csproj) that references the file SomeFile. cs, ensure that you do not pass EnableRename or EnableAll to allowRenameOfFile. Otherwise, when the user opens the project file, the file SomeFile.cs will try to load the original SomeFile.cs, not the renamed version.

The method IContentInstallerSite.EnableNextButton takes a Boolean value that allows you to enable or disable the Next button within the wizard. If you have UI displayed for your installer and the user enters data that is invalid, which should prevent him or her from navigating to the next page, you can call this method specifying false to prevent the user from going to the next page. After the user enters correct data, you can call EnableNextButton(true) to enable the button and to allow the user to continue.

GetApplicationData allows you to retrieve information specific to the application into which you are installing the content item. This method takes two values and returns an array of interfaces containing data that you can use to install your program. The first parameter is the content type that you are trying to install; the second is the version of the content type (both of which are stored in the .vscontent file). Upon return from GetApplicationData, an array of IApplicationHostData contains one element for each application (such as Visual Studio, Visual Basic Express, Visual J# Express, and so on) that the content can be installed for. The IApplicationHostData interface has six properties that contain information from the registry, and these properties are RegistryRoot, ApplicationName, UserDataFolder, ApplicationPath, ProgId, and ApplicationImage. Each of these properties is valid for every edition of Visual Studio except the ProgId property. Visual Studio is the only version that supports an automation object model, so for any of the Express versions of Visual Studio, this property will return an empty string. Table 4-1 lists each of these properties and example values returned from these properties.

Table 4-1. IApplicationData properties

IApplicationData property

Example Value

Property Use

RegistryRoot

SoftwareMicrosoftVisualStudio8.0

This is the location where data for the application is stored in the registry. Prepend the registry hive key, either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER, as appropriate.

ApplicationName

Microsoft Visual Studio 2005

Display text for the name of the application.

UserDataFolder

%USERPROFILE%My DocumentsVisual Studio 2005

Location where user files are stored for the application.

ApplicationPath

"c:Program FilesMicrosoft Visual Studio 8Common7IDEdevenv.exe"

The path to the application.

ProgId

VisualStudio.DTE.8.0

COM ProgID for the application. This is not valid for the Express versions of Visual Studio.

ExpressVersion

False

Returns true if the program is an express version (such as Visual Basic Express), or false if the program is Visual Studio.

ApplicationImage

An image for the application. If the content item does not supply an image, the default Visual Studio logo (the "Infinity" icon) is used.

The last important method on the IContentInstallerSite is the ShouldContinue method. The Content Installer is multithreaded, allowing the user to cancel installation even while an installer is installing data to the computer. Periodically while installing, such as just before you call CopyFile, your installer should call this method to determine if it should continue. If at any time ShouldContinue returns false, you should immediately return from your implementation of Import.

Registration

After you have implemented the code for your installer, the final step is to let the Content Installer know how to find your code with a set of registry keys and values. All registry information for the Content Installer is under the key HKEY_LOCAL_MACHINE SOFTWAREMicrosoftMSEnvCommunityContentContentTypes. Underneath this key, you create a key with the name of your installer, and you create two values named Assembly and ClassName. Assembly gives either the path or the strong name of the assembly implementing your installer, and ClassName is the full name, including namespace and class name, of the class implementing IImportCommunityContent. Underneath that is a key with the name ContentHosts and then a key with the version number of your installer, usually named 1.0. Next is a list of keys with the name of the editions of Visual Studio, which can be Visual Studio 2005, Visual Basic Express 2005, Microsoft Visual Web Developer™ Express 2005, Visual J# Express 2005, Visual C# Express 2005, and Microsoft Visual C++® Express 2005. If your installer does not support one of these editions of Visual Studio, you can omit that key. For example, if you are installing a set of C++ header files, you should generate keys only for Visual Studio 2005 and Visual C++ Express 2005. When you call IContentInstallerSite.GetApplicationData, one IApplicationHostData entry is returned for each version of Visual Studio that is registered. Underneath each of these keys is a set of values, each having the name as given in the left column of Table 4-1, and a value similar to that as in the center column of Table 4-1. Following along with this description of keys and values is probably not easy, so an example .reg file with all the registry values necessary to define an installer named MyContentType is given in Example 4-1. This registry script creates all the necessary entries for Visual Studio and the Express versions of Visual Studio. If you were to use the starter kits to create a content installer, all this data would be pre-populated. All you would need to do is merge the .reg file in the system registry by double-clicking the file in Windows Explorer, and Registry Editor will create all the necessary registry settings for you. Notice that there are some values that contain text, such as %USERPROFILE%. When the Content Installer reads these values from the system registry, it will expand all environment variables into their set values.

Example 4-1. An example registry script to register a content installer

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentType]
"ClassName"="MyContentType.MyContentType"
"Assembly"="C:Documents and SettingsCRAIGSMy Documents
  Visual StudioProjectsMyContentTypeMyContentType
  bindebugMyContentType.dll"

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts]

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0]

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0Visual Studio 2005]
"ApplicationName"="Microsoft Visual Studio 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\
  Common7\IDE\devenv.exe"
"RegistryRoot"="Software\Microsoft\VisualStudio\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"
"ProgId"="VisualStudio.DTE.8.0"

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0Visual Basic Express 2005]
"ApplicationName"="Microsoft Visual Basic Express 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\
vbexpress.exe"
"RegistryRoot"="Software\Microsoft\VBExpress\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"
"ExpressVersion"=dword:00000001

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0
  Visual C# Express 2005]
"ApplicationName"="Microsoft Visual C# Express 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\
  Common7\IDE\csexpress.exe"
"RegistryRoot"="Software\Microsoft\VCSExpress\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"

"ExpressVersion"=dword:00000001

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContentContentTypes
MyContentTypeContentHosts1.0Visual C++ Express 2005]
"ApplicationName"="Microsoft Visual C++ Express 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\
vcexpress.exe"
"RegistryRoot"="Software\Microsoft\VCExpress\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"

"ExpressVersion"=dword:00000001

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0
  Visual J# Express 2005]
"ApplicationName"="Microsoft Visual J# Express 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\
  Common7\IDE\vjsexpress.exe"
"RegistryRoot"="Software\Microsoft\VJSExpress\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"

"ExpressVersion"=dword:00000001

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSEnvCommunityContent
  ContentTypesMyContentTypeContentHosts1.0
  Visual Web Developer Express 2005]
"ApplicationName"="Microsoft Visual Web Developer Express 2005"
"ApplicationPath"="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\
vwdexpress.exe"
"RegistryRoot"="Software\Microsoft\VWDExpress\8.0"
"UserDataFolder"="%USERPROFILE%\My Documents\Visual Studio 2005"

"ExpressVersion"=dword:00000001

An Example—Samples Installer

Now that you know how to implement an installer, you can try out a very simple installer, the samples installer. Samples are used extensively by programmers for tips on how to more effectively use an API. Available in this book's companion content, this samples installer allows you to package samples and redistribute them to another user through a .vsi file. The samples installer is so simple that you already saw the majority of the code when we described the IContentInstallerSite.CopyFile method. The only additional code that it uses is some security checks to ensure that the files are installed in places that will not harm the user's computer.

Security Attributes

Unless they are written with security in mind, installers could be used to place files onto disk that probably should not be installed. As an extra layer of protection, there are two attributes that you can place on your content installer class to restrict which content is passed to it. This minimizes the impact of rogue content that a user could download and blocks installers that spoof existing installers. The first of these attributes is the ContentInstallerContentTypeRestrictionAttribute attribute, which takes as a parameter the name of a content type. When the Content Installer loads an installer, the Content Installer will look for this attribute and, if found, will compare the content type to be installed with the value passed to this attribute. If they match, the content will be permitted to be installed; otherwise, the content will not be allowed to install, and a security exception will be generated. The string passed to ContentInstallerContentTypeRestrictionAttribute should match the name that you give to your installer within the system registry.

The second attribute that you can place on your content installer class is ContentInstallerSupportedFileSecurityAttribute. This attribute allows you to filter out the types of files that your installer can install. The installer for macro projects should be allowed to install only macro projects (files with the extension .vsmacros), not executable files (files with the extension .exe), so the installer for macros uses this attribute declaration: ContentInstallerSupportedFileSecurity(".vsmacros"). There is the possibility that a macro could reference an external DLL, but to minimize the possibility of bad code being installed, the macro project installer allows only .vsmacros files to be installed. You should be equally security-minded when creating your installer. Allow installation of only the minimum list of file types, not everything that could possibly be installed. If your installer needs to install multiple file types, you can specify the ContentInstallerSupportedFileSecurityAttribute attribute multiple times. This is a portion of the class used to install add-in file types, and it specifies that it can install .addin and .dll files:

[ContentInstallerContentTypeRestriction("Addin")]
[ContentInstallerSupportedFileSecurity(".addin")]
[ContentInstallerSupportedFileSecurity(".dll")]
class AddinInstallerPage : IImportCommunityContent
{
    ...
}

Your installer also needs to make sure that the files that are being installed are installing to the location you intended. Suppose you wanted to install your content into the folder My DocumentsVisual Studio 2005MyContentType; the installer places .exe files on disk, and the .vscontent file gives a destination path of Windows otepad.exe. When your installer runs this .vscontent file, it will try to overwrite the Notepad program with code that could be harmful to the computer. The next time the user tries to run the Notepad program, that malicious code will run. Therefore, you should check where you are installing content before you call IContentInstallerSite.CopyFile. The CopyFile method does not check this path for you before copying the file because there may be content installers that have a valid reason to install content into a path such as the Windows directory.

Creating VSTemplates

Probably the most common type of content that you will be sharing with others is templates. In earlier versions of Visual Studio, if you wanted to create a project or project item that the user could add to the New Project or Add New Item dialog boxes for cloning, you had to write a wizard. Writing a wizard to generate even a simple project was not easy, so to make this process easier, Visual Studio 2005 offers a new wizard technology that makes creating reusable projects and project items easier than ever. This technology, VSTemplates, enables you to quickly create a new project or project item to import into a solution or project by putting together an XML file that describes the files that make up your project or project item. When the user runs your template through the New Project or Add New Item dialog boxes, Visual Studio will start the template wizard, which then reads and processes the XML file, creating the project or project item for you. The VSTemplate wizard turns generating projects and project items into a data-driven process. Each item that appears in the New Project and Add New Item dialog boxes, when added to a solution or an existing project, uses the VSTemplate wizard to generate the project and project items. And of course, you can create your own template files to appear within these dialog boxes.

Using the Export Template Wizard

The easiest way to get started creating a VSTemplate is to use the Export Template Wizard. This wizard will examine a project or project item that is loaded into a solution or project, and it will generate a VSTemplate that you can use within the Add New Item or New Project dialog boxes to create new project items or projects. You can also package these files into a .vsi file for easy distribution to other users. This wizard is available from the File menu, and it will quickly generate a .zip file containing a .vstemplate file and the files necessary to recreate an item within a project or a project and its contents.

To use the wizard, you must first have one or more Visual Basic, Visual C#, Visual J#, or Web projects opened in a solution. After you start the wizard, you will see a dialog box like that shown in Figure 4-7.

The first step of the Export Template Wizard

Figure 4-7. The first step of the Export Template Wizard

This dialog box gives you two different options. The first, Project Template, will generate a template for a project. The Item Template option, if selected, will enable you to export one single file from within a project. After you choose to export either a project or project item, you then select which project to export, or the project containing the item to export. This is done from within the first drop-down box, which gives a list of all projects that are within the currently open solution. The final drop-down box on this page is only visible when you have selected a Web project to export and allows you to select the programming language for the Web project.

If you select to export a project item and then click the Next button, the dialog box shown in Figure 4-8 will appear.

Here, you can navigate through the tree to find an item to turn into a template. If you were to select one item and then select another, the check box on the first item will be cleared. Some items, such as forms or controls, can contain multiple items. For example, a Windows form could have a Form1.cs file, a Form1.designer.cs file, and a Form1.resx file. The dependent items (Form1.designer.cs and Form1.resx) will not appear in this step of the wizard, but if you select the Form1.cs file, even though you do not see those items in the tree, they will automatically be exported. When you click the Next button, a list of assemblies referenced by the project in which the item is located is given. (See Figure 4-9.) When the template is added to a project, any references that you select in this step of the wizard will also be added to the project. Suppose you are adding a Windows Form to a console application. If you were to select the check box next to the System.Windows.Forms assembly, the user importing your form will not need to manually add the reference to System.Windows.Forms because the template wizard will take care of adding that reference for you.

Selecting a project item to export

Figure 4-8. Selecting a project item to export

Selecting the references to import when the project item is added to a project

Figure 4-9. Selecting the references to import when the project item is added to a project

The final page of exporting a project item is the same as the final step of exporting a project. (See Figure 4-10.) This step in the wizard allows you to select an icon to use within the New Project or Add New Item dialog boxes. It also allows you to specify the text to show beneath the icon by changing the Template name field. The description of the template shown within the New Project and Add New Item dialog boxes can be changed by modifying the Template description field.

The final page of the Export Template Wizard

Figure 4-10. The final page of the Export Template Wizard

After you click Finish, the wizard will gather the data that you entered, generate a .vstemplate file, package together all the files that make up your project, and then create a .zip file containing this data. If the Automatically Import The Template Into Visual Studio check box is selected, the .zip file is placed into the correct location so that if you were to open the New Project or Add New Item dialog boxes immediately after exporting the template, the template appears in the My Templates section of these dialog boxes. A copy of the template is also stored into the C:Documents and SettingsusernameMy Documents Visual Studio 2005My Exported Templates folder, making it easier for you to find the template and send it to others, or to package it into a .vsi file.

Creating Templates by Hand

Although the template wizard will quickly generate a project or project item template for you, there could be times when you want to customize the template beyond what the Export Template Wizard automatically generates. To create a template, you need a set of files that will produce the sources for the new project or new project item that the user will create. The template wizard supports only Visual Basic, Visual C#, Visual J#, and Web projects, so your base project must be of one of these types. After you have created your base project or project item, you then need to go through those files and insert special strings, called replacement tokens, into the source and project files. The Export Template Wizard automatically scans all the files in the project for specific strings, such as the name of the project, and replaces those strings with a replacement token. When the template wizard processes your source files, the files are searched for the replacement tokens. When one is found, the token is replaced with a value that is dependent on the state of your computer and the files that are currently open inside of Visual Studio. For example, suppose your company used a common header at the top of each source file that looked like the following:

//------------------------------------------------------------------
//  Class1.cs
//
//  (C) Copyright 1999 My Company.
//
//  Contents:   My source code file
//
//  Owner:      UserName
//
//  Revisions:  02/07/2005 14:24:35     Created by UserName
//
//------------------------------------------------------------------

You could modify your template file so that when processed by the wizard, it is automatically modified to contain the relevant information. The modified template would look like this:

//------------------------------------------------------------------
//  $itemname$
//
//  (C) Copyright $year$ $registeredorganization$.
//
//  Contents:   My source code file
//
//  Owner:      $username$
//
//  Revisions:  $time$    Created by $username$
//
//------------------------------------------------------------------

There are many different replacement values available for use within your project files. Table 4-2 lists these values, as well as possible values and a description.

Table 4-2. Replacement Values

Replacement Variable

Example Value

Description

$guid1$, $guid2$, $guid3$, $guid4$, $guid5$, $guid6$, $guid7$, $guid8$, $guid9$, $guid10$

e3593046-f53f-48f9-9c6e-c1761196384f

These variables are used for generating globally unique identifiers (GUIDs) within source code. They can be used when a unique value is necessary, including for generating COM object code.

$time$

02/07/2005 14:24:35

The time that the source code file was generated.

$year$

2005

The year that the source code was generated.

$username$

Craigs

The name of the user who is currently logged on to the computer.

$userdomain$

Redmond

The name of the domain, if available, that the computer is a member of. At Microsoft, the name of the domain that most users belong to is Redmond.

$machinename$

CraigsLaptop

The name of the computer on the network.

$clrversion$

v2. 0.50215

The version of the .NET Framework that is being used by Visual Studio at the time the file is processed to build the program.

$registeredorganization$

Microsoft

The name of the organization that owns the license for the operating system. This value is entered when installing the operating system when the user is prompted for the user name and organization.

$itemname$

My ClassFile

The file name as entered by the user, but with the extension removed.

$safeitemname$

My_ClassFile

The file name as entered by the user, but modified so that the name can be used as an identifier. Any character that would not be recognized as a valid identifier character is replaced with an underscore. This name does not include the extension.

$itemrootname$

My Form1.vb

This is the name of an item being added to either a new or existing project.

$safeitemrootname$

My_Form1.vb

The same as $itemrootname$, but in a form that can be used as a programmatic identifier.

$fileinputname$

Form1

Available only to Add New item templates, this is the file name entered into the Add New Item dialog box. If the name entered into the dialog box is Form1.MyForm.vb, this value will be Form1. MyForm.

$fileinputextension$

.vb

Available only to Add New Item templates, this is the extension of the file name entered into the Add New Item dialog box.

$rootnamespace$

WindowsApplication1

Available only to Add New item templates, this is the default namespace, as specified in the project properties window, for the project. If an item is added to a folder of a project, this value will also include the folder name. For example, adding a file to a folder named NewFolder will cause $rootnamespace$ to be WindowsApplication. NewFolder.

$runsilent$

true/false

This value is set to true when any user interface that the wizard may display should be hidden, and it's set to false if UI can be shown

$wizarddata$

 

Within the VSContent XML file, you can create a tag named <WizardData> under the document node. Any data within this tag is passed along through this value.

$rootname$

Form1.vb

This is the complete file name entered in the Add New Item dialog box.

$projectname$

My Project1

The name of the project.

$safeprojectname$

My_Project1

The name of the project, but modified in a way that allows you to use the name as an identifier in your source code.

$installpath$

C:Program FilesMicrosoft Visual Studio 8Common7IDE

The directory in which Visual Studio is installed.

$exclusiveproject$

true/false

If this value is false, the project is being added to an existing project; otherwise, the project is being added to a new solution.

$destinationdirectory$

C:Documents and SettingscraigsMy DocumentsVisual Studio 2005ProjectsMyProjectName

The directory into which the new project is being created.

Note

Not only can you create your own templates by hand, but you can also modify templates generated with the Export Template wizard. Simply export a template, open the .zip file containing the template, and then modify the files to your liking. When you are done, add the modified files to the .zip file.

The VSTemplate Schema

After you have modified your project or project item to contain the replacement tokens, you now need to create an XML file that describes to the template wizard how the project or project item should be re-created. This file, which has the extension .vstemplate, begins with a <VSTemplate> tag and has three XML attributes. The Version attribute specifies the version of the wizard that the XML file is designed to work with. For Visual Studio 2005, the version number is 2.0.0. The second attribute, Type, specifies the type of items that can be generated with the .vscontent file. Currently, three types of items are supported: Project, Item, and ProjectGroup. If you are creating a new project template, the value is Project; if you are creating a new project item, the value is Item. The template wizard also supports creating multiple projects at a time within the solution. Suppose you need to create a Web Service application and a console application that consumes that Web Service. With a template type of ProjectGroup, you could create both of these projects at once rather than create them separately. Finally, the xmlns attribute lists the XML Schema of the file. The following XML is the most basic of .vstemplate files:

<VSTemplate Version="2.0.0" Type="Project" xmlns=
  "http://schemas.microsoft.com/developer/vstemplate/2005">
</VSTemplate>

A .vstemplate file has two main sections. The TemplateData section describes the visual representation of the template in the UI. The second section, TemplateContent, details each file that is part of your template and how those files should be re-created in the target solution or project.

The TemplateData Section

The TemplateData section is within the VSTemplate section of the .vscontent file. This section determines the appearance of templates within the New Project and Add New Item dialog boxes. An example of this section looks like this XML fragment, which defines a custom C# class library template:

<TemplateData>
  <Name>My Class Library</Name>
  <Description>A project for creating a C# class library (.dll)</Description>
  <Icon>AnIcon.ico</Icon>
  <ProjectType>CSharp</ProjectType>
  <SortOrder>20</SortOrder>
  <DefaultName>ClassLibrary</DefaultName>
  <ProvideDefaultName>true</ProvideDefaultName>
</TemplateData>

The tags of this XML have the following meanings:

  • Name This is the name of the item shown underneath the icon within the New Project or Add New Item dialog boxes.

  • Description This is the text shown when the user selects the icon in the New Project or Add New Item dialog boxes, giving the user more information about what type of project will be created when the template is processed.

  • Icon A path, relative to where the .vstemplate file is located, to the icon to display within the New Project or Add New Item dialog boxes.

  • ProjectType Can either be CSharp, JSharp, VisualBasic, or Web. This controls which node of the tree on the left side of the New Project dialog box the item will appear under, or, if the template is an item template, it specifies the project that the item can be added to.

  • SortOrder This is the priority of the item within the New Project or Add New Item dialog boxes. The lower this value is, the higher it will appear to the top of these dialog boxes.

  • DefaultName This is the default name of the new project or project item that appears in the New Project or Add New Item dialog boxes. Visual Studio takes this name and appends a value onto the end of the name starting at 1. If a project or project item with that name exists, the number is incremented until a unique name is found.

  • ProvideDefaultName If this is true, then the Name field of the New Project or Add New Item dialog box will contain a default value based upon the DefaultName value. Otherwise, the project name is <Enter name>, and the user must manually enter a valid project name to continue.

The TemplateContent Section

The TemplateContent section is where you specify the directory structure layout on disk for your project. When the template wizard opens your .vstemplate file, it reads this section and copies the listed file or files into a temporary location. From this temporary location, the files are added to the solution (if the .vstemplate specifies a new project or project group) or added to the project (if the .vstemplate specifies a project item). This section looks different depending on whether you are creating a project, a project item, or a group project. An example TemplateContent section for creating new projects, taken from the C# class library template, looks like this:

<TemplateContent>
  <Project File="ClassLibrary.csproj" ReplaceParameters="true">
    <ProjectItem ReplaceParameters="true"
      TargetFileName=
      "PropertiesAssemblyInfo.cs">
      AssemblyInfo.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true"
    OpenInEditor="true">
      Class1.cs</ProjectItem>
  </Project>
</TemplateContent>

Here, a project file with the name ClassLibrary.csproj is expected to be in the same folder as the .vstemplate file. This project file, along with two files, AssemblyInfo.cs and Class1.cs, are copied into the temporary folder. Class1.cs will be copied into the same folder as ClassLibrary.csproj, but for AssemblyInfo.cs, two file names are given, AssemblyInfo.cs and PropertiesAssemblyInfo.cs. If only one file name is given (because the TargetFileName attribute is not supplied), the source file is read from the same folder containing the .vstemplate file, and the file is copied into the project destination folder. Because, in this example, two file names are given in the <ProjectItem> tag, a folder named Properties will be created and the AssemblyInfo.cs file will be copied into that folder This allows you to fine-tune exactly how the directory structure for a project or project items are re-created on disk. You could also give a relative path for the text of the ProjectItem tag, such as PropertiesAssemblyInfo.cs. When the template wizard processes that item, the AssemblyInfo.cs file is copied from a subfolder named Properties into the folder named Properties within the temporary folder. Thus, you can write the line of XML to copy the AssemblyInfo.cs into a Properties folder using these three styles:

<ProjectItem ReplaceParameters="true"
  TargetFileName="PropertiesAssemblyInfo.cs">
  AssemblyInfo.cs
</ProjectItem>
<ProjectItem ReplaceParameters="true">
  PropertiesAssemblyInfo.cs
</ProjectItem>
<ProjectItem ReplaceParameters="true"
  TargetFileName="PropertiesAssemblyInfo.cs">
  PropertiesAssemblyInfo.cs
</ProjectItem>

The first line requires the Assemblyinfo.cs file to be in the same folder as the .vstemplate file, whereas for the second and third possibilities, the template wizard expects the file to be in a folder named Properties. All three styles will copy the file into a folder named Properties.

Note

It is important to distinguish a project file from the TemplateContent section of a .vscontent file for creating projects. A project file, such as a file ending in the extensions .csproj, .vbproj, or vjsproj, defines the programming language—specific layout of a project on disk and how that project is loaded into Visual Studio. A .vstemplate file is a programming language—agnostic way of defining a project or project item. It might seem unnecessary to have both a project and a template file because they both contain similar data, but a separate .vstemplate file has its purpose. A group project template and project items do not have an associated project file, so the .vstemplate file is needed in that case. In addition, because the .vstemplate file is independent of language, you can use one file format to create projects for multiple languages, even languages not created by Microsoft.

In all three files from the example .vstemplate file, the ReplaceParameters attribute is set to true, meaning that files will be opened in the temporary folder and examined for replacement parameters, and if any are found, they will be replaced. If you do not have any replacements to make in a file, you can set this value to false (or not even give the attribute—it defaults to false) and increase performance by a little bit, because if this value is false, the steps to open the file and scan for replacement tokens that will not exist can be bypassed.

This sample also makes use of the OpenInEditor attribute. If this attribute is set to true, when the wizard finishes building the project or project item, it will open all the files with this attribute. With the OpenInEditor attribute, you can also optionally use the OpenOrder attribute. This attribute takes an integer value and lets you control the order in which documents are opened. If you were to use the OpenInEditor="true" attribute on multiple files, then all the files will be opened, but only one will be the active window in the Visual Studio UI when the wizard is complete. If you use the OpenOrder attribute, the file with the highest value is opened last and will then be active in the UI.

An example <TemplateContent> section for creating new project items, which was taken from the C# class template, looks like this:

<TemplateContent>
  <References>
    <Reference>
      <Assembly>
      System, Version=2.0.0.0, Culture=neutral,
      PublicKeyToken=b77a5c561934e089
      F</Assembly>
    </Reference>
  </References>
  <ProjectItem ReplaceParameters="true">Class.cs</ProjectItem>
</TemplateContent>

A new project item template is similar to new project templates, except that a project item template does not have an associated project file, so the Project> tag is removed and the <ProjectItem> tag becomes a child of the <TemplateContent> tag. Also, a <References>, <Reference>, and <Assembly> tag has been added to the XML. After the Class.cs file has been added to the project, the template wizard will use the XML XPath query for References/Reference/Assembly tags, and, if found, the wizard will add the assembly named to the list of references for the project the item is being added to. Here, the System assembly is referenced by its full, strong name. The <References> tag is available only for new item templates and not in new project templates. Any references for project templates are already specified within the project file, so references for new projects are not necessary. You can use the <References> tag to prepare a project so that it will build correctly without the user needing to manually add references.

The final type of template is the project group. If you are trying to package a group project for installation in a VSI file, then the TemplateType attribute should be set to ProjectGroup. Rather than using the Project tag as you do for new projects, the <ProjectCollection> tag is used within the <TemplateContent> tag. Within this tag, you can combine the <SolutionFolder> tag to create a new solution folder within the solution and a <ProjectTemplateLink> tag to reference another .vstemplate file. If the <ProjectTemplateLink> tag is given within a <SolutionFolder> tag, the new project will be created within the new solution folder. Any <ProjectTemplateLink> or <SolutionFolder> tags that appear directly under the <ProjectCollection> tag will create the project or solution folder in the location where the project group is added to the solution.

<TemplateContent>
 <ProjectCollection>
  <SolutionFolder Name="Folder1">
   <ProjectTemplateLink ProjectName="ConsoleApp1">
     ConsoleApplicationcsconsoleapplication.vstemplate
   </ProjectTemplateLink>
  </SolutionFolder>
  <ProjectTemplateLink ProjectName="ConsoleApp2">
     ConsoleApplicationcsconsoleapplication.vstemplate
  </ProjectTemplateLink>
 </ProjectCollection>
</TemplateContent>

<SolutionFolder> has one attribute, Name, that is used to name the solution folder that is created, whereas <ProjectTemplateLink> has one optional attribute, ProjectName. The value of this attribute is used to name the new project, but if it is not given, the name of the .vstemplate file without the extension, in both uses of the <ProjectTemplateLink> tags in this example, is csconsoleapplication. When a <ProjectTemplateLink> tag is encountered, the template wizard will gather together all the necessary information (such as where the project is to be created, the name of the new project, and so on), and then spawn off a new instance of the template wizard and create the new project. All replacements in the subproject are processed just as if the project were being created as a new project, and any values from the subproject (such as replacement values) will not propagate back up into the project group wizard replacements. When the new projects are added to the solution, the .vstemplate of those subprojects are searched for relative to the location of the group project .vstemplate file. In this example, the folder containing the group project template is prepended to ConsoleApplicationcsconsoleapplication.vstemplate, and then the template wizard is run on the template file located at this computed path.

Wizard Data

The <WizardData> section of the .vstemplate file allows you to store freeform data in the .vstemplate file. One or more of these XML blocks can appear as a child of the <VSTemplate> tag, and one such <WizardData> XML fragment is shown here:

<WizardData Name="MyWizardData">Some user defined data here.</WizardData>

The data within a <WizardData> tag can be of any XML-representable data you may want, meaning it can be more XML, or, as in this example, just some plain text. When the template wizard reads in the .vstemplate file, any VSTemplate/WizardData tags that are found are read into memory, the Name attribute is read, a $ character is added to the beginning and end of the name, a replacement value is created with this name, and then the replacement value is set to the data within the <WizardData> tag. So, for this example, a replacement variable named $MyWizardData$ is created and the value of this replacement is set to "Some user defined data here." You can then use this replacement value in your source files. This will allow you to create custom replacement values and make replacements in your files just by modifying the .vstemplate file. You could even pack all the contents of source files into the .vstemplate file and then create files that have nothing more than the replacement argument name.

Storing the Template on Disk

After you have created your .vstemplate and source files, you need to place them in a location so that Visual Studio can find and display your templates in the New Project and Add New Item dialog boxes. But first, you must place all the necessary files into a .zip file—simply zip all the files, such as the .vstemplate, source, and project files, into one .zip file. You do not need to rename the file to have a special extension as you do for .vsi files; the extension .zip will do. After the files have been zipped, you need to copy the file into one of four folders so that they can be found. We have already seen the path of two of these folders when discussing the VSTemplate content installer. The folder C:Documents and SettingsUserNameMy DocumentsVisual Studio 2005Templates has two subfolders, ItemTemplates and ProjectTemplates. When the New Project or Add New Item dialog boxes are shown, Visual Studio will examine the appropriate directory for new, deleted, or modified .zip files and make the necessary updates to show the correct template in the dialog box. However, only the user whose My Documents folder has been modified will see changes made to these two folders.

What if you need to install a template that is available for all users of a computer? To make a VSTemplate available to all users, you need to place the template file in a location that all users can read from. The My Documents folder is readable only by the owner of that folder, so this is not an appropriate location for templates all users can invoke. The folders that are available to all users and where Visual Studio will look are either C:Program Files Microsoft Visual Studio 8Common7IDEProjectTemplates for project or group projects, or C:Program FilesMicrosoft Visual Studio 8Common7IDEItemTemplates for project items. Under these folders are subfolders such as CSharp, JSharp, VisualBasic, and Web. Within each of these folders are more folders, which further qualify how and in which dialog box the template can be invoked by the user. After you have selected the folder in which to place your template, you then need to force Visual Studio to recognize the template. Because the .zip files for a VSTemplate are stored in the Program Files folder, a location that only users with elevated permissions (such as an Administrator) can write to, Visual Studio does not try to extract these .zip files every time the New Project or Add New Item dialog boxes are shown. Checking a folder that is changed infrequently would incur a performance hit. So to force Visual Studio to install a template for all users, after copying the file into the appropriate folder, you need to run the command ProgramName/setup from a command prompt, where ProgramName is devenv, vbexpress, csexpress, vwdexpress, or another for the appropriate program that you are setting up the template for.

Wizard Extensions

Although the template wizard does all of the work necessary to process a .vstemplate file, there might be times when you need to customize how a project or project item is generated. For example, suppose you need to display UI to the user to configure how the template is generated, or maybe you need to copy some files into the global assembly cache (GAC) before the project is created so that the project will run correctly, or maybe you need to modify one of the replacement parameter values to your own specification before the .vstemplate file is processed. With a wizard extension, you can easily add to the template wizard the ability to run your own custom code at opportunistic times when the project, the project item, or the group project is being created.

To create a wizard extension, you will need a class library that implements a specific interface, and information about the wizard extension needs to be added to the .vstemplate file. The definition of this interface is contained within the assembly Microsoft.VisualStudio. TemplateWizard.dll. The methods of this interface, which you need to implement, are as follows:

  • void RunStarted(object automationObject, System.Collections.Generic. Dictionary<string, string> replacementsDictionary, Microsoft.VisualStudio. TemplateWizard.WizardRun Kind runKind, object[ ] customParams) This method is called just after your wizard extension has been loaded and before the TemplateData section of the .vstemplate file starts to be processed. This method provides you with data such as the automation model (an instance of a DTE object) of the application running the wizard passed through the applicationObject parameter. The runKind parameter provides you with an enumerated value that can be AsNewItem if the wizard is being invoked to add a new item to an existing project, AsNewProject if a new project is being created, or AsMultiProject if a group project is being added to a solution. The customParams argument provides a way for the host application to pass context-sensitive information to the wizard extension, but this array usually contains 0 elements. But the replacementsDictionary is the argument that gives you the most power over how the template wizard processes a .vstemplate file. Table 4-2 listed replacement values that the template wizard will search for in files within the project or project item that is being created. The replacementsDictionary contains a list of the tokens to replace and the values that will be used to replace with. This Dictionary object can be read from and written to, meaning that you can modify, add, or remove the values that replacements will be made with. The RunStarted method is also a good place for you to display any UI that might be necessary for configuring your template; you can combine any user input from UI to modify the dictionary and control how the template is rendered.

  • void BeforeOpeningFile(EnvDTE.ProjectItem projectItem) This method is called just before a file is opened. The OpenInEditor="true" attribute on the ProjectItem tag must be specified for the item to be opened.

  • void ProjectFinishedGenerating(EnvDTE.Project project) This method is called when all processing to create a project is complete, the project has been loaded into the solution, and the project is open. You can use the automation model for the project to do any further manipulations that might be necessary. This method is called only when a new project or project group is being created; it will not be called for Add New Item templates.

  • void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem) ProjectItem FinishedGenerating is similar to the ProjectFinishedGenerating method, except this method is called when an Add New Item template is processed.

  • bool ShouldAddProjectItem(string filePath) This method is called when an Add New Item template is processed, and it is supplied the file path of the item that is being generated. If you return true from this method, the item will be added to the project, and if you return false, the item will not be added to the project. This method is useful for wizards that show UI, and, based on the input from the UI, this method selectively adds files to a project. The Visual Web Developer Add New Item templates use this method for the Place Code In Separate File check box in the Add New Item dialog box. The new Web Service template has three project item files listed: WebService.asmx, WebService_cb.asmx, and CodeBehind.cs. If the Place Code In Separate File check box is selected, this method returns false for the WebService.asmx file, while the value false is returned for WebService_cb.asmx and CodeBehind.cs files if the check box is not selected.

  • void RunFinished() After all processing of a .vstemplate file is complete, the wizard will call this method allowing you to perform any cleanup your code needs to do.

After you have created your wizard extension, you must place the assembly implementing the extension in a place where Visual Studio can find and load it. For security reasons, the assembly must be in either the directory containing the executable for that application (such as the folder containing Devenv.exe), a subdirectory containing the executable, or a directory listed in the .config file for the executable (such as Devenv.exe.config) in the probing section of the XML. This security restriction is in place because you do not want templates installed through the Internet with the Content Installer to be able to execute code. Suppose you were to download template content from the Internet and create a project from that template. If the VSTemplate .zip file contained an assembly implementing IWizard, and the .vstemplate file referenced that assembly, when the project is created it will cause that code to run. You can see how this is an easy way for a malicious hacker to place code on your computer, and the innocent action of creating a project would run that code. Templates from the Internet can still run code through a wizard extension, but if you need to have elevated permissions (such as Administrator permission) to install an assembly into one of the special directories from which an assembly will be loaded, you minimize the risk of installing bad templates, but you still provide the ability to run wizard extensions. This also means that if you are creating wizard extensions, you need to make sure that your IWizard extension cannot be used to cause harm to a user's computer.

Creating a new wizard extension is easy with the WizardExtension starter kit. This starter kit, which is available with the samples for this book, will create a C# class library that implements the IWizard interface and create a fragment of XML that you can paste into a .vstemplate file. All you need to do is supply the .vstemplate and copy the .dll file into the correct location so that it can be loaded.

Security Attributes

You can strengthen the security of a wizard extension through the use of attributes. Much as you can place attributes into your IImportCommunityContent implementation of a custom installer to restrict which content can be installed, you can place attributes on your class implementing IWizard to restrict which templates can call into your assembly. The TemplateWizardDisallowUserTemplatesSecurityAttribute attribute takes a Boolean value. If this value is true, then only templates that are installed into the Program Files Microsoft Visual Studio 8… location can call into your wizard extension, and templates installed into the My Documents location cannot load and call the extension. If this value is false (the default), any template, regardless of where it is stored, can call into the wizard extension. Another attribute, TemplateWizardSecurityAttribute, limits which template can call into your wizard extension. This attribute takes a string that is the file name without full path information, but with the .vstemplate extension of a template that can call the wizard extension. When the template wizard loads the wizard extension, it first checks for the TemplateWizardSecurityAttribute. If it is found, it compares the string passed to the attribute with the file name of the template. If the two strings match, the wizard extension will be loaded and run. If it does not match, the wizard will not be loaded. Multiple TemplateWizardSecurityAttribute attributes can be placed on the class implementing the wizard extension so that you can call the extension from multiple templates. If you are using the TemplateWizardSecurityAttribute attribute, you should also be using the TemplateWizardDisallowUserTemplatesSecurityAttribute attribute. If you were to use only the TemplateWizardSecurityAttribute attribute and you specified the template name as MyVBTemplate.vstemplate (with the intention to restrict calling the extension from within the Program FilesMicrosoft Visual Studio 8… location), the user could download from the Internet a template that contains the file MyVBTemplate.vstemplate and then your wizard extension would run. By using these two attributes together, you will make sure that the only templates that will run are those the wizard extension was built to aid.

Looking Ahead

Now that we have shown you how to use some of the features of Visual Studio, we can begin exploring how you can customize Visual Studio programmatically with macros.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset