Dotneteers.net
All for .net, .net for all!

LearnVSXNow! - #5: Basic VSX ideas

 

In the previous posts we only scratched the surface of VSX by building and “analyzing” three very small packages generated by the Visual Studio Integration Package Wizard. These examples were very useful to get familiar with the basic steps of creating small packages. We could have a look behind the scenes and have a feeling how Visual Studio IDE works and how to integrate packages.

Now, before going to treat other details, it is time to reorganize our emotions and perceptions about VSX. In this post we are going to create no programs but try to clean definitions and ideas related to VSX.

Warning: “COM is used behind!”

I mentioned at several points in the previous posts that behind Visual Studio Extensibility the implementation uses COM technology. Objects representing packages and entities in the packages (like commands, menus, toolbars, windows, editors, projects, etc.) are all COM object instances. Of course, if we use managed code (we use C#, VB.NET) we see these object classes and instances as managed .NET types and instances. If we use unmanaged code, we return to deal with COM objects and instances.

Many patterns and features used when developing code for VSX comes from the fact that COM is used all around of VSX. I do not assume deep knowledge of COM from your side (as I cannot take into account myself as a COM guru), but you are definitely need some basics that I will tell you later.

What is a Visual Studio Package?

In the previous posts we created a few simple Visual Studio Packages (VSPackage). Now we have an idea about what a VSPackage is, but let us have a closer look.

A VSPackage is the principal architectural unit of Visual Studio. Actually, Visual Studio itself is set of VSPackages working together like an ecosystem.  A VSPackage is a software module that is a unit not only from architectural point of view but also from deployment, security and licensing aspect. Physically one or more packages can be in the same assembly.

Developers — including the developers of Visual Studio — create VSPackages to provide some extension to the VS IDE. This extension can be:

—  Services. Software objects that offer functionality for the developer or for other packages. For example, the C# Language Service (as its name also tells) is a service.

—  UI elements. Menus, toolbars, windows that can be used by the developer to initiate some actions on the user interface, interact with or display messages, information, figures, etc.

—  Editors. During development we write and modify program text to create applications. This task is the responsibility of en editor. Visual Studio 2008 has its own core editor but we can create our own editors in VSPackages.

—  Designers. Application creation is not just simply a text-typing activity. We have many visual tools known as designers that allow an alternative representation and design of modules, components, parts or even full applications. Well-known example is the WinForms designer we use to create WinForms user interfaces.

—  Projects. When developing applications we generally work with a large number of files. A project is an organization of source files and resources. A project not just simply stores these files but also allows building, debugging and deploying the product created from source files.

Later we will go into details about each of these extensions a VSPackage can define, here above I tried to give you a basic overview what they are and how they are used in VS.

A VSPackage can display information about itself in the splash screen of Visual Studio or in the About dialog.

A VSPackage can store its state and settings that can be retrieved between Visual Studio sessions from a persistent storage. A good example is the settings of the text editor including syntax highlighting, fonts, colors, tabs, etc.

Each VSPackage should be signed with a so-called package load key (PLK) that is used by Visual Studio to check if the package is the one it says about itself it is. It only loads the package, if the PLK is valid.Technically, a VSPackage is a type implementing the IVsPackage interface. This time we do not look behind IVsPackage, but in a later post we examine it in details with code samples.

What are Services?

We generally do not develop VSPackages just for their own existence. We use them because they provide functionality either for us as a user or for other VSPackages as consumers. For example, if our VSPackage provides a tool window to search for a specific method’s reference information we are users of this window. If this VSPackage provides this search functionality not only for the tool window, but as “callable method”, other VSPackages can be the consumers of this service.

So, services are contracts between VSPackages or contracts between a VSPackage and its related objects. When I say “objects of a VSPackage” I mean those windows, commands, editors, etc. that has been created by the package itself.

In the following figure I illustrate the concept around VSPackages and services:

VSPackages can have services. Those that have are called service provider packages. On the figure above VSPackage1 and VSPackage3 is a service provider, VSPackage2 is not. Services that are provided in order other VSPackages can consume them are called global services. VsPackage1 and VSPackage3 both have global services that are consumed by VSPackage2 — and of course, can be consumed by other VSPackages. A package can have services that are used only from within the package itself or its consumers are the objects created by the service. This kind of a service is called local service. VSPackage1 and VSPackage3 both have local services consumed by objects (for example by an editor of VSPackage1 and by tool window of VSPackage3).

Using services

There is a bad news about services in VSX: they are not discoverable. It means that we have no way to communicate with a VSPackage (or with some other manager object) to guess out what kind of services it provides.

So, if you want to use a service, you must “call it on its name”. It means that you have to know the “name” of the service. Your only way to get it is turning to the reference documentation of the package defining the service. The VSX documentation lists about hundred and thirty services.

Generally service object contracts are defined by interfaces. This is the way service contracts are defined in VSX. Most of services implements only one interface but there are a few that implement more than one. So, when we want to use a service, we must know actually two “names”: the name of the service, and name of the interface we want to consume.

You may note that I used “name” between quotes. It is because both the services are objects. If we use them through interop, their “name” is their .NET type. If we use them as native COM objects (from unmanaged code) their “name” is the GUID of the COM type.

Let us see an example to make it clear! In the SimpleCommand package we used the SVsUIShell service to display a message box. We did it by obtaining a reference to IVsUIShell interface using the GetService method of the package:

IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));

