最近利用空闲时间学习了VSPackage,于是萌发了在IDE中扩展一个模板代码生成工具的想法。以下是学习中的一些笔记。
要在IDE的服务器资源管理器中扩展一个右键菜单真是一个非常蛋疼的事,不过我还是成功将想要的菜单扩展了出来,效果如下图:
虽然其中困难重重,但这篇博客"VS2013在右键菜单添加命令插件开发 "给了我极大帮助,博文中标题三:“如何获取目标菜单的guid和id值”中的介绍是解决问题的关键。根据文中方法,我取得了服务器资源管理中表右键菜单Guid:{D4F02A6A-C5AE-4BF2-938D-F1625BDCA0E2}。如下图:
有了该Guid一切将迎刃而解,当然,期间将自定义菜单放到这个Guid对应的菜单中还是碰到了不少的麻烦。但不必细说,以下为扩展右键菜单的vsct配置:
<?xml version="1.0" encoding="utf-8"?> <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <Extern href="stdidcmd.h"/> <Extern href="vsshlids.h"/> <Commands package="guidMyVSPackagePkg"> <Groups> <!--<Group guid="guidMyVSPackageCmdSet" id="myTableRightClickMenuMasterGroup" priority="0x0600"> <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/> </Group>--> <!--服务器资源管理器表右键菜单自定义组,该组独立于表右键菜单其他组并在菜单顶部显示--> <Group guid="guidMyVSPackageCmdSet" id="myTableRightClickMenuMasterGroup" priority="250"> <!--<Parent guid="guidEditorRightClickMenuCmdSet" id="editorRightClickMenu"/>--><!--解决方案资源管理器文件右键菜单,下同--> <!--<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>--> <!--该组位于服务器资源管理器表右键菜单--> <Parent guid="guidServerExplorerMenuCmdSet" id="serverExplorerTableRightClickMenu"/> </Group> <!--"对象模型"菜单中的组--> <Group guid="guidMyTableRightClickMenu" id="myTableRightClickMenuGroup" priority="0x0600"> <!--该组位于"对象模型"菜单中--> <Parent guid="guidMyTableRightClickMenu" id="myTableRightClickMenu"/> </Group> </Groups> <Buttons> <Button guid="guidMyVSPackageCmdSet" id="cmdidMyCommand" priority="0x0100" type="Button"> <!--该按钮位于"对象模型"菜单组中,值得注意的是自定义菜单中无子菜单或按钮时是不会显示出来的--> <Parent guid="guidMyTableRightClickMenu" id="myTableRightClickMenuGroup" /> <Icon guid="guidImages" id="bmpPic1" /> <Strings> <ButtonText>模板代码生成</ButtonText> </Strings> </Button> </Buttons> <Menus> <!--服务器资源管理器表自定义右键菜单--> <Menu guid="guidMyTableRightClickMenu" id="myTableRightClickMenu" priority="0x700" type="Menu"> <!--该菜单位于服务器资源管理器表右键菜单自定义组中,自定义菜单或者命令貌似无法脱离组而独立存在--> <Parent guid="guidMyVSPackageCmdSet" id="myTableRightClickMenuMasterGroup" /> <Strings> <ButtonText>对象模型</ButtonText> <CommandName>对象模型</CommandName> </Strings> </Menu> </Menus> <Bitmaps> <Bitmap guid="guidImages" href="Resources\Images.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/> </Bitmaps> </Commands> <Symbols> <!-- This is the package guid. --> <GuidSymbol name="guidMyVSPackagePkg" value="{27b31d67-797a-4b04-98da-b66c930919b8}"/> <!--{74D21310-2AEE-11D1-8BFB-00A0C90F26F7}1283服务器资源管理器右键菜单--> <!--{D309F791-903F-11D0-9EFC-00A0C911004F}1072解决方案资源管理器文件右键菜单--> <!--服务器资源管理器表右键菜单,该Guid从注册表中找到--> <GuidSymbol name="guidServerExplorerMenuCmdSet" value="{D4F02A6A-C5AE-4BF2-938D-F1625BDCA0E2}"> <IDSymbol name="serverExplorerTableRightClickMenu" value="33280" /> </GuidSymbol> <!-- This is the guid used to group the menu commands together --> <GuidSymbol name="guidMyVSPackageCmdSet" value="{fc41e6d9-9414-4cda-92a8-0d2f39215247}"> <!--服务器资源管理器表右键菜单自定义组--> <IDSymbol name="myTableRightClickMenuMasterGroup" value="0x1020" /> <!--定义"模板代码生成"按钮--> <IDSymbol name="cmdidMyCommand" value="0x0100" /> </GuidSymbol> <!--在服务器资源管理器表右键菜单中扩展自己的菜单,此处Guid可随意生成--> <GuidSymbol name="guidMyTableRightClickMenu" value="{87325143-3929-42B9-A6C2-9B61DC72879B}"> <!--"对象模型"菜单--> <IDSymbol name="myTableRightClickMenu" value="0x1000" /> <!--"对象模型"菜单组--> <IDSymbol name="myTableRightClickMenuGroup" value="0x1001" /> </GuidSymbol> <!--代码编辑器右键编辑菜单--> <GuidSymbol name="guidEditorRightClickMenuCmdSet" value="{D309f791-903F-11D0-9EFC-00A0C911004F}"> <IDSymbol name="editorRightClickMenu" value="1037" /> </GuidSymbol> <GuidSymbol name="guidImages" value="{67d162b3-9cbc-4e1f-b6b1-48ac6206b50b}" > <IDSymbol name="bmpPic1" value="1" /> <IDSymbol name="bmpPic2" value="2" /> <IDSymbol name="bmpPicSearch" value="3" /> <IDSymbol name="bmpPicX" value="4" /> <IDSymbol name="bmpPicArrows" value="5" /> <IDSymbol name="bmpPicStrikethrough" value="6" /> </GuidSymbol> </Symbols> </CommandTable>
这个配置文件中,我把原来自动生成的注释都去掉了,这样看起来应该会调理一些。估计新手在看到这个配置文件的时候可能有点蒙,以下问配置文件的截图解释:
先看Symbols节点:
再看Commands节点:
可以看得出来,Commands节点是对Symbols具体实现,这个地方之所以说得那么详细是因为我在接触这个的时候一头雾水,虽然网上有很多解说,但是都不够简单和直接,于是就花了这么大篇幅来介绍,希望对后来者有所帮助。至此,服务器资源管理器表右键菜单(下称表右键菜单)被成功扩展了出来,接下来就是更蛋疼的了。
要在表右键菜单中连接数据库,首先要做的当然是获取选中的表名了。鉴于扩展表右键菜单这种想法可能比较奇葩,所以这方面的介绍网上少之又少,更别说要取右键菜单弹出时被选中的表名。翻遍了百度找到的只有MSDN里边的介绍,无奈之下,只能直接到MSDN里边查找了。考虑到自动生成模板实例中有这样一段代码:
DTE dte = (DTE)GetService(typeof(DTE));
那么我设想,所有的扩展是不是都应该从DTE展开呢?于是打开了DTE命名空间下的API EnvDTE 命名空间,开始盲目寻找,因为受到实例代码:
IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
的启发(这句代码在很多博文中有露脸),我要找的目标应该属于接口,于是缩小了查找范围。终于EnvDTE 的接口快要被我浏览完的时候,我发现如下介绍:
众里寻他千百度,原来是层级结构UIHierarchy,果断点进去看,内容如下:
那么接下来就好办了,获取UIHierarchy的代码是VB写的:
Dim UIH As UIHierarchy = _ DTE.Windows.Item(Constants.vsWindowKindMacroExplorer).Object
很轻易的将这句代码转换成了C#,如下:
DTE dte = (DTE)GetService(typeof(DTE)); var serverExplorar = dte.Windows.Item(EnvDTE.Constants.vsWindowKindServerExplorer).Object as UIHierarchy;
从另外一个地方看到,可以这样更方便的获取服务器资源管理器的层级结构:
var dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.11.0"); var serverExplorar = dte2.ToolWindows.GetToolWindow("服务器资源管理器") as UIHierarchy;
那么在"模板代码生成"按钮的事件(如何绑定扩展的按钮事件,请阅读其他文章或参照自动生成的代码示例)中获取右键选中表名的代码片段如下:
DTE dte = (DTE)GetService(typeof(DTE)); var serverExplorar = dte.Windows.Item(EnvDTE.Constants.vsWindowKindServerExplorer).Object as UIHierarchy; var selectedItems = serverExplorar.SelectedItems as object[]; var selectedUih = selectedItems[0] as UIHierarchyItem;
好了,表名也知道了,怎么连接数据库呢?从IDE全局配置文件中读取?在IDE安装目录中找了很长时间没有找到相关配置文件。那是不是IDE自身允许我读取服务器资源管理器中已经配置好的数据连接字符串甚至数据库连接也就是Connection呢?基于这样的想法再次在MSDN中翻来覆去的找,怎么找到关键代码的就不絮叨了,跟前文所述差不多就是了。在历尽千辛万苦,万苦千辛的情况下,找到关键性的接口“IVsDataExplorerConnectionManager”,该接口位于命名空间Microsoft.VisualStudio.Data.Services中,可以在添加引用的程序集-扩展中引用到,代码片段如下:
var conectionService = (IVsDataExplorerConnectionManager)GetService(typeof(IVsDataExplorerConnectionManager));
另GetService方法可直接在按钮绑定事件中的命名空间下直接调用,不必太在意。
conectionService里面就有你配置的所有数据库配置信息、数据库连接等。在这个地方想留个悬念,如果服务器资源管理器配置了多个数据库信息的话,怎么获取表右键菜单选中的表对应的数据库信息呢?
在我的博客文章中,我不喜欢直接将完整代码贴出,只是希望通过引导后来者的方式,使后来者能更容易解决难题,期望阅读的朋友谅解。