本文主要:如何开发一个 visual Studio 扩展,其实扩展也叫插件。

那么就是如何开发一个 vs插件。

我写这博客时候,是我在开发一个插件:编码规范工具。记录的是我从不知道到发布插件,如果遇到了开发中的问题,欢迎交流。

安装 Visual Studio SDK

首先需要安装 Visual Studio SDK ,安装不需要其它的工具就可以,直接使用vs安装包。

我的是 Visual Studio 2015 ,所以我到这个页面:https://msdn.microsoft.com/en-us/library/bb166441 看教程。

垃圾wr(我说的就是微软),找个东西好难

首先是需要安装 SDK ,如果一开始没有安装的话,那么在控制面板,找到 vS 右击,修改 VS ,然后选择工具安装。

选择修改,然后选最后一个工具

我的是中文,可能翻译不一样,不过相信这一点压力对大家没有什么。

然后就来说下如何做插件,主要教程是看: http://dotneteers.net/blogs/divedeeper/archive/2008/01/06/LearnVSXNowPart3.aspx ,除了国外的还有国内的大神翻译:http://www.cnblogs.com/default/archive/2010/02/27/1674832.html 这里是一系列。

那么我将会来说下使用一个简单的方法去做一个 Command ,功能是可以判断 VS 工程的所有文件编码。

首先是新建一个插件项目。打开 vs ,新建一个 VSIXProject

新建之后居然发现有一个 index.html 我开始还以为是 写html 来着,还好不是,这个 index.html 只是卖萌的而已。

添加菜单

那么新建完 VSIXProject 我们就开始编写按钮,虽然说是按钮,其实是菜单,在这里,全部的按钮都是和菜单一样。

那么我们直接新建 Command ,注意他的位置是在哪。

新建出来可以看到多了好多文件,其中 *.vsct 是核心,如果想知道关于他更多,请去中文博客:http://www.cnblogs.com/default/archive/2010/06/28/1766451.html

我先放出做出了的菜单。

首先打开 *.vsct 在 Symbols 添加 id ,我们添加 EncodingNormalizerMenu ,EncodingNormalizerId2,他们的值随意给。关于这个 GUID 或者其它的,其实我也不懂。

    <GuidSymbol name="guidEncodingNormalizerPackageCmdSet" value="{0640f5ce-e6bc-43ba-b45e-497d70819a20}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="EncodingNormalizerId" value="0x0100" /> <!--添加 EncodingNormalizerMenu ,EncodingNormalizerId2-->
<IDSymbol name="EncodingNormalizerMenu" value="0x1021" />
<IDSymbol name="EncodingNormalizerId2" value="0x0101" /> </GuidSymbol>

然后是创建菜单 在<Command>下面使用<Menus>

    <Menus>
<Menu guid="guidEncodingNormalizerPackageCmdSet" id="EncodingNormalizerMenu"
type="Menu" priority="0x100">
<!--注意这个id 和 type-->
<Parent guid="guidSHLMainMenu" id="IDG_VS_MM_BUILDDEBUGRUN" />
<!--这里的id是说他在哪-->
<Strings>
<!--按钮显示的字-->
<ButtonText>规范编码</ButtonText>
<!--命令名-->
<CommandName>EncodingNormalizer</CommandName>
</Strings>
</Menu>
</Menus>

然后修改 Groups ,修改 Parent 的 guid 为 Menu 的,修改 id 为 Menu 的。不一样就不在一个菜单。

      <Groups>
<Group guid="guidEncodingNormalizerPackageCmdSet" id="MyMenuGroup" priority="0x0600">
<Parent guid="guidEncodingNormalizerPackageCmdSet" id="EncodingNormalizerMenu"/>
</Group>
</Groups>

然后添加按钮,注意按钮需要 id 、priority、CommandName 和之前默认的不一样。

添加了按钮,我们需要添加事件,在 .cs 构造

我们使用 CommandID 绑定,我们需要知道 按钮在哪个组,我们直接使用 CommandSet 还需要知道 按钮的 id ,第一个按钮是 0x0100 就是CommandId,第二个是 0x0101 ,于是就写 new CommandID(CommandSet, 0x0101) 使用第二个按钮

那么使用了按钮,我们需要关联事件使用,我的 MenuItemCallback 事件作为按钮点击使用函数。

                  menuItem = new MenuCommand(this.MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);

代码全部在全球同性交友平台,上面写的代码在 EncodingNormalizerVsx/EncodingNormalizer.cs

接着就是需要加上文件编码检查,在我之前写的 C# 判断文件编码 博客有说道如何检测文件编码。