uiShell.ShowMessageBox(...);

When we get a reference to a service objects, we can use its methods or properties. In the example above we call the ShowMessageBox method of the service instance represented by uiShell.

Loading packages and accessing services

The pattern VSPackages use to access service objects (right now I simply mean we use the GetService method to obtain the objects providing service methods and properties) adds some “features” and consequences to the service model.

—  On-demand loading. A VSPackage do not have to be loaded into the memory while its services are not used. So, the VS IDE only loads a VSPackage when a service object is requested from its “repertoire”. In reality, a package and a service provider are two different ideas. A service can be provided by theoretically any object implementing the IServiceProvider interface. In practice our package is automatically a service provider if we inherit it from the abstract Package class (as we did in the previous posts’ samples). That is, because Package implements IServiceProvider along with a number of other interfaces.

—  Siting VSPackages. The IDE offers a service provider for all VSPackages to obtain service objects to the global services (services offered by other service providers or VSPackages). When a VSPackage is loaded into the memory, Visual Studio passes a service provider reference to it in order to gain access to global services. This is what we call “siting” the VSPackage. Without siting a package would not be able to communicate with Visual Studio through service objects.

—  Accessing global services. If we have a sited object (a VSPackage instance or an object instance implementing the IVsPackage) it is easy to access a global service using the GetService method. If we have on object that is not sited in the hierarchy of a package instance and we can traverse from the object to its parent VSPackage instance, we can also use GetService. However, there are situations when we have an unsited object and it is not simple (or does not work) to obtain a package reference. In this case we can use the static Package.GetGlobalService method.

Registering services

I guess having known so much about services none of you will be surprised if I tell services should be registered in order to use them.

This is done by adding the ProvideService attribute to the definition of the package class. The regpk.exe utility reads the attribute and registers the service accordingly.

There are many interesting facts about the service architecture in VSX, but right now it is enough to go on. Later, we are going to take a deeper look with code examples.

Interoperability assemblies and the Managed Package Framework

.NET developers prefer to use managed .NET classes since those leverage the great features of the underlying runtime environment. However, the major part of Visual Studio is built on unmanaged code and supports COM classes and interfaces due to historical reasons pointing back to the “pre-dotnet” era of VS. To access the COM objects .NET offers the so-called interoperability assemblies that simply said are containers of .NET types wrapping COM types. To use VSX COM objects we have two major alternatives as for accessing any kind of COM objects: we either create unmanaged code (for example in C++) or write managed code (in C#, VB.NET) leveraging on the interoperability assemblies.

As I prefer managed code — and I suppose, so do almost all .NET developer—, I am going to use the interop assemblies through my samples. COM has different patterns for many usual tasks just like for type identification, memory allocation, exception handling, etc. COM does not support inheritance.

To use only the interop assemblies our code would be verbose and could not use those language and runtime features we like in .NET and C#. Microsoft created a thin layer over the Visual Studio COM interoperability assemblies called Managed Package Framework (MPF) to allow writing ”native” managed code to create VSPackages.

Interop assemblies in VSX

VSX comes with a dozen of interop assemblies that are installed to the Global Assembly Cache (GAC). You can also locate these assemblies under the VS SDK installation path (for example under C:\Program Files\Microsoft Visual Studio 2008 SDK) in the VisualStudioIntegration\Common\Assemblies folder. The interoperability assemblies’ names start with Microsoft.VisualStudio but not all assemblies starting with that are interop assemblies. In the folder you find nearly a hundred of assembly files. VSX interoperability assemblies are the followings (I omitted the Microsoft.VisualStudio prefix from their names):

Assembly Description
~.Shell.Interop

This assembly defines several hundreds of core interoperability types (interfaces, structures, enumerations, classes, etc.).

~.Shell.Interop.8.0 and ~.Shell.Interop.9.0

There are COM types new in VS 2005 IDE and VS 2008 IDE. The interoperability wrappers of them are defined in these assemblies where the 8.0 suffix is for VS 2005 while 9.0 suffix for VS 2008.

~.OLE.Interop

There are a few hundred of standard OLE types and interfaces. This assembly provides wrapper types for them.

~.TextManager.Interop and
~.TextManager.Interop.8.0

Visual Studio has a great built-editor. These assemblies contain wrapper types to access the editor interfaces. The assembly with suffix 8.0 defines the interface types new to Visual Studio 2005 and 2008.

~.Debugger.Interop

You can use this interop assembly if you plan to access interfaces types (and so debugging functionality) offered by the VS IDE built-in debugger.

Managed Package Framework assemblies

The assemblies of MPF can be found in the same folder as interop assemblies (and other VSX related assemblies) and also start with Microsoft.VisualStudio. The most important assemblies are the followings:

Assembly Description
~.Shell and~.Shell.9.0

The core types of MPF. There is a separate version for VS 2008 with the 9.0 suffix. If you work with VS 2008, you should use this assembly in order regpk.exe could register your compiled package.

~.Shell.Desing

This assembly contains types that can be used to develop designer extensions for Visual Studio.

   

Assemblies referenced in VSPackages

When we create a new VSPackage with Visual Studio 2008, the wizard creates a project where a few interop assembly and MPF assembly is added to the reference list. These are the followings:

—  Microsoft.VisualStudio.OLE.Interop
—  Microsoft.VisualStudio.Shell.9.0
—  Microsoft.VisualStudio.Shell.Interop
—  Microsoft.VisualStudio.Shell.Interop.8.0
—  Microsoft.VisualStudio.Shell.Interop.9.0
—  Microsoft.VisualStudio.TextManager.Interop

If you need any other interop or MPF assemblies, you can add it to the references manually.

Where we are?

In this post we took a look into the most important details of the fundamental ideas in VSX.

—  VSPackages are the principal architectural, security, deployment and licensing units of Visual Studio. Visual Studio itself is built on a set of VSPackages.

—  VSPackages can offer global services that can be consumed by other packages. Services are not discoverable; they must be registered in order to be consumable. Visual Studio supports an on-demand load mechanism to find and load service provider packages.

—  VSPackages are built on COM technology. Visual Studio SDK ships interop assemblies to access COM types. The Managed Package Framework provides a thin layer over the interop assemblies to allow “native” .NET VSPackage development in many areas.

Of course, there are a dozen of other ideas playing significant role in VSX, but to go on with learning, the ones treated here are just enough.

Next time we go on with code samples to discover new ideas and features.


Posted Jan 11 2008, 06:29 PM by inovak
Filed under:

Comments

DiveDeeper's blog wrote LearnVSXNow! #26 - Services — with no-code service initialization
on 07-23-2008 9:15

In the LearnVSXNow series I treated Visual Studio Services several times. In Part #5 ( Basic VSX Ideas

Add a Comment

(required)  
(optional)
(required)  
Remember Me?