前言
上一篇随笔Macro和Add-In初探介绍了如何开发两者的HelloWorld程序。没错,宏确实简单易行。不过在某些情况下,比如在商业软件中,宏在性能和知识产权方面可能会带来麻烦,此时那把更好的锤子是Add-In。
在初探一文中,我介绍了如何使用Add-In向导来开发第一个Add-In。VS是一款很棒的开发工具,它的各种向导(以及其它模板、可视化工具等)做得非常好,不过我发现这一强大之处到头来反而给人诟病。其中一种说法是,这些方便的工具让初学者入门容易,并惯坏了他们,以致于想登堂入室就难得多了。客观地说,这不是VS的错,VS没有阻止你去了解这些工具的背后所在。这些工具会生成大量代码,我们需要主动去了解它们,《程序员修炼之道》中曾提到:
Don't Use Wizard Code You Don't Understand
很明显,作者不是说不能使用向导,而是说要在了解向导的前提下使用它。尤其是你写的代码要跟向导生成的代码混在一起的时候,这些代码终究要变成你的代码,而Add-In的开发正是如此!所以我们必须得先了解Add-In向导做了些什么。
Add-In向导在收集信息
Add-In向导共有六步,每一步我们都可以输入一定的信息,告诉VS如何设置。这可以看作是向导收集信息的过程,这些信息包括:
- 编程语言:可以选择C#、VB.NET、VC,如果是手工编写Add-In,就没这个限制了
- 宿主环境:Add-In可同时运行在不同版本的VS IDE和/或Macro IDE内
- 名称和描述
- 菜单命令:VS据此生成一些代码,在Tools菜单中添加一个新的菜单项
- 命令行运行支持:这样的Add-In说明它不会呈现需要用户介入的UI,如模式对话框
- 启动时加载:VS可以在启动时自动加载Add-In
- About对话框:可以将Add-In的信息显示在About对话框中
信息收集完毕后,VS会生成一个新的Add-In项目。
Add-In项目
Add-In项目是一个类库项目(可以参考初探一文中做的例子),仅此而已。该项目包含了“Connect.cs”文件,它定义了Connect类,还有一个配置文件FirstAddin.AddIn。
打开Connect.cs,我们仔细分析一下。Connect类实现了两个接口,一是IDTExtensibility2,该接口用于在Add-In和IDE之间进行通信;二是IDTCommandTarget,如果选择了向导中的UI选项,就需要实现它。
IDTExtensibility2包含5个方法:
- OnConnection:在加载Add-In时调用
- OnStartupComplete:在Add-In随着VS的启动完成加载后调用
- OnAddInsUpdate:在VS加载或卸载Add-In时调用
- OnBeginShutdown:在VS关闭时调用
- OnDisconnection:在卸载Add-In时调用
在文件顶部可以看到引用了若干个命名空间,对于Add-In开发来说最重要的是其中三个:Extensibility、EnvDTE和EnvDTE80。Extensibility定义了IDTExtensibility2使用的类型;后面两个命名空间则定义了自动化对象模型(Automation Object Model,以下简称AOM)中的类型。
回到前面的5个方法,最重要的一个是OnConnection,VS在加载Add-In时调用它,通过第一个参数application将AOM的根对象传入,向导产生的代码将该对象的引用保存_applicationObject中;同时通过第三个参数addInInst将当前Add-In所对应的AddIn对象传入,保存在_addInInstance中。再往下看,这些代码将向Tools菜单添加一个菜单命令(如果你在向导中选中该选项的话),其中包括如下代码:
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)