增加选项

我们需要保存一些设置,那么如何自定义配置的界面,把配置页面放在工具->选项,可以参见 http://www.cnblogs.com/winkingzhang 提供的方法,我使用了他的方法,很简单。还有垃圾wr的方法 https://github.com/Microsoft/VSSDK-Extensibility-Samples/blob/646de671c1a65ca49e9fce397baefe217e9123e8/Options_Page/Readme.md

首先打开 *Package.cs ,不过我们需要新建一个类 DefinitionPage ,不需要在这个类写什么。

    public class DefinitionPage
{ }

然后添加 ProvideProfile ,需要写类型、分类名称、页面名称、资源ID。关于 ProvideProfile 可以去:https://msdn.microsoft.com/zh-cn/library/microsoft.visualstudio.shell.provideprofileattribute.aspx

  [ProvideProfile(typeof(DefinitionPage), "PowerExtension", "DefinitionPage",101,104,true)]
[ProvideOptionPage(typeof(DefinitionPage), "PowerExtension", "DefinitionPage", 101, 104, true)]

PowerExtension 就是显示在选项的一级菜单,DefinitionPage就是二级菜单。

我这里把 PowerExtension 改为 EncodingNormalizer

我们选项不需要复杂的,只需要使用默认的,于是我们添加属性 CriterionEncoding

添加时候,需要让 DefinitionPage 使用 DialogPage

    [ClassInterface(ClassInterfaceType.AutoDual)]
[Guid(GuidStrings.GuidDefinitionPage)]
public class DefinitionPage : DialogPage

GuidStrings.GuidDefinitionPage) 是我自己定义的GUID放在一起的类,实际没有用

需要让属性知道设置的标题,点击显示的 Description ,于是我就写下面代码

        /// <summary>
/// 规范格式
/// </summary>
[Category("规定编码")]
[Description("规定的编码,如果是ascii 那么无论是选择utf8或GBK都判断为对")]
public Encoding CriterionEncoding { set; get; }

如果需要复杂窗体,新建一个 用户控件,注意是 WinForm 控件,然后 override Window。

假如我有一个控件 View.DefinitionPage ,那么我可以使用

        protected override IWin32Window Window
{
get
{
if (_definitionPage == null)
{
_definitionPage = new View.DefinitionPage();
}
_definitionPage.Owner = this;
_definitionPage.InitializeLifetimeService();
return _definitionPage;
}
}
private View.DefinitionPage _definitionPage;

那么如何读取 vs扩展配置 ?

vsx的配置保存使用上面的方法,就自动保存配置注册表,于是如何去读,就是问题。

读取 vs插件 配置的方法是在 *Package.cs 写静态的 Ensure*Package

        public static EncodingNormalizerPackage EnsureEncodingNormalizerPackage()
{
IVsShell shell = Package.GetGlobalService(typeof(SVsShell)) as IVsShell;
if (shell != null)
{
IVsPackage package;
Guid guid = new Guid(EncodingNormalizerPackage.PackageGuidString);
if (ErrorHandler.Succeeded(shell.IsPackageLoaded(ref guid, out package)))
{
return package as EncodingNormalizerPackage;
}
if (ErrorHandler.Succeeded(shell.LoadPackage(ref guid, out package)))
{
return package as EncodingNormalizerPackage;
}
}
return null;
}

EncodingNormalizerPackage 是我的类,大家替换他为你的类,然后拿到了EncodingNormalizerPackage还需要拿到他的页面属性

DefinitionPage 就是我上面定义的选项

        public static DefinitionPage DefinitionPage()
{
EncodingNormalizerPackage package = EnsureEncodingNormalizerPackage();
return package?.GetDialogPage(typeof(DefinitionPage)) as DefinitionPage;
}

那么在上面定义的 CriterionEncoding 也可以拿到,这样就可以读配置了。

修改 CriterionEncoding 关闭 vs,可以看到下次打开值还在,他是写在注册表。

我发现写注册表对于List的和一些类型都不好,于是我用了写文件,写文件可以写在用户文档。

System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) 获取用户文档,在里面新建文件夹,然后就可以写入读取。

如果需要做方法去和 Resharper 的注册等差不多的页面,那么可以使用 Window 弹出。

首先创建一个 UserControl 然后写一个复杂的界面,假如我写了一个叫 ConformPage 的控件,那么我如何去显示他。可以使用下面代码。

            System.Windows.Window window = new System.Windows.Window();
ConformPage conformPage = new ConformPage();
window.Content = conformPage;
window.Title = "编码规范工具";
window.Show();

