Team LiB   Previous Section   Next Section

10.1 Why VSIP?

Since you can automate tasks, create custom commands, and even add wizards using the techniques shown in previous chapters, why would you ever need to use the VSIP extensibility model? The problem with macros, add-ins, and wizards is that they have their limits—there are many tasks that can be accomplished only by creating a VSIP package. Of course, just as add-ins are more complex to create than macros, the downside of building a package is that packages are much more complex (and therefore more time consuming) to develop than macros or add-ins. Also, while add-ins can be enabled and disabled at will by the end user, the only way to disable a package is to uninstall it, which may further complicate the development process. Table 10-1 shows which extensibility features are available to the various ways of extending VS.NET.

Table 10-1. Features of macros, add-ins, and packages

Feature

Can implement with macro

Can implement with add-in

Can implement with package

Manipulate the IDE object model (i.e., automate a task)

Yes

Yes

Yes

Create Tool windows

No

Yes

Yes

Insert a menu command

No

Yes

Yes

Create custom property pages on the Options dialog

No

Yes

Yes

Appear on the About box

No

Yes

Yes

Appear on the splash screen

No

No

Yes

Add a new project type

No

No

Yes

Be part of a build

No

No

Yes

Create a debugger

No

No

Yes

Create an editor

No

No

Yes

Create a designer

No

No

Yes

Add a new data source in the Server Explorer

No

No

Yes

Add a command-line switch to devenv.exe

No

No

Yes

Add IntelliSense or syntax coloring to an editor

No

No

Yes

Write using a managed language

Yes (VB.NET only)

Yes

No

Many of the features that can be implemented only with a package are already built into VS.NET for languages like C#; all of the project types and editors built into VS.NET are built using packages. If you build your own custom package, you will be using the same extensibility framework on which the majority of the functionality in VS.NET is built.

A package is a COM component, registered in a special way, which advertises services through various registry entries. VS.NET loads packages automatically when their services are required. Although all packages implement the same IVsPackage interface, individual packages may expose different sets of services to the environment. A package is effectively a factory object—it acts as a source of objects that implement services for the development environment.

At this time it isn't feasible to implement a package in a managed language, so if you choose to write a package, you will be writing unmanaged code, unlike add-ins, which you can easily write in managed code. (It is technically possible to use a managed language, but there are some very tricky COM interop issues to deal with, meaning that it is more effort than it is worth. However, this is likely to improve with future versions of VS.NET.)

Table 10-2 shows the packages installed with VS.NET Enterprise Edition, organized by the kind of service that they provide. As you can see, Microsoft has not yet come up with a wholly consistent naming policy for its packages—the CSharp Project Package and the Visual J# Project Package seem to be using different conventions for representing language names, for example.

Table 10-2. Visual Studio .NET Enterprise Edition packages

Package type

Package name

Project

Visual Basic.NET Project System

Visual C++ Package

Solution Build Package

Visual Basic .NET SDE Project System

ATL Package

Visual Studio Analyzer Package

ACT Project Package

CSharp Project Package

Enterprise Templates Package

Visual Studio Project Persistence Package

Visual C++ Project System

Visual J# Project Package

Language

Babel Language Package

Visual Basic Common Compatibility Wrapper Package

CPP Language Manager

C# Language Service

Compiler

Microsoft Visual Basic Compiler

Debugging

Visual Studio Debugger

UI

Class Outline Package

TaskList Package

Editor/Designer

HtmEditorPackage

Undo Package

Visual Studio Deployment Editors

Component Enumerator Package

Visual Database Tools Package

Binary Editor Package

Visual Studio XML DataSet Designer

VSDesignerPackage

VsRptDesigner Package

Text Management Package

Crystal Reports Tools Package

DesignerPackage

Resource Editor Package

VS7 CSS Editing Package

CFDesignerPackage

Help

Help Package

Utility

Commands Definition Package

Visual Studio Team Core Package

DirListPackage

Visual Studio Deployment Package

Visual Basic Deploy Deployment Package

DBServicesPackage Class

PltPkg Package

Device CAB Package

Visual Studio .NET Converters Package

Shell

MS Environment Menu Package

MS Help Package

vsmacros

Source Code Control Package

WebBrowser Package

MS Environment Package

Complus Library Manager Package

The VS.NET environment is built around the idea of services. Packages provide interesting services to the environment and can also consume services provided by other packages. Packages do most of the heavy lifting by providing services for persistence, editing, building, and debugging—the shell mostly acts as a container for packages, although it also exposes a number of interesting services of its own. The shell and packages work together to provide all the services that we use in VS.NET.

When a package needs a service, either from the shell or from another package, it asks the shell for that service. The shell will attempt to locate the package that provides this service, on behalf of the requesting package. So in a way the shell is just a coordinator, obtaining services from packages and handing them back out again to other packages that request them.

Here are some of the services that the shell is responsible for:

  • Drawing and maintaining the main UI windows

  • Loading packages when needed (packages are loaded on demand)

  • Routing of commands to the appropriate package

  • Managing the solution files

  • Maintaining a list of all the currently running documents in a running document table (RDT)

