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 limitsthere 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.
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 objectit acts as a source of objects that implement services for the development environment.
|
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 packagesthe CSharp Project Package and the Visual J# Project Package seem to be using different conventions for representing language names, for example.
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 debuggingthe 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 intermediaryif 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 RegistryAlthough 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 developmentif 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 keyit 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 SDKit 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 keyyou 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 keyif 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 |
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:
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.
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.
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.
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.
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.)
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.
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.