从零做一个扩展,可以参见:http://www.cnblogs.com/stg609/p/3711443.html

传到商店

做完了功能,我们需要发布扩展,我们要把按钮加上好看的 ico 。

首先,下载一张图。

把图片改为 32bits 16x16。修改图片可以使用 http://www.easyicon.net/ 在线转换。

图片放到工程,我放在 Resource ,我给他名称 code_711px_16_easyicon.net.png

打开 *.vsct ,在 Symbols 添加 一个GUID。这需要给他名称和ID

      <GuidSymbol name="EncodingNormalizerIdICO" value="{B1E160E6-CC24-4FD5-857F-69F45DCEE27B}"
>
<IDSymbol name="bmpPic1" value="1" />
</GuidSymbol>

然后在 Bitmaps 添加 Bitmap ,他的 guid 就是我们上面 EncodingNormalizerIdICO ,href 是图片所在文件夹,usedList 写 IDSymbol 。

        <Bitmap guid="EncodingNormalizerIdICO" href="Resources\code_711px_16_easyicon.net.png" usedList="bmpPic1"/>

然后在我们需要的按钮 Icon 的 guid 写 guid,在 id 写 IDSymbol 。

这样就好啦。

如果发现有自己做的和我讲的不同,那么一定是有一点我没说道,去我的github看我做的。

图片可以把多个图片放在一起,于是按照一个图片 16x16 读取,这就是多个IDSymbol做的。

获取工程所有项目

我需要获取用户工程的所有项目,我开始使用dte.Solution.Projects但是放在文件夹的项目获取不到,所以使用堆栈提供的方法。

这个方法写在C# 解析 sln 文件 可是 vs 说找到不 Microsoft.Build.dll 所以这个方法还是不可以的。那么如何从 dte 获取所有项目?我找到一个大神博客:http://www.wwwlicious.com/2011/03/29/envdte-getting-all-projects-html/

开始判断是不是文件夹,如果是的话,递归函数获取文件夹所有项目。

我以为文件夹不是 Project 但是后来发现,工程的文件夹也是 Project 文件夹可以通过ProjectKinds.vsProjectKindSolutionFolder判断。

那么如何获得 文件夹所有文件夹和项目,其实 Project 有 ProjectItems 可以获取。

于是可以使用这个方法

        private static List<Project> GetSolutionFolderProjects(Project solutionFolder)
{
List<Project> list = new List<Project>();
for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++)
{
var subProject = solutionFolder.ProjectItems.Item(i).SubProject;
if (subProject == null)
{
continue;
} // If this is another solution folder, do a recursive call, otherwise add
if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
list.AddRange(GetSolutionFolderProjects(subProject));
}
else
{
list.Add(subProject);
}
} return list;
}

一开始判断是不是文件夹,如果是就使用 GetSolutionFolderProjects 得到所有的项目,这样就可以获得工程所有项目。

  foreach (var temp in dte.Solution.Projects)
{
if (temp is Project)
{
if (((Project) temp).Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
project.AddRange( GetSolutionFolderProjects((Project) temp));
}
else
{
project.Add((Project)temp);
}
} }

代码:https://gist.github.com/lindexi/3105bd0f0c5225bec4aa476f84dd29db

升级 2017

如果有之前扩展需要升级,参见https://docs.microsoft.com/en-us/visualstudio/extensibility/how-to-migrate-extensibility-projects-to-visual-studio-2017

  1. 安装 vs2017 需要添加扩展

    关于vs2017 可以到我网盘下载,参见:http://lindexi.oschina.io/lindexi/post/C-7.0/

  2. 打开 Nuget 升级,把所有提示升级的都升级。

  3. 打开 source.extension.vsixmanifest

    选 InstallationTarget 包括各版本

  4. 打开属性,修改路径

    启动外部程序C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\devenv.exe

  5. 启动项目

  6. 把插件共享 https://visualstudiogallery.msdn.microsoft.com/site/upload/view

参见:http://blog.csdn.net/liuruxin/article/details/17955363

https://github.com/Microsoft/VSSDK-Extensibility-Samples

https://msdn.microsoft.com/zh-cn/library/cc138589

https://msdn.microsoft.com/zh-cn/library/dn705845

https://msdn.microsoft.com/zh-cn/library/mt683786

代码:https://github.com/lindexi/EncodingNormalior

如果开发中遇到问题,欢迎联系 lindexi_gd@163.com

现在我还有另一个插件:图片注释 这个插件不是我写的,我是修改的,所以没有发布,如果需要就在这里下。


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

