目录
环境准备
安装 Visual Studio extension development
如果安装 VS 时已选择了此功能,则跳过这一步。
Hello World
本节通过一个最简单的 Hello World 示例演示如何创建一个 VS 插件。
创建 VSIX 项目
在项目中添加一个 Command.
进入新添加的 Command 的 Execute 方法, 修改变量 message, 可改为任意测试字符串.
调试运行该项目(直接按 F5 即可),程序会自动启动一个试验版(experimental) Visual Studio。
在试验版的 Visual Studio 上,Tools 菜单下会出现 Invoke XxxxCommand(即新添加的 Command )。正常的(非试验版) Visual Studio 上不会有该菜单。
点击 Invoke XxxxCommand, 即显示上一步修改的测试字符串。
编译该项目即可生成一个 VS 插件 (.vsix 文件),双击即可安装,安装过程可能需要关闭所有 VS。具体安装过程略。
安装完成后, 打开 VS 即可看到新的菜单。
至此, VS 插件的 Hello World 即完成。
理解 VSIX 项目结构
Command
自定义菜单的命令,即点击菜单后会执行的操作逻辑都写在该文件中。
通常只需修改 Execute 方法内容,将插件的功能逻辑写在此处。
Package
插件包,可以包含多个 Command。
一个 VSIX 项目下可建多个 Package, 但是通常情况下,我们使用一个 Package 即可。
如果一个 VSIX 仅有一个 Package, 则添加 Command 时,VS 会自动将 Command 添加到 Package 下。
.vsct 文件
Visual Studio Command Table, xml 文件,包含所有自定义菜单配置。
vstc 文件包含如下 3 部分:
- 宏和预处理
- 命令
- GUIDs
宏和预处理
VSCT 编译器能使用 C++ 宏和预处理
从外部引入一些宏可以简化 vsct 的配置. 下面以 vsshlids.h 为例说明。
vsshlids.h 头文件位于 {VS安装目录}\VSSDK\VisualStudioIntegration\Common\Inc, 例如我的目录是 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VSSDK\VisualStudioIntegration\Common\Inc。
vsct 文件中有用到宏 IDM_VS_MENU_TOOLS, 它表示 VS 上的 Tools 菜单的ID,这个宏即位于 vsshlids.h 头文件中。
如果不引入该头文件,不使用宏 IDM_VS_MENU_TOOLS, 就得使用 0x0085 来表示 VS 上的 Tools 菜单, 这无疑会大大增加配置和维护的难度。
命令
这部分配置自定义的菜单:按钮文字,所属组,图标等等。
以 Hello World 示例的命令为例说明。
组的概念:
菜单中,以横线分割在一起的菜单属于一个组。
GUIDs
VSIX 项目配置中,几乎所有东西都通过 GUID 进行关联
以 Hello World 示例的命令为例说明。
实践
代码格式化
VS 本身已有代码格式化功能,本节实践只是为了练习在 VS 中添加自定义菜单
期望目标:在 VS Edit 菜单下增加一个子菜单,实现对当前选中的代码进行格式化。
基于 Hello Word Demo 程序上修改。
添加一个命令 FormatSelectionCommand
打开 .vsct 文件, 找到新添加的 FormatSelection 命令。
修改 Button 的 Parent, 使得 Button 位于 VS Edit 菜单下。并修改按钮文本。
注:宏 IDG_VS_EDIT_OBJECTS 位于 vsshlids.h 文件中。
运行程序,可以看到 Format Selection 菜单已加到 VS Edit 菜单下。
接下来,修改 FormatSelectionCommand 的 Execute 方法,实现代码进行格式化功能。
我们不需从 0 开始实现代码格式化功能,而是通过执行 VS 内置的命令的方式来实现。具体代码如下:
private async void Execute(object sender, EventArgs e)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var service = await this.ServiceProvider.GetServiceAsync<SVsCmdNameMapping, IVsCmdNameMapping>();
service.MapNameToGUIDID("Edit.FormatSelection", out Guid pguidCmdGroup, out uint pdwCmdID);
var cmdId = Convert.ToInt32(pdwCmdID);
var commandID = new CommandID(pguidCmdGroup, cmdId);
var commandService = await this.ServiceProvider.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
commandService.GlobalInvoke(commandID);
}
注:代码中用的 "Edit.FormatSelection" 来自:https://docs.microsoft.com/en-us/visualstudio/ide/reference/visual-studio-command-aliases?view=vs-2019
编译项目,生成 .vsix 文件。然后安装。效果如下图。
至此,VS 插件的实践完成。
完整代码附件:https://download.youkuaiyun.com/download/u013688451/18740986
尚未解决的问题
本人在实践过程中遇到一个问题,尚未找到原因。
上述代码格式化的功能,使用调试的方式启动的试验版 VS 上,Edit.FormatSelection 不能正常执行,但 Edit.FormatDocument 又能正常执行。
项目编译成 .vsix 文件后安装,Edit.FormatSelection 能正常执行。