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
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
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
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
|
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.
|