VisualStudio 扩展开发的更多相关文章

  1. VisualStudio 扩展开发 获得输出窗口内容

    本文告诉大家如何拿到 VisualStudio 输出窗口的内容 在上一篇告诉大家如何开发添加菜单 点击的时候可以使用方法,如果需要拿到 VisualStudio 的输出窗口的内容,如想要开发一个插件, ...

  2. 利用Visual Studio 2017的扩展开发(VSIX、ItemTemplate) 快速实现项目的半自动化搭建

    目录 0.引言 1.什么是Visual Studio项目模板 2.IWizad接口 3.通过Visual Studio扩展开发实现领域驱动开发 3.1 使用VSIX+ProjectTemplate创建 ...

  3. iOS开发系列--App扩展开发

    概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...

  4. PHP 扩展开发(将自己的一些代码封装成PHP扩展函数)

    今天时间不多,先给个地址,能搜到我这篇blog的朋友先看看我最近在看的一些文章.资料吧: 我的环境是 lnmp1.1 的 (LNMP一键安装包),所以要进行PHP扩展开发首先应该对环境配置和shell ...

  5. 关于PHP扩展开发(收藏)

    一.Linux shell命令: ls –lh    查看文件大小 du –a    查看文件及文件夹大小 -------------------------- nginx ------------- ...

  6. postgres扩展开发

    扩展开发的基本组成 demo--1.0.sql demo.c demo.control Makefile demo.c当中包含了自定义函数的实现,纯C语言,目录下可包含多个.c文件.demo-1.0. ...

  7. 【转发】NPAPI学习(Firefox和Chrome扩展开发 )

    NPAPI学习(Firefox和Chrome扩展开发 ) 2011-11-08 14:41:02 by [6yang], 1172 visits, 收藏 | 返回 Firefox和Chrome扩展开发 ...

  8. Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  9. PHP扩展开发相关总结

    1.线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, ...

随机推荐

  1. 201521123091 《Java程序设计》第1周学习总结

    Java 第一周总结 第一周的作业. 1.本章学习总结 [x] 初识Java语言:Java的历史,三大平台,了解JVM/JRE/JDK [x] Java开发环境的配置 [x] 用记事本编写Java程序 ...

  2. 201521123083《Java程序设计》第四周学习总结

    [toc] 1. 本周学习总结 尝试使用思维导图总结有关继承的知识点. 2. 书面作业 1.注释的应用 使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图) 在这里 ...

  3. 201521123072《java程序设计》第十二周学习总结

    201521123072<java程序设计>第十二周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象 ...

  4. 201521123044 《Java程序设计》第10周学习总结

    1. 本章学习总结 2. 书面作业 本次PTA作业题集异常丶多线程 1.finally题目4-2 1.1 截图你的提交结果 1.2 4-2中finally中捕获异常需要注意什么? 1.无论try-ca ...

  5. JAVA课程设计-----加减法测试博客

    1.团队成员介绍(一个人做的) 谢季努:网络1513 201521123079 2.项目git地址 3.项目git提交截图 4.项目运行截图 输入答案后点击确认就会出现本次的得分 如果觉得成绩不理想点 ...

  6. Akka(25): Stream:对接外部系统-Integration

    在现实应用中akka-stream往往需要集成其它的外部系统形成完整的应用.这些外部系统可能是akka系列系统或者其它类型的系统.所以,akka-stream必须提供一些函数和方法来实现与各种不同类型 ...

  7. C#设计模式(6)-原型模式

    引言 上一篇介绍了设计模式中的抽象工厂模式-C#设计模式(3)-建造者模式,本篇将介绍原型模式: 点击这里查看全部设计模式系列文章导航 原型模式简介 原型模式:用原型实例指定创建对象的种类,并且通过拷 ...

  8. Struts2第九篇【OGNL、valueStack详解】

    什么是OGNL表达式? OGNL是Object Graphic Navigation Language 是操作对象属性的开源表达式. Struts2框架使用OGNL作为默认的表达式语言. 为什么我们学 ...

  9. temp-重庆农商行二次出差

    1, 住宿(远舰商务酒店) 与胡仕川一起住   1722房间,  178-27=151(返现后). 7月30日   7月31日  8月1日 8月2日 8月3日 2, 住宿(郎菲酒店)一个人住, 158 ...

  10. springmvc04-文件上传-JSON数据

    文件上传部分: 1, 导入commons-fileupload-1.2.2.jar commons-io-2.4.jar 两个jar包. 2, 在主配置文件中,添加如下信息 <!-- 文件上传- ...