Visual Studio 插件开发(一)—— 如何开始VSX开发

一、首先要做的

要利用VS SDK进行插件开发,你需要安装Visual Studio 2013 SDK (English) 。Visual Studio 2013 默认隐藏了其他项目类型下的扩展性项目,需要安装SDK以后才可以创建VS扩展项目,之后才可以创建VS的扩展性项目。

二、如何扩展VS IDE?

Visual Studio 提供了三种扩展其 IDE 的方式:
  • Visual Studio Shell Isolated:从Visual Studio 11开始,微软抛弃了通过宏来扩展VS IDE的方式,而加强了 Visual Studio Shell Isolated。Visual Studio Shell 使开发人员能够快速创建和分发自己的定制工具,它对于 Visual Studio services 具有完全的访问权限,并且支持定制化和品牌化。本系列中不打算涉及Shell的开发。
  • Visual Studio 外接程序:Add-ins具有更强大的功能来扩展 Visual Studio,因为它可以访问 Visual Studio 所有的对象模型并添加新的UI,例如工具窗口,选项页,菜单以及工具栏,这些添加的命令看起来就是IDE的一部分。Add-ins还可以访问由IDE自己以及其他Add-ins提供的服务。对于一些简单的功能来说,插件是最简单的开始方式。但在这个系列中,我并不打算关注如何开发Add-ins,不过Add-ins中那些用于VS Package的技术是非常有用的。
  • Visual Studio Package:毫无疑问VS扩展包是扩展 Visual Studio 最强大的工具。最直接的证据就是整个 Visual Studio 的功能就是建立在以 Visual Studio Shell 为核心的扩展包上的。从开发人员的角度来看,添加新的VS Package与微软添加VS IDE的核心功能是一样的。VS IDE并不会区别对待微软开发的Package和第三方Package。VS SDK提供了Package的安装以及注册工具:regpkg.exe。Visual Studio 通过所谓的PLK(Package load key)来检查一个扩展包是否合法,这个PLK可以从微软站点上获取到,它是你的Package的数字散列码。如果你的Package部署到了产品环境下,它的PLK会被检查,在开发和调试环境下不需要PLK。

三、如何开始?

现在,我们首先需要理解什么是VS Package,它怎么工作,它包含了哪些元素,我们可以从实践中开始:建立一个简单的扩展包,看看里面都有些什么。现在让我们来建立一个“Hello World”级别的VS Package。

打开 Visual Studio 2013,新建项目,在项目类型对话框中选择:“其他项目类型/扩展性/Visual Studio Package”,如下图:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第1张图片

如上图所示,我们把Framework的版本设置为4.5,项目名称设置为:EmptyPackage,点击确定后,会弹出Visual Studio Package Wizard向导。如下图:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第2张图片

点击Next,开始定义我们的Package:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第3张图片

如上图所示,我们选择 C# 作为开发语言,另外,由于VS Package需要被强命名,所以我们必须提供一个key文件来给我们的Package程序集进行签名,在这里我们利用向导帮我们自动生成一个key文件。点击Next,将在下一步中设置Package的基本信息。
Visual Studio 插件开发(一)—— 如何开始VSX开发_第4张图片

请根据上图填入响应的基本信息,下一步是设置VS Package的选项:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第5张图片

向导可以帮助我们创建一个菜单命令(menu command)、一个工具窗口(tool window)和一个自定义编辑器(custom editor),但是由于这次我们只是创建一个空的package,所以这里一个都不用勾选。点击Next后会转向最后一步,在这里我们可以为我们的package添加测试项目:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第6张图片

像上一步一样,为了创建一个最简单的package,在这里我们也不要勾选任何选项。点击Finish按钮,Visual Studio 会在几分钟之内帮我们创建该package项目。成功创建项目后,在解决方案管理器中,我们将看到下面的结构:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第7张图片

可以看到,在项目中引用了很多interop assembly,这些程序集帮助我们与VS IDE中的COM对象交互,并提供package需要的service。

四、Package的文件

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

向导也生成了一些“并不重要”的文件:
文件名 描述
AssemblyInfo.cs 定义程序集的信息
Package.ico 该package的图标
Resources.resx package级别的资源文件(初始的时候是空文件)
GlobalSuppressions.cs 用于取消报告特定的静态分析工具规则冲突

五、测试这个Package

如果是一个“Hello World”程序的话,测试它是很简单的:只需要运行程序即可。但是对于一个Package来说,只有一个地方可以证明这个Package注册成功并被IDE识别了:在“帮助|关于”菜单下,所有的Package都会被列出。如果运行我们的Package,将会启动 Visual Studio 2013 实验实例,通过点击“帮助|关于”菜单,就可以看到我们的Package:
Visual Studio 插件开发(一)—— 如何开始VSX开发_第8张图片

六、它是如何工作的

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

namespace Company.EmptyPackage
{
    static class GuidList
    {
        public const string guidEmptyPackagePkgString = "c1356456-0e16-46db-a7d5-f9db67f9e009";
        public const string guidEmptyPackageCmdSetString = "70963f97-610a-44d8-868e-cb281146f947";

        public static readonly Guid guidEmptyPackageCmdSet = new Guid(guidEmptyPackageCmdSetString);
    };
}

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

Package定义在EmptyPackagePackage.cs文件中:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;

namespace Company.EmptyPackage
{
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [Guid(GuidList.guidEmptyPackagePkgString)]
    public sealed class EmptyPackagePackage : Package
    {
        public EmptyPackagePackage()
        {
            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        }
        
        protected override void Initialize()
        {
            Debug.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

        }
        #endregion

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


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

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

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

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

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

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

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 IDS Service的对象
下一篇文章将为我们的Package添加一些实用的功能。

你可能感兴趣的:(C#,Visual,Studio,扩展)