Microsoft的Visual Studio开发工具为软件开发人员提供了强大的功能,通过集成开发环境(IDE)的构建平台提供的菜单和工具条,开发人员可以生成项目,编写代码,调试直到最终生成应用程序。除此之外,Visual Studio还提供了一套完整的自动化对象,通过操作这些自动化对象提供的方法,开发人员在不使用菜单和工具条的情况下也能够完成诸如打开文件、激活窗口或改变窗口大小等操作。Visual Studio提供了两种方法让用户控制集成开发环境和它的自动化组件对象,一种是使用宏(Macros),另一种是使用内置插件(Add-ins)。本文主要是介绍内置插件(Add-ins)方面的内容。
每一个自动化对象都代表了集成开发环境的一个构件或相关的一组构件。例如Document代表一个打开的文档,而Documents对象则代表所有打开的文档,同样,一个Window对象代表一个打开的窗口,Windows对象代表所有打开的窗口。使用内置插件可以在脱离IDE的情况下打开一个项目。修改项目中的文件甚至重新Build这个项目。使用内置插件还可以向IDE添加实用的命令,在工具条上创建按钮,扩展IDE的功能。
IDSAddIn接口是Visual Studio定义的插件接口,Visual Studio只是定义了这个接口,一个内置插件就是一个IDSAddIn接口的实现,也就是说,一个内置插件就是一个COM对象,它至少应该实现一个IDSAddIn接口。使用VC的Add-ins向导生成的内置插件代码核心就是一个CDSAddIn类:
class CDSAddIn : public IDSAddIn,
public CComObjectRoot,public CComCoClass<CDSAddIn, &CLSID_DSAddIn<
IDSAddIn接口其实只有两个方法,分别是:
IDSAddIn::OnConnection 插件被装载
IDSAddIn::OnDisconnection 插件被卸载
插件工作的过程是这样的,当IDE启动一个插件时,就按照IDSAddIn接口的约定调用插件的coclass实现的OnConnection,并传递一个Application参数给插件,插件的OnConnection就完成插件的初始化。并通过这个Application对象查询IDE支持的其他自动化对象,通过这些对象完成特定的任务。一个内置插件典型的任务就是向系统的Commands对象注册一个命令并在工具条上创建一个按钮。当系统要卸载一个内置插件时就会调用相应的OnDisconnection,插件应该利用这个机会释放已经申请的系统资源,从系统的Commands对象中注销已经注册的命令,同时删除添加在工具条上的按钮。从这个过程可以看出,虽然插件的加载和释放都是被动进行的,但是通过实现自定义的OnConnection和OnDisconnection接口函数,插件可以完成自己定制的初始化和销毁任务,这很像是COM事件的回调过程,从接口函数的名字也可以看出一些倪端,接口函数的名字都是以“On”开头,这通常是COM体系中事件接口的声明方式。图(1) 表示了内置插件、IDSAddIn接口和实现类之间的关系:
图1. 插件组件、IDSAddIn接口和实现类之间的关系
Visual Studio的IDE通过IDSAddIn::OnConnection接口函数将一个很重要的参数传递给插件,这个参数就是一个Application对象。Application对象对应的是整个Visual Studio IDE 的实例,简单点讲,可以理解为一个Application就是一个打开的VC集成开发环境。Application对象是整个Visual Studio内置组件的核心组件,通过Application对象可以查询整个Visual Studio IDE的所有内置对象,并通过这些内置对象控制Visual Studio。以下代码演示了在不打开VC的IDE的情况下打开一个VC的工作区并执行Rebuild All命令编译这个工作区的所有项目:
//伪代码,不可直接编译
#import "....\devshl.dll"
IApplicationPtr pApp;
pApp.CreateObject(_T("MSDEV.Application"));
if(pApp != NULL)
{
pApp-<RebuildAll();
}
内置插件通过IDSAddIn::OnConnection接口函数接口函数获得一个当前集成开发环境的Application对象,插件随后的所有工作都是围绕着这个Application对象进行的,CDSAddIn负责保存这个Application对象。Visual Studio的内置COM对象接口是一个比较复杂的接口模型,图(2)就是这些内置接口的关系图:
图2. Visual Studio的内置COM对象接口关系图下面就以TabBars的自动添加格式化函数说明功能的部分代码为例,演示如何通过Application对象实现在当前文档中插入文字内容:
CComPtr<IDispatch< pDispDoc;
CComQIPtr<ITextDocument, &IID_ITextDocument< pDoc;
//m_pApplication是CDSAddIn保存的一个Application对象
m_pApplication-<get_ActiveDocument(&pDispDoc);
pDoc = pDispDoc; //隐式的接口查询
pDispDoc = NULL; //Release it
if(pDoc)
{
CComPtr<IDispatch< pDispSel;
CComQIPtr<ITextSelection, &IID_ITextSelection< pSel;
CComBSTR bstr;
CString strText;
pDoc-<get_Selection(&pDispSel);
pSel = pDispSel; // 查询ITextSelection对象
pDispSel = NULL;
hr = pSel-<get_Text(&bstr); //得到当前选择的文字
if(SUCCEEDED(hr))
{
strText = bstr;
strText.Replace(_T("\t"),_T(" ")); //将\t替换成空格
bstr = strText;
pSel-<put_Text(bstr);//写入document
}
}
了解了Visual Studio的自动化接口以及如何使用这些接口之后,就可以大致了解TabBars是如何工作的,随后的代码解读将重点介绍每个TabBars的功能是如何实现的,并结合这些功能讲解Visual Studio内置COM对象接口的用法。