240
一线老司机

(翻译)LearnVSXNow!-#2 创建一个空的VS Package

    为了熟悉Visual Studio Package的概念,在这篇文章中我们将创建一个空的Visual Studio 2008 package。

    打开Visual Studio 2008,新建项目,在项目类型对话框中选择“其他项目类型”/“扩展性”/Visual Studio Integration Package,如下图:(译者注:原文的图片无法打开,本系列的图是由译者另外截图的,并非原图)

 image

    如上图所示,我们把Framework的版本设为.NET Framework 2.0(译者注:实际上,不一定非得选择.NET Framework 2.0作为framework的版本,采用默认的3.5版本即可),项目名称设为EmptyPackage,点击“确定”后,将弹出Visual Studio Integration Package向导,如下图:

image

    点击Next按钮,开始定义我们的Package。

 image

     如上图所示,我们选择C#作为该Package的开发语言,另外,由于VS Package必须被强命名,所以我们需要一个key文件来给我们的package程序集进行签名,在这里我们利用向导帮我们自动生成一个key文件。点击“Next”,将在下一步中设置Package的基本信息。

image

    请根据上图填入相应的内容。VSPackage Name, VSPackage Version and Detailed information用于显示我们的package,Minimum Visual Studio value选项代表我们的package支持哪个版本的vs。

    这个选项非常重要,因为不同版本的Visual Studio有不同的service。如果我们的package用到了VS专业版才有的service,那么在VS标准版中将不能用我们的package。同时,我们也应该根据到自己的Visual Studio的版本和许可证来设置该选项。

    下一步是设置VS Package的选项。

image

    向导可以帮助我们创建一个菜单命令(menu command)、一个工具窗口(tool window)和一个自定义编辑器(custom editor),但是由于我们这次只是创建一个空的package,所以在这里一个框都不要勾选。点击Next按钮后,会转到向导的最后一步,在这里我们可以为我们的package添加测试项目。

image

    像上一步一样,为了创建一个最简单的package,在这里我们也不要勾选任何选项(事实上,我们需要勾掉它们,因为它们默认是被勾中的)。点击Finish按钮,Visual Studio会在几秒钟内帮我们创建该package的项目。成功创建项目后,在解决方案浏览器中,我们将看到下面的结构:

image

    可以看到,在项目引用中,包含很多interop assembly,这些程序集帮助我们与Visual Studio IDE中的COM对象交互,并提供package需要的service。你也许发现了项目引用中的System.Core.dll,这个程序集是.Net Framework 3.5的一部分,这和我们一开始创建项目的时候选择的.NET Framework 2.0有些矛盾,不过没关系,现在我们先忽略它。

Package的文件

    在我们的项目中,最重要的文件是一个资源文件和两个cs文件,如下:

文件名 描述
EmptyPackagePackage.cs 该文件定义了可以被Visual Studio加载的EmptyPackagePackage类。
Guids.cs 就像COM世界充满GUID一样,我们的package也用GUID来标识自己。这个文件用于定义这些GUID
VSPackage.resx 资源文件,保存我们package用到的字符串和图片

 

    向导也生成了一些“并不重要”的文件:

文件名 描述
AssemblyInfo.cs 定义程序集的信息
Package.ico 该package的图标
Resources.resx package级别的资源文件(初始的时候是空文件)
GlobalSupressions.cs Global static code analysis rule suppressions(译者注:用于取消报告特定的静态分析工具规则冲突)

 

测试这个Package

    如果是一个“Hello, world”程序的话,测试它是否正确是很简单的:只需要运行它使他显示信息即可。但是对于这个空的Package来说,只有一个地方可以证明这个Package注册成功了并且被IDE识别了:在“帮助|关于”菜单下,所有的packages都会被列出。如果运行我们的package(Ctrl+F5),将会启动Visual Studio 2008实验室(Microsoft Visual Studio 2008 Experimental hive),通过点击“帮助|关于”菜单,就可以看到我们的package:

image

 

它是如何工作的?

    现在是时候去查看这些代码并弄清楚我们的package是如何工作的了。为了使代码的可读性更好,我将忽略掉注释和不重要的部分。

    让我们从Guid.cs文件开始:

   1: using System;  
   2: namespace MyCompany.EmptyPackage
   3: {
   4:   static class GuidList
   5:   {
   6:     public const string guidEmptyPackagePkgString = "223643a0-af7c-4741-99df-e9641691af50";
   7:  
   8:     public const string guidEmptyPackageCmdSetString = "cecdc1f1-2fb2-40e4-88e8-ae8c85287a7c";
   9:  
  10:     public static readonly Guid guidEmptyPackageCmdSet = new Guid(guidEmptyPackageCmdSetString);
  11:  
  12:   };
  13: }

    这个文件定义了GuidList类,该类负责定义我们的package用到的GUID。第一个字符串guidEmptyPackagePkgString是我们的package的GUID,第二个字符串是我们的package的命令集(Command Set)的标识。由于我们只是做一个空的package,并没有任何命令(Command),所以我们可以忽略掉第二个GUID。

    Package定义在EmptyPackagePackage.cs文件中:

   1: using System;
   2: using System.Diagnostics;
   3: using System.Globalization;
   4: using System.Runtime.InteropServices;
   5: using Microsoft.VisualStudio.Shell;  
   6: namespace MyCompany.EmptyPackage
   7: {
   8:   [PackageRegistration(UseManagedResourcesOnly = true)]
   9:   [DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]
  10:   [InstalledProductRegistration(false, "#110", "#112", "1.0", 
  11:     IconResourceID = 400)]
  12:   [ProvideLoadKey("Standard", "1.0", "EmptyPackage", "MyCompany", 1)]
  13:   [Guid(GuidList.guidEmptyPackagePkgString)]
  14:   public sealed class EmptyPackagePackage : Package
  15:   {
  16:     public EmptyPackagePackage()
  17:     {
  18:       Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, 
  19:         "Entering constructor for: {0}", this.ToString()));
  20:     } 
  21:     protected override void Initialize()
  22:     {
  23:       Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, 
  24:         "Entering Initialize() of: {0}", this.ToString()));
  25:         base.Initialize();  
  26:     }
  27:   }
  28: }

    EmptyPackagePackage类定义上面有一些属性(Attribute),并且继承于Package抽象类,Package抽象类实现了IVsPackage接口。只要一个类实现了IVsPackage接口并注册到Visual Studio shell中,这个类就是一个package。标注在EmptyPackagePackage类上面的属性(Attribute)描述了怎样去注册package:

属性 描述
PackageRegistration

    regpkg.exe命令发现到类定义中有PackageRegistration这个Attribute时,会把该类当作一个package。例如把这个Attribute加到我们的类定义上面,regpkg.exe就会把我们的EmptyPackagePackage类当作一个package,并且根据该类上面含有的其他Attribute来注册我们的类。另外,在我们的例子中,我们把PackageRegistrationUseManagedResourcesOnly设成了true,这意味着我们的package中的所有资源都会定义在可管理的package中(managed package),而不是定义在卫星程序集里(statelite.dll)

DefaultRegistryRoot

    VS提供了一个简单的方法去开发和调试Visual Studio组件:在运行devenv.exe(也就是VS IDE)时,可以指定一个注册表的根。当我们在调试模式下运行我们的VS组件时,我们的组件实际上会运行在Visual Studio实验室下(Microsoft Visual Studio 2008 Experimental hive)。实验室模式下的VS和我们平时的开发环境应用了不同的设置。(译者注:有两种方式启动Visual Studio实验室,1、在开发package的VS IDE点击调试/开始执行或Ctrl+F5。2、通过开始-》所有程序-》Microsoft Visual Studio 2008 SDK-》Start Microsoft Visual Studio 2008 SP1 under Experimental hive)

    当我们在VS中执行“开始调试”时(译者注:应该是利用VS进行编译时),VS会执行regpkg.exe命令,并且为该命令指定参数,以便注册我们的package到VS实验室环境中。
    如果regpkg.exe命令在运行时并没有指定参数,那么就会用到DefaultRegistryRoot属性里指定的注册表的根。在我们的例子中,我们指定了“普通”的VS 2008 IDE用到的注册表根。