To enable packages to use one another's services, the shell acts as an intermediary—if a package needs another package's services, it can ask the shell. The shell therefore also offers these functions:

  • Retrieving interface pointers to services or packages

  • Registering a package's services with the environment

  • Creating, hosting, and modifying windows in the UI

    Packages and the Registry

    Although VS.NET packages are written as COM components, they are registered slightly differently. Instead of using the normal parts of the registry related to COM, VS.NET uses its own registry keys to hold information about the package coclasses. This is to allow multiple different versions of a VS.NET package to be installed simultaneously. This serves several purposes.

    First, it is possible to install versions of your package that are specific to a particular version of VS.NET. Second, VS.NET is able to load the registration information from different parts of the registry, selected by a command-line switch. This means you can install packages that will be loaded only when you want them to be. This is very useful during development—if your package gets into a state in which it prevents VS.NET from loading, it is useful to be able to fire up a copy of VS.NET that won't try to load your package. This facility also allows you to override any of the built-in services with your own versions but still leave the original installation intact.

    Figure 10-1 shows a VS.NET registry key with several different paths. The 6.0 key is for a previous version of Visual Studio, but the three keys starting with 7.1 are all for VS.NET 2003. (VS.NET 2002 used 7.0.) The key named 7.1 is the root key—it is the key from which VS.NET will normally load its configuration. The keys named 7.1Exp and 7.1Foo contain configuration settings that will only be loaded when you pass the appropriate command-line switch. To load the Exp settings, you would launch VS.NET thus:

    devenv.exe /rootsuffix Exp

    This 7.1Exp key is created when you install the VSIP SDK—it makes a copy of the settings in the main 7.1 key. When you are developing packages, you will normally install them under the 7.1Exp key during development. There is nothing magic about the 7.1Exp key—you can create as many more configuration keys as are useful to you. But as a general rule, you should never install packages that are still in development to your root key—if your package does something wrong, you may have to reinstall VS.NET to fix the problem.

    In the rest of this chapter, registry keys are always named relative to the base registry path unless otherwise specified. So the Packages key means this key:

    HKLM\SOFTWARE\Microsoft\VisualStudio\7.1\Packages

Figure 10-1. Multiple VS.NET registry sections
figs/mvs_1001.gif

10.1.1 Typical Package Execution Path

Since the VS.NET architecture is built around packages, it is interesting to look at some typical usage scenarios to examine all the packages that come into play when creating, building, debugging, and persisting a project. Here is how packages are used as you work with a project in VS.NET:

Launching VS.NET

When you launch VS.NET, it loads a number of base packages, including the MS Environment Package and the MS Environment Menu Package, which are responsible for creating the basis of the VS.NET UI.

Loading or creating a project

When opening a project, VS.NET loads the package responsible for that particular kind of project. Each project type has a GUID, and these are all listed under VS.NET's Projects registry key. Each project type's key has a string value called Package, which contains the GUID of the package responsible for the project type.

For example, the GUID for the C# project type is {FAE04EC0-301F-11d3-BF4B-00C04F79EFBC}. Looking this up under the Projects key reveals that the package ID is {FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}. VS.NET will then look up this ID in its Packages registry key, where it will find an entry for the CSharp Project Package.

Putting the project under source control

When a project is added to source control, the project package asks the shell for the source control service. This service is provided by the Visual Studio Team Core Package.

Editing a file in design view

C# Windows Applications usually define one or more forms. These are C# source files containing a class derived from the Form class. When you double-click on a form in the Solution Explorer, the form will be shown in a design view, which displays the form more or less as it will appear at runtime and which allows you to edit the form using drag and drop.

When you open a file from the Solution Explorer, the CSharp Project Package asks the shell for the appropriate editor package. (By default, this will be the CSharp Project Package itself, although the user can choose a different editor by using the Open With dialog, as described in Chapter 2.) The CSharp Project Package then asks the editor package's Editor Factory class to open the design view if one is available. In the case of the CSharp editor, it actually looks at the .cs file to see if a designer is available for the class it contains. (There are built-in designers for all form classes.) If a designer class is found, the editor package will create and return the appropriate view. Otherwise, it will return a normal code view.

Editing a file in code view

A file may be opened in code view in several ways. The user can explicitly request this from the Solution Explorer's context menu. Files opened with the Open File dialog (Ctrl-O) are always opened with the code view. Or the user may have pressed F7 while looking at a file's design view. In all cases, the Editor Factory is located in the same way as it was for the design view. But this time, the factory will be asked for a code view. The factory will return an interface pointer to another view object, usually the VS.NET default text editor.

When the text editor first opens a file, it looks in the Language Services registry key and tries to find a language service for the relevant file extension. (See "Language Services" later in this chapter for detailed information about language services.) If it finds one, it loads that language service's package and sets up a bidirectional communication between the language service and the editor. The language service can then provide syntax highlighting, statement completion, and method tips. (The CSharp Language Service uses a language parser to provide highlighting and uses CLR metadata to provide statement completion and method tips.)

Building and debugging

When you build a project, the project package is responsible for loading and executing the appropriate compiler. If syntax errors are discovered during the build, the language service package can highlight the lines in the editor where the syntax errors occur.

After a successful build, you can debug your program. Loading the correct debugger is also the responsibility of the project package. All .NET projects compile to IL, so the .NET project packages just ask the shell for the IL debugger engine. VC++ projects compile to x86 machine code, so the VC++ project package asks for the standard Windows debugger engine. Both of these debug engine services are provided by the Visual Studio Debugger package.

Saving projects

When you save a project, the project package is responsible for persisting the project's settings. However, the shell provides services to aid that persistence. The shell is responsible for persistence of the solution files.

    Team LiB   Previous Section   Next Section