cnblogs Post date: 2009-11-09 14:44
今天使用Eclipse时突发奇想:给VS2008做个插件,实现保存文件就编译。
这个想法的缘自一个很烦人的问题:我的VS2008中的快捷键老是会变,如“编译”这个命令有时是F7,有时是F6,有些诡异。我也懒得去记这些快捷键,所以想找一个简单的方法来解决,想到CTL+S这个是一个从来不变的快捷键,又由于我们保存文件时通常是一些工作已经做好了,这时无疑很适合编译。
选择什么工具来解决呢? Eclipse提醒我,可以在VS2008中也可以弄个插件。
想到就去做,VS2008中提供了“Visual Studio外接程序”的模板。
使用该模板创建一个工程后,该想一想怎么加入什么样的代码。
分析一下“保存即编译”的目标,程序必须要实现:
能捕获“文档保存”这样的事件;
能“编译”整个工程/解决方案。
文档保存和编译都是VS的活动,一个插件怎么获取VS的活动呢? 需要慢慢分析模板代码啦……
从VS自动生成的代码可以看到程序的主体是一个类:
public class Connect : IDTExtensibility2, IDTCommandTarget;
它有两个字段:
private DTE2 _applicationObject;
private AddIn _addInInstance;
分析代码可以看到这两个将在 OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
中获得赋值,而OnConnection()
是在VS初始化加载插件时调用,而 _applicationObject=application
代表宿主应用程序的根对象,_addInInstance=addInInst
代表表示此外接程序的对象。
找到关键点了 “宿主应用程序” 在此自然代表VS啦,接下来看看能不能从_applicationObject得到些有用信息。 _applicationObject的类型是DTE2,查看其成员,我当然希望能直接找到个DocumentSave或DocumentSaving事件,再有一个Build方法就能直接解决问题了。可惜找了半天没找到,不过有替代方案:发现有Documents属性和ExecuteCommand方法。
从Documents中可以得到每一个Document,而Document中有个属性Saved能指示其是否被保存了,于是捕获文档保存事件的代码可以这样写:
Timer monitorDocSaveTimer = new Timer(1000); //每1秒检测一遍
monitorDocSaveTimer.Elapsed += delegate(object sender, System.Timers.ElapsedEventArgs arg)
{
static bool _lastSaved=true;
bool curSaved=true;
//文件保存 是指 所有文件都保存 所以用到遍历
foreach (Document doc in _applicationObject.Documents) {
if (!doc.Saved)
curSaved = false;
}
if(!_lastSaved && curSaved) { //此时刻前文件保存了
Build();//可以编译了,该方法见下文
}
_lastSaved=curSaved;
};
为了方便阅读这里用匿名方法来写,实际中可以另外定义个函数来创建委托。
文件保存事件现在能被检测到了,可是怎么编译项目呢?
这就用到了ExecuteCommand()了,它有两个参数分别代表要执行的命令和其命令参数,这里的命令是指可以在VS“命令窗口”中执行的命令,刚好其中有一个命令是Build.BuildSolution可以编译整个解决方案,当然编译单个工程的命令也有,这里就直接使用编译整个解决方案的了。如此,Build方法可以如下:
void Build()
{
_applicationObject.ExecuteCommand("Build.BuildSolution", "");
}
这样以来整个插件的功能点已经都有了。
再随便加点用户配置和异常处理的模块就能用了。