前言
在VS概览中,我们简单回顾了一下VS的历史。本文将通过两个简单的例子来说明Macro和Add-In的开发。通过Macro我们把VS中的一些重复操作录制下来,之后可以多次运行,节省时间并保持好的心情;通过Add-In,我们可以自己动手来为VS添加新的功能,扩展了VS就意味着扩展了我们自己。
VS 2008扩展方式一览
在上篇VS概览中提到了扩展VS有三种主要的方式:Macro、Add-In和VsPackage。事实上,还有更多的选择,这里先简单列一下:
- Macro
- Add-In
- VsPackage
- VS Shell
- Domain Specific Language Tool
- Visualizer
- Code Snippet
- Project/Item Template
- MSBuild
甚至External Tools(菜单Tools->External Tools)也可看作是一种扩展方式,我们可以将外部工具添加到VS菜单中,这样使用起来会更方便一点。之前曾写过关于VS中的模板和Code Snippet的随笔,它们属于比较简单的扩展方式了。
在本文中将简单介绍一下Macro和Add-In的用法,它们可以访问共同的API,既然这样,了解一下Macro对Add-In的开发也会有帮助,然后就正式步入Add-In的开发。之后对于其它的扩展方式也会尽量多介绍一点。
开发第一个Macro
作为程序员,我们在编写代码的时候害怕重复,它的危害人人知晓。另一方面,对于某些操作,如果经常重复进行也会让人厌烦,Macro此时也许能帮得上忙。
Macro一般翻译为宏,它的一个含义是“大量使用的”,这个词对于我们Windows平台下的开发人员来说应当不陌生,在Word和Excel中都有它的身影。宏的作用是将我们在VS中的一些重复操作录制下来,之后可以多次运行,也就可以节省很多时间,心情也会因此变得好一点。
在VS中,宏可以用两种方式来创建,一是录制,二是手工编写代码。可以想见的是,第一种方式更为简单,第二种方式则更为灵活、强大。需要注意的是,当前只能用VB.NET来开发宏。
以前我曾用VBA开发过简单的Excel自动化,当时如果某些操作不知如何用代码表示,就录制一个宏,看看它生成的代码。VS中的宏与此类似,这也是为什么我说对Add-In开发也有帮助了。我们可以将前面说的两种方式结合起来使用,看看下面的例子。
1)Macro Explorer
通过菜单Tools -> Macros -> Macro Explorer(快捷键Alt+F8)打开Macro Explorer:
它的结构看起来有点像Solution Explorer。在Macros上点击右键,选择New Macro Project...,我这里把宏命名为FirstMacro,将其设置为录制项目:
2)录制宏
现在可以进行录制了,按下Ctrl+Shift+R,这时会出现一个小工具栏,可以暂停、停止或取消一次录制(如果某些操作不想录制,就可以将它暂停)。然后执行以下操作:
- 按下Ctrl+F打开Find and Replace对话框
- 输入":a+"(含引号),查找范围为Current Document,然后选择使用正则表达式
- 点击Find Next
- 按下Ctrl+Shift+R停止录制
3)运行宏
这个简单的宏可以用来查找当前文档内硬编码的字符串。按下Alt+F8打开Macro Explorer,可以看到新录制的宏TemporaryMacro:
通过右键菜单运行它,怎么样,是不是很方便涅?现在打开另一个文件,再次运行宏。奇怪的是,还是在刚才的那个文件内查找!看来刚才在录制的时候把文件名也录制进去了,这时我们利用第二种方式:手工编写代码。
4)修改宏
参考上图,在TemporaryMacro上点击右键,对它进行编辑。在新打开的Macro IDE内可以看到如下代码:
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
Sub TemporaryMacro()
DTE.ExecuteCommand("Edit.Find")
DTE.Windows.Item("ListViewSample.cs").Activate()
DTE.Find.FindWhat = """:a+"""
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = True
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
End Sub
果然,这里包含了刚才打开的文件名,把这一行注释掉。另外最后的异常信息也不喜欢,改成"Sorry, I cannot find it:("。
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
Sub TemporaryMacro()
DTE.ExecuteCommand("Edit.Find")
'DTE.Windows.Item("ListViewSample.cs").Activate()
DTE.Find.FindWhat = """:a+"""
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = True
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("Sorry, I cannot find it:(")
End If
End Sub
这样使用起来就没问题了。
通过这个小例子我们应当对Macro的用法有基本的了解了。不仅可以编写有用的宏,还可以通过宏来帮助编写Add-In,比如通过上面的代码,我们就可以知道如何打开查找和替换对话框了。下面再来编写我们的第一个Add-In。
开发第一个Add-In
相信有很多人用过Add-In(一般译作插件,这里保留不译),常见的如DPack、TestDriven.NET、GhostDoc等等,这些我都曾经介绍过(至于ReSharper、Visual Assist X等很棒的Add-In,没钱买,又不愿用破解版,故没有尝试)。Add-In与VS环境完全集成,可以为VS添加新的功能。
Add-In可与宏访问相同的API,但是VS处理起来有所不同。宏是可被VS运行的源代码,但没有集成到IDE中,它不被编译为程序集,要分发宏只能使用源代码。而Add-In呢,会被编译为dll,然后VS启动时加载它,这样它就与IDE集成在了一起,部署的时候不需要源代码。另一个区别是,当前只能用VB.NET开发宏,而对于Add-In来说,任何.NET下的语言都可以使用,如C#、VB.NET和VC(理论上来说F#当然也可以,但还没做过尝试)。
Add-In可以操作解决方案、项目、文件、编辑器,也可以操作VS中的各个工具窗口。可以说,它是扩展VS的一种比较专业的方式。下面来看看如何开发我们的第一个Add-In,它的功能是向编辑器内插入当前的日期,不管怎样,还是有一点点用处。
1)Add-In Wizard
新建一个项目:
在Other Project Types->Extensibility中可以看到Add-In项目模板,项目名称设置为FirstAddin,OK。出现欢迎界面,点击Next,进入向导第一步,选择一种语言,如C#,点击Next进入第二步。
这里可以选择Add-In运行的宿主环境,只保留VS 2008,Next进入第三步,设置Add-In的名称和描述信息,进入第四步:
选中第一个选项,这样VS会在Tools菜单中添加一个新菜单项,剩下的部分一路Next下去即可。最后VS会创建一个新的项目,其中包含了Connect.cs文件,这就是今后我们的主战场了。
打开Connect.cs,如果这是你的Add-In初体验,也许会觉得代码有些奇怪,进而感觉有些慌乱,不用担心,很快你就会熟悉它了。找到OnConnection方法,Add-In就是在这里加载,搜索文本"commands.AddNamedCommand2"(大约在77行),这一行创建了一个新的菜单项,将其AddNameCommand2的第二、三个参数都改为"InsertDate"。将QueryStatus和Exec方法稍作修改:
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
public void QueryStatus(string commandName,
![dot.gif](https://i-blog.csdnimg.cn/blog_migrate/2fb7a7a1351bea3c046ebb445cae4386.gif)
{
if(neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if (commandName == "FirstAddin.Connect.InsertDate")
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|vsCommandStatus.vsCommandStatusEnabled;
return;
}
}
}
public void Exec(string commandName,
![dot.gif](https://i-blog.csdnimg.cn/blog_migrate/2fb7a7a1351bea3c046ebb445cae4386.gif)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName == "FirstAddin.Connect.InsertDate")
{
TextSelection selectedText = _applicationObject.ActiveDocument.Selection as TextSelection;
selectedText.Text = DateTime.Now.ToShortDateString();
handled = true;
return;
}
}
}
按下F5进行调试,此时打开一个新的IDE窗口,在新的窗口中可以测试Add-In的功能,随便打开一个解决方案,然后打开一个代码文件,查看Tools菜单:
点击新菜单项,看看编辑器内是不是插入了当前日期?注意,这个菜单项只有在调试的时候才会出现。
好,我们第一个Add-In也创建成功了。它颇为简陋,而且菜单项放在编辑器的上下文菜单中会更好,这里先放一放,在后面的文章中我会对此作出修改。
可以在这里下载代码。第一次使用Google Code,感觉很不错。
我们身在何处
本文将通过两个例子说明了Macro和Add-In的开发。希望这些能让你对两者有个基本的认识。在随后的随笔中,我将对Add-In展开详细的介绍。
参考
《Professional Visual Studio® 2008 Extensibility》
《Working with Microsoft Visual Studio® 2005》