(译者注:利用VS进行编译时,查看输出窗口,可以看到有这么一条命令:RegPkg.exe /root:Software\Microsoft\VisualStudio\9.0Exp /ranu /codebase "路径\EmptyPackage\bin\Debug\EmptyPackage.dll",在这条命令里,通过/root开关指定了注册表的根为9.0Exp,也就是说我们通过DefaultRegistryRoot指定的根并没有用到。所以在通过VS进行编译时,会把package注册到Experimental hive中。原文作者的意思是如果不指定RegPkg命令的/root开关的话,就会用到DefaultRegistryRoot指定的注册表根)

InstalledProductRegistration

    这个Attribute提供的信息会显示在VS IDE的“帮助|关于”对话框里。它的构造函数需要四个参数:

    --第一个参数是false,表示我们并不提供自己的界面去显示package信息。

    --第二和第三个参数分别表示package的名字和描述。字符“#”表明名字和描述的值需要在资源文件中读出,资源名就是#号后面的ID。

    --第四个参数“1.0”是产品ID(版本号)

    --第五个参数(IconResourceID)代表package的图标。

    资源(名字、描述和图标)定义在VSPackage.resx文件中。

ProvideLoadKey

    每一个VS组件都应该用所谓的package load key(PLK)进行签名,Visual Studio用PLK去检查package的合法性。不过,如果你安装了Visual studio SDK的话,会安装一个VSIP的许可证,通过它,package可以在没有PLK的情况下运行。

    但是,我们的package的最终用户很可能没有VSIP的许可证,所以我们需要PLK。ProvideLoadKey属性用于定义PLK和生成PLK的基础信息。通过比较基础信息和PLK是否一致,VS可以验证package并决定是否允许package加载运行。

    ProvideLoadKey属性构造函数的前4个参数分别表示如下含义:

    --预计的最小版本

    --产品(Package)的版本号

    --产品(Package)的名字

    --公司名称(所有者/开发者)

    第5个参数是资源文件中定义PLK的资源ID。

(译者注:PLK需要到微软网站上http://msdn.microsoft.com/en-us/vsx/cc655795.aspx去申请)

Guid

这个Attribute定义了我们package的GUID。GUID是我们package的唯一标识,被用作COM注册、在IDE里得到我们package的引用,等等。

 

    对于定义一个空的package来说,这些Attribute已经足够了。为了使package正常工作,必须初始化它。有两个地方可以放置初始化代码:

    — package类的构造函数可以初始化任何不需要放到VS IDE中的东西。当package的构造函数执行的时候,package虽然已经被实例化了,但是还没有和VS IDE关联起来。所以在构造函数里,我们不能访问到VS IDE的service和VS IDE的对象。

    — 当我们的package实例和VS IDE关联起来的时候,VS会调用Package类的虚方法Initialize。我们可以重写这个方法,并且在这个方法里去初始化任何需要访问到VS IDE的service的对象。

总结

    我们创建了一个最小功能的和VS IDE集成的VS package(并且证明了它可以在关于对话框中显示)。这个package继承了Package类,Package类实现了IVsPackage接口。同时,我们为这个package类加了一下Attribute的标记,这些Attribute被regpkg.exe命令用来注册package。

    下一步,是时候为我们的package增加一些实用的功能了。

 

原文链接:http://dotneteers.net/blogs/divedeeper/archive/2008/01/03/LernVSXNowPart2.aspx

posted @ 2010-02-26 22:19  明年我18  阅读(4106)  评论(6编辑  收藏