在前几篇文章中,我们只是通过创建和“分析”三个非常小的、由VSPackage向导生成的package来管中窥豹地见识了一下VSX。这些例子有助于我们熟悉创建小的package的基本步骤。但是,我们必须更深入一些, 看一下Visual Studo IDE是怎样工作的,以及它是怎样集成package的。
在我们涉及到其他细节之前,我们先要整理一下对VSX的认识。在本篇文章里,我们不会创建任何代码,只是试图去搞清楚和VSX相关的概念。
我在前面几篇文章中多次提到过,Visual Studio扩展性开发是基于COM技术的。package中的对象和实体(例如命令、菜单、工具栏、窗口、编辑器、项目等)都是COM对象。当然,如果我们用的是托管代码(例如C#、VB.NET),我们看到这些类和实例是托管的.NET类型和实例。但如果我们用了非托管代码,我们不得不处理COM对象和实例。
在开发VSX的代码时,之所以可以用很多模式和特性,是因为VSX里里外外都用了COM。我假设你对COM没有太深入的理解(我自己也不是一个COM专家),但我待会会告诉你一些必须要了解的基础知识。
在前几篇文章中,我们创建了几个简单的Visual Studio Package,所以我们已经对VSPackage有了一个初步的认识,现在让我们更深入的探讨一下它。
VSPackage是构建Visual Studio的一个基本的单元。实际上,Visual Studio是由一系列的VSPackage协同工作而成的,就像一个生态系统一样。一个Package,不论是从VS体系结构上来看,还是从部署、安全和许可认证方面来看,它都是VS的一个基本单元。另外,在物理上,一个或多个package可以存在于同一个程序集中。
开发者(包括Visual Studio的开发者)通过创建VSPackage来扩展VS IDE。这些扩展可以是:
在后面的文章中,我们将逐一探讨这些扩展的细节,今天在这里我先给大家一个基本概述来说明它们是什么,以及它们如何在VS中使用。
另外,一个package可以在Visual Studio的启动界面里或在关于对话框里显示它自己的信息。
一个package可以把它的状态和配置信息保存在持久化存储设备中,并且可以读取这些配置。例如文本编辑器可以设置语法高亮、字体、颜色、标签等。
每个package必须被所谓的package load key(PLK)签名,Visual Studio通过它来检查package的合法性。Visual Studio只会加载拥有合法PLK的package。另外,从技术上来说,Package是实现了IVsPackage接口的类型。这一次我们不会深入讨论IVsPackage,但在后面的文章中,我们将通过代码来测试它的细节。
一般来讲,我们不会为了开发package而开发package。我们创建package是因为它们不但可以为我们自己提供功能(此时,我们是消费者),也可以为其他的package提供功能(此时,其他package是消费者)。例如,假设我们的package提供了一个工具窗去查找特定方法的引用,我们就是这个窗口的消费者。如果这个package不仅为这个工具窗提供查找功能,也作为“可调用的方法”为其他package服务,那么其他package就是这个服务的消费者。
所以,服务是package之间或package和与它相关的对象(当我说“package的对象”时,我指的是窗口、命令、设计器等这些被package自己创建的东西)之间的契约。
下图说明了VSPackage和服务之间的概念:
(译者注:非常遗憾,这里缺图。原文中的图片链接已经无效,联系了原文作者但一直没有回应,以后如果找到这个图片一定补上。)
VSPackage可以包含服务,这些package被称为service provider。在上图中,VSPackage1和VSPackage3是service provider,而VSPackage2不是。能给其他package调用的服务被称为全局服务(global service)。VsPackage1和VsPackage3都包含global service,这些服务可以被VSPackage2调用(当然也可以被其他的package调用)。package也可以包含只能被自己调用或者只能被package的对象调用的服务。这种服务被称为本地服务(local service)。VSPackage1和VSPackage3都包含local service,它们被对象调用(例如被VSPackage1中的编辑器和VSPackage3中的工具窗)。
关于VSX中的服务,有一个坏消息:它们是隐蔽的,不容易被发现。这意味着我们不能猜测出一个package(或其他对象)中能提供哪些服务。
所以,如果你想使用一个服务,你必须“通过它的名字调用它”,这意味着你必须知道这个服务的名字。要知道服务的名字,唯一的方法是去查阅这些服务所在package提供的文档。VSX的文档里列出了大概130个服务。
一般来说,服务被定义成接口。大部分服务只实现一个接口,但也有一部分服务实现了多个。所以,当我们想使用一个服务的时候,我们必须要知道两个“名字”:服务的名字和接口的名字。
你也许注意到了,我在“名字”这里用了引号。这是因为所有的服务都是对象。如果我们用的是interop类型,“名字”就是它们的.NET类型;如果我们用的是COM对象(非托管代码),“名字”就是这些COM类型的GUID。
让我们用一个例子来更清楚的说明它!在SimpleCommand里,我们使用SVsUIShell服务去显示一个消息框,我们用GetService方法去获得一个IVsUIShell接口的引用:
1: IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
2: uiShell.ShowMessageBox(...);
当我们得到一个服务的引用后,我们就可以使用它提供的方法和属性了。在上面的例子中,我们调用了这个服务实例uiShell的ShowMessageBox方法。
VSPackage访问服务的方式(现在我只是指通过GetService方法去得到服务实例)为服务模型带来了一些“特性”和结果。
了解了关于服务的这么多信息之后,如果我告诉你在使用服务前必须要注册它们,我猜你们都不会感到奇怪。
要想注册服务,需要在Package的类定义文件上面加上ProvideServiceAttribute。regpkg.exe会利用这个属性来注册服务。
虽然还有很多关于服务的有趣的东西,但就目前来说,这些已经足够了。以后我们会通过代码去深入研究它。
.NET开发人员更喜欢用托管的.NET类型,因为它们可以利用底层运行环境的强大特性。然而,由于历史原因(.NET时代之前的VS版本),Visual Studio的主体部分是建立在非托管代码上的,并支持COM类和接口。为了访问到COM对象,.NET提供了一种被称为interoperability程序集的东西,简单来讲,就是用.NET类型来包装了COM类型。我们有两种主要的方法去使用VSX中的COM对象:创建非托管代码(例如用C++);或者利用interoperability程序集中来编写托管代码(用c#或VB.NET)。
对于我来说,我更喜欢托管代码(并且我猜大部分.NET开发人员也是如此),所以我会用interop程序集去做我的示例代码。在一些常见的任务中,COM使用了不同的模式,例如在类型标识、内存分配、异常管理等方面,另外,COM不支持继承。
如果只使用interop程序集的话,我们的代码会变得非常冗长,并且不能够使用.NET和C#提供的语言和一些运行时的功能。微软在Visual Studio的COM interoperability程序集之上创建了一个框架,叫做Managed Package Framework(MPF),可以帮助我们用“本土化”的托管代码来创建VSPackage。
GAC中安装了一堆的VSX的interop程序集,你也可以在VS SDK的安装目录(例如在C:\Program Files\Microsoft Visual Studio 2008 SDK)下的下VisualStudioIntegration\Common\Assemblies子目录中找到它们。这些interop程序集的名字以Microsoft.VisualStudio开头,但不是所有以这个开头的程序集都是interop程序集。在这个文件夹下面,你可以看到差不多100个程序集文件。其中,interop程序集如下(我省略了Microsoft.VisualStudio前缀):
程序集 | 描述 |
~.Shell.Interop | 这个程序集定义了几百个核心的interop类型(包括接口,结构,枚举,类,等等) |
~.Shell.Interop.8.0 ~.Shell.Interop.9.0 |
在VS 2005和VS 2008中,有不同的COM类型,这些不同的COM类型定义在这两个程序集中,其中8.0是给VS 2005用的,9.0是给VS 2008用的。 |
~.OLE.Interop | 这个程序集包装了几百个标准OLE类型。 |
~.TextManager.Interop |
Visual Studio有一个很好的内置编辑器。这两个程序集用来访问编辑器接口。其中,8.0是针对Visual Studio 2005和2008中新增的接口类型的。 |
~.Debugger.Interop | 如果你想访问VS IDE提供的内置调试器中的接口和调试功能,你可以用这个程序集。 |
MPF程序集与interop程序集(以及其他的VSX相关的程序集)在同一个文件夹中,并且也是以Microsoft.VisualStudio开头的。其中,最重要的程序集如下:
程序集 | 描述 |
~.Shell and~.Shell.9.0 | 这两个程序集定义了MPF的核心类型。以9.0结尾的程序集是针对于VS 2008的,如果你用VS 2008开发,你应该用这个程序集,以便regpkg.exe可以注册你编译后的package。 |
~.Shell.Design(译者注:原文中的~.Shell.Desing应该属于笔误) | 这个程序集中定义的类型可以用来扩展Visual studio的设计器。 |
如果用VS 2008创建一个新的VSPackage,向导会帮我们添加一些对interop程序集和MPF程序集的引用,这些引用有:
如果你需要其他的interop或者MPF程序集,你可以自己再添加引用。
在这篇文章中,我们VSX的基本概念和最重要的细节做了一些探讨。
当然,VSX中还有很多其他的重要的概念,但对于继续我们的学习来说,今天讨论的这些已经足够了。
在下一篇,我们继续用代码示例来探讨VSX的开发。
原文链接:http://dotneteers.net/blogs/divedeeper/archive/2008/01/11/LearnVSXNowPart5.aspx