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, MySubDirectory\File.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 × 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

Software\Microsoft\ VisualStudio\8.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 Documents\Visual Studio 2005

Location where user files are stored for the application.

ApplicationPath

"c:\Program Files\Microsoft Visual Studio 8\Common7\ IDE\devenv.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

image from book

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\ SOFTWARE\Microsoft\MSEnvCommunityContent\ContentTypes. 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 Listing 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.

Listing 4-1: An example registry script to register a content installer

image from book
     Windows Registry Editor Version 5.00     [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType]     "ClassName"="MyContentType.MyContentType"     "Assembly"="C:\Documents and Settings\CRAIGS\My Documents\       Visual Studio\Projects\MyContentType\MyContentType\       bin\debug\MyContentType.dll"     [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts]     [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.0]     [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.0\Visual 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_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.0\Visual 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_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.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_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\ContentTypes\     MyContentType\ContentHosts\1.0\Visual 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_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.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_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\       ContentTypes\MyContentType\ContentHosts\1.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 
image from book

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 Documents\Visual Studio 2005\MyContentType; the installer places .exe files on disk, and the .vscontent file gives a destination path of \Windows\notepad.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.




Working with Microsoft Visual Studio 2005
Working with Microsoft Visual Studio 2005
ISBN: 0735623155
EAN: 2147483647
Year: 2006
Pages: 100

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net