VS.NET 2003集成环境插件开发指南
转载请注明出处:http://www.cnblogs.com/dragon
一、 简介――Visual Studio.NET插件能做什么?
Visual Studio.NET插件能做很多事情,例如:
1、 编写如CodeRush一样的开发环境代码辅助工具
2、 编写如CodeSmith这样的代码模板工具
3、 编写代码生成器,根据自定义的一些条件自动生成代码。如现在比较流行的一些代码生成工具,如果和开发环境集成,使用起来应该会更加方便。
4、 编写如DataSetPryer这样的调试工具,可以在调试时查看DataSet的内容。
5、 甚至还可以在VS.NET里集成Google搜索引擎,或将MSN集成到VS.NET。
这里不再一一列举,总而言之,凡是可以和Visual Studio.NET开发环境相关的,都能以插件的形式进行。
开发VS.NET插件,目前有两种形式:一是利用VS向导生成的VS外接程序;二是利用微软的VSIP开发包(Visual Studio Industry Partner:微软合作伙伴计划)。本文讨论的是第一种方法。
二、 程序框架概述
在Visual Studio.NET中选择”新建项目à其他项目à扩展性项目àVisual Studio.NET外接程序”,按照向导生成代码,最后会生成两个工程文件,一个是外接程序项目,一个是外接程序安装项目。可以在外接程序项目里看到生成的项目文件中有个connect.cs文件,该文件有以下几个部分:
1、 类的继承接口及其常量定义
[GuidAttribute("952A6CFF-8516-4DA0-B0BA-519CB9614525"), ProgId("STDTools.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2, IDTCommandTarget
{…}
Connect类主要从两个接口继承,一个是Extensibility.IDTExtensibility2接口,该接口主要定义了下面几个方法:
OnAddInsUpdate 方法:在环境中加载或卸载外接程序时发生。
OnBeginShutdown 方法:正在关闭环境时发生。
OnConnection 方法:将外接程序加载到环境中时发生。
OnDisconnection 方法:当从环境中卸载外接程序时发生。
OnStartupComplete 方法:环境启动完毕时发生。
IDTCommandTarget接口则定义了以下两个方法
Exec 方法:在VS开发环境中选择了某个外接菜单命令时被VS环境所调用。
QueryStatus方法:当VS环境要显示外接菜单时调用该方法查询菜单的状态。
该方法返回指定的已命名命令的当前状态,无论此命令是启用、禁用还是隐藏
2、 OnConnection()函数:
本事件处理函数是在插件被加载时发生,一般用于做一些初始化工作,如创建菜单等。该函数的传入参数如下:
object application:定义了IDE自动化对象
Extensibility.ext_ConnectMode connectMode:连接模式,指明了插件当前的连接模式
ext_cm_AfterStartup 外接程序是在应用程序启动后加载的,或是通过将相应 AddIn 对象的 Connect 属性设置为 True 加载的。
ext_cm_Startup 外接程序是在启动时加载的。
ext_cm_UISetup 外接程序自安装后首次被启动。
object addInInst:表示外接程序实例的 AddIn 对象。
ref System.Array custom: 一个 Variant 数组,可以用来提供附加数据,一般不太常用。
3、 OnDisconnection()函数:系统卸载插件时被调用
本事件处理函数是在插件被卸载时发生,其传入参数如下
Extensibility.ext_DisconnectMode disconnectMode:
ext_dm_HostShutdown:外接程序是在开发环境关闭时卸载的。
ext_dm_UserClosed:外接程序是在用户清除“外接程序管理器”对话框中该外接程序的复选框时卸载的
ext_dm_UISetupComplete:外接程序是在环境安装完成后和在 OnConnection 方法返回后卸载的。
ref System.Array custom:
4、 QueryStatus()函数:系统查询菜单状态
该方法有四个传入参数
CmdName 要检查的命令的名称。
NeededText
一个 vsCommandStatusTextWanted 常数,指定是否返回检查信息,如果返回,还指定返回信息的类型。
vsCommandStatusTextWantedNone不返回信息。
vsCommandStatusTextWantedName返回命令名。
vsCommandStatusTextWantedStatus返回命令状态。
StatusOption
一个指定命令的当前状态的 vsCommandStatus 常数。
vsCommandStatusUnsupported 命令在此上下文中不受支持。
vsCommandStatusSupported 命令在此上下文中受支持。
vsCommandStatusEnabled 命令当前处于启用状态。
vsCommandStatusLatched 命令当前处于锁存状态。
vsCommandStatusNinched 留作将来使用。
vsCommandStatusInvisible 命令当前处于隐藏状态。
CommandText
指定 vsCommandStatusTextWantedStatus 时返回的文本。
5、 Exec()函数:
在VS开发环境中选择了某个外接菜单命令时被VS环境所调用,在这里可以编写自己的响应代码,例如运行自己的程序或弹出某个窗口。
三、 处理菜单
在OnConnect方法中可以进行一系列初始化工作,其中之一就是生成菜单
1、 添加菜单条菜单和工具条菜单
applicationObject = (_DTE)application;
addInInstance = (AddIn)addInInst;
if(connectMode == Extensibility.ext_ConnectMode.ext_cm_UISetup
|| connectMode == Extensibility.ext_ConnectMode.ext_cm_Startup)
{// 如果是安装状态或是插件刚被启动的状态,则创建菜单
object []contextGUIDS = new object[] { };
//获取IDE环境的Command集合和CommandBar集合
Commands commands = applicationObject.Commands;
_CommandBars commandBars = applicationObject.CommandBars;
try
{
//菜单条对象和工具条对象都是CommandBar类型
CommandBar menuObj,toolbarObj;
//生成新的子菜单对象,将会被插入到菜单条和工具条对象上
Command commandObj = commands.AddNamedCommand(addInInstance,
"PublishUserManage",
"添加用户管理代码",
"添加用户管理的代码",
true,
127,
ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled
);
//如何确定在按钮上显示的位图的ID,可以利用附录中的工具
#region 相关帮助信息
/*
返回一个 Command 对象
[C#]
public Command AddNamedCommand(
AddIn pAddIn, //用于添加新命令的 AddIn 对象
string Name, //新命令的名称缩写。AddNamedCommand 会自动给此缩写加上前面类前缀的
//ProgId("STDTools.Connect")中的"STDTools.Connect"以创建唯一的名称
string ButtonText,//在命令绑定到以名称而不是以图标显示的按钮时使用的名称
string Tooltip, //当用户将鼠标指针悬停在任何绑定到新命令的控件上时所显示的文本
bool MSOButton, //指示指定命令的按钮图片是否是 Office 图片。True = 按钮图片从
//Office资源文件中获取。False则表示按钮的图片资源来源于其他的文件
int Bitmap, //在按钮上显示的位图的 ID
object[] ppsaContextUIGUIDs,
//GUID的SafeArray,它确定启用此命令的环境上下文(即调试模式、设计模式等
int DisableFlags
//确定当您提供了 ContextUIGUIDs 而当前它们都不活动时,此命令的禁用状态是不可见还是灰色的
);
DisabeFlags命令的当前状态。
vsCommandStatusUnsupported 0 命令在此上下文中不受支持。
vsCommandStatusSupported 1 命令在此上下文中受支持。
vsCommandStatusEnabled 2 命令当前处于启用状态。
vsCommandStatusLatched 4 命令当前处于锁存状态。
vsCommandStatusNinched 8 留作将来使用。
vsCommandStatusInvisible 16 命令当前处于隐藏状态。
这里有几点要说明:
1、菜单图片:
bool MSOButton参数为true,意味着该菜单命令的图片是来源于Office资源库,则后面的int Bitmap(位图ID号)则指明了是哪个图片。如何知道哪个ID号对应哪个图片?可以从微软的页面上下载一个用VBA写的工具,该工具列举出了所有的图片资源(附录工具中已经包含FaceID.xls)。
如果不想用Microsoft Office自带的图片资源,则需要先在自己的解决方案中添加一个MFC DLL的工程,然后在其中加入图片资源;然后将MSOButton设为False,将Bitmap位图ID设为该资源文件中图片资源的ID号,然后还要在安装程序里添加一个文件夹,同时添加注册表项。具体过程就不再多说了,可以看参考资料
2、菜单项的名称
函数的第二项参数string Name(本例中是”PublishUserManage”),系统在生成菜单对象时会自动在Name前面添加本插件的ProgID代表的字符串作为菜单对象的全名。后面在根据名称检索该菜单对象时,需要用全名,本例中应该是”STDTools.Connect.PublishUserManage”;
*/
#endregion
//添加主菜单
CommandBarButton buttonObj;
//创建主菜单项和工具条
menuObj = (CommandBar) applicationObject.Commands.AddCommandBar("代码生成(&C)" ,
vsCommandBarType.vsCommandBarTypeMenu,
applicationObject.CommandBars["MenuBar"],10);
#region 相关帮助信息
/*
public object AddCommandBar(
string Name,//新命令栏的名称
vsCommandBarType Type,//用于确定命令栏类型的 vsCommandBarType 常数
CommandBar CommandBarParent,//要添加新命令栏的 Office CommandBar 对象
int Position// 命令栏中放置新命令栏的索引位置,从 1 开始
);
vsCommandBarType的取值为:
vsCommandBarTypePopup 10 弹出命令栏
vsCommandBarTypeToolbar 23 工具栏命令栏
vsCommandBarTypeMenu 24 菜单命令栏
*/
#endregion 相关帮助信息
toolbarObj = (CommandBar) applicationObject.Commands.AddCommandBar(
"CodeTools(&C)",
vsCommandBarType.vsCommandBarTypeToolbar,
null,
-1);
toolbarObj.Position = Microsoft.Office.Core.MsoBarPosition.msoBarTop;
//增加子菜单
//将子菜单加入主菜单和工具条
buttonObj = (CommandBarButton) commandObj.AddControl(menuObj, menuObj.Controls.Count + 1);
buttonObj = (CommandBarButton) commandObj.AddControl(toolbarObj, toolbarObj.Controls.Count + 1);
buttonObj.Style = MsoButtonStyle.msoButtonIcon;
//将子菜单加入Project的右键菜单
CommandBar projBar = this.applicationObject.CommandBars["Project"];
commandObj.AddControl(projBar,1);
}
catch(System.Exception ex)
{
string error = ex.Message;
// MessageBox.Show(error);
}
2、 添加右键弹出快捷菜单
除了上面在VS开发环境中添加常规菜单外,还允许用户为开发环境添加一些右键弹出菜单项。下面代码是为代码编辑窗口添加右键弹出菜单。
//检索代码编辑窗口右键弹出菜单的工具条
CommandBar projBar = this.applicationObject.CommandBars["Code Window"];
//将自己的菜单项加入工具条
commandObj.AddControl(projBar,1);
如果想在鼠标右键点击“解决方案资源管理器”中的某项目结点时弹出的菜单条中添加自己的菜单项,则只需把上面的"Code Window"改成"Project"即可。如何知道是"Code Window"或"Project"这个没有什么资料说明,但是也很简单,只要编写一个插件程序,列举出所有的菜单条对象就行了。幸好笔者已经做了这件事情,把所有的菜单条对象的名称都列在附录的CommandBar_Names.txt文件里了,你所要做的就是根据名称去猜哪个是你所需要的菜单条了。
3、 卸载菜单
为什么需要卸载菜单?
因为如果在你的插件中没有处理卸载菜单,则用户通过“工具à外接程序管理器”暂时关闭了你的插件,或者是通过添加/删除程序卸载了你的插件时,菜单项依然存在,但是却已经不能执行命令,这是不合理的。
public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode,
ref System.Array custom)
{
/*
CmdName 要检查的命令的名称。
NeededText
一个 vsCommandStatusTextWanted 常数,指定是否返回检查信息,
如果返回,还指定返回信息的类型。
vsCommandStatusTextWantedNone不返回信息。
vsCommandStatusTextWantedName返回命令名。
vsCommandStatusTextWantedStatus返回命令状态。
StatusOption
一个指定命令的当前状态的 vsCommandStatus 常数。
vsCommandStatusUnsupported 命令在此上下文中不受支持。
vsCommandStatusSupported 命令在此上下文中受支持。
vsCommandStatusEnabled 命令当前处于启用状态。
vsCommandStatusLatched 命令当前处于锁存状态。
vsCommandStatusNinched 留作将来使用。
vsCommandStatusInvisible 命令当前处于隐藏状态。
CommandText
指定 vsCommandStatusTextWantedStatus 时返回的文本。
*/
try
{
if(disconnectMode == Extensibility.ext_DisconnectMode.ext_dm_HostShutdown
|| disconnectMode == Extensibility.ext_DisconnectMode.ext_dm_UserClosed )
{
Command commandObj = this.applicationObject.Commands.Item(
"STDTools.Connect.PublishUserManage",-1);
if(commandObj != null)
commandObj.Delete();
CommandBar menuObj = (CommandBar)
this.applicationObject.CommandBars["代码生成(&C)"];
CommandBar toolbarObj = (CommandBar)
this.applicationObject.CommandBars["CodeTools(&C)"];
if (menuObj != null)
{
this.applicationObject.Commands.RemoveCommandBar(menuObj);
}
if (toolbarObj != null)
{
this.applicationObject.Commands.RemoveCommandBar(toolbarObj);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
上面的代码比较简单,先判断是不是VS环境正在关闭或用户通过外接程序暂停了插件,然后查找到子菜单项并删除,再查找到主菜单项和工具条项,从系统菜单集合里移除掉这些命令条对象。
4、 确定菜单状态
菜单显示时有一项比较重要的工作,就是根据应用环境的不同,菜单对象的显示状态也在不断变化。也即可用,禁用,不显示。每当VS集成环境要显示一个插件的菜单时,它会调用QueryStatus()方法查询该菜单应该显示的状态。如下代码所示:
public void QueryStatus(string commandName,
EnvDTE.vsCommandStatusTextWanted neededText,
ref EnvDTE.vsCommandStatus status, ref object commandText)
{
/*
CmdName:要检查的命令的名称。
NeededText:一个 vsCommandStatusTextWanted 常数,指定是否返回检查信息,
如果返回,还指定返回信息的类型。
status:一个指定命令的当前状态的 vsCommandStatus 常数。
vsCommandStatusUnsupported 0 命令在此上下文中不受支持。
vsCommandStatusSupported 1 命令在此上下文中受支持。
vsCommandStatusEnabled 2 命令当前处于启用状态。
vsCommandStatusLatched 4 命令当前处于锁存状态。
vsCommandStatusNinched 8 留作将来使用。
vsCommandStatusInvisible 16 命令当前处于隐藏状态
CommandText:指定 vsCommandStatusTextWantedStatus 时返回的文本。
*/
if(neededText==EnvDTE.vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if(commandName == "STDTools.Connect.PublishUserManage")
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported;
if(//此处省略判断状态的代码….)
{
status =
(vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|
vsCommandStatus.vsCommandStatusEnabled;
}
}
}
}
上面的代码先预设菜单项状态为受支持状态(如果不受支持,则菜单不会显示),然后判断条件满足的情况下添加菜单为可用状态,如果不满足条件,则菜单为禁用状态。
5、 执行菜单命令
当用户选择插件的菜单项时,集成环境会调用Exec()方法,如下所示:
public void Exec(string commandName,
EnvDTE.vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
/*
vsCommandExecOption 指定执行选项的常数。
常量 值 说明
vsCommandExecOptionDoDefault 0 执行默认行为,无论是否提示用户输入。
vsCommandExecOptionPromptUser 1 获取用户输入后执行命令。
vsCommandExecOptionDoPromptUser 2 不提示用户就执行命令。
例如,选择工具栏上的“打印”按钮导致立即打印文档,而不必用户输入。
vsCommandExecOptionShowHelp 3 显示可能存在的相应命令的帮助,但不执行命令。
*/
handled = false;
if(executeOption == EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "STDTools.Connect.PublishUserManage")
{
//此处略去200字…^_^
handled = true;
return;
}
}
}
上面代码查询被执行的菜单的名称是本插件所期望的名称,则执行一段代码,如显示窗口等,执行成功,则设置handled为true,告诉集成环境已经成功执行命令。
VS.NET 2003集成环境插件开发指南(二)----使用窗口
VS.NET 2003集成环境插件开发指南(三)----操纵VS开发环境(完结篇)
大家也可以看看妖居写的用VB.NET开发的VS插件,他写的内容更加丰富一些,
http://www.cnblogs.com/farrio/category/21434.html