在工做中咱们要实现一个功能,须要建立MS Office 和 WPS 兼容插件,也就是建立一个DLL,能够同时兼容office和wps。这样带来的好处就是只须要维护同一份代码,大大下降维护的工做!c++
1. 咱们先看看要建立office插件都有哪些技术能够用编程
VSTO = Visual Studo Tools for Office,基于.net framework框架的Office开发技术。相对于传统的VBA(Visual Basic Application)开发,VSTO为中高级开发人员提供了更增强大的开发平台和语言,并部分解决了传统Office开发中的诸多问题(难于更新、可扩展性差、难以维护、安全性低等),开发人员可使用熟悉的技术来构建更加灵活的、强大的、跨平台的企业级解决方案。api
下图是个人机器上VS2013的建立项目:安全
主要采用C#语言开发,功能强大,感兴趣的同窗能够去google更多相关知识。app
个人需求是要建立可兼容两大办公软件平台的插件,很显然这种技术在WPS下不大可能支持,并且对于XP系统的用户,咱们不可能让用户再去安装一个几百M的.net框架,毕竟国内使用XP的量还比较大。所以这个方案不属于咱们的要求,继续寻找中。。。框架
这是VS2010的项目建立截图:函数
在扩展插件项目下,有两种类型的插件能够建立。ui
1) Visual Studio Add-in 顾名思义,这个项目类型是用于建立 Visual Studio IDE插件的项目,不是咱们的菜。this
2) Shared Add-in 字面意思是共享插件 项目,这个正是咱们所须要的插件类型。google
Shared Add-in 的官方解释:Conversely, a Shared add-in can be loaded only into Microsoft Office applications such as Microsoft Word, Microsoft Publisher, Microsoft Visio, and Microsoft Excel. 大意是,Shared add-in能够被MS Office系列软件调用。
进一步研究后得知,Shared Add-in 也就是com插件技术,在wps的最新版本上支持这种com插件,这样就初步知足了咱们要的全平台兼容插件。
2. 开始建立咱们的插件
建立项目
点击OK后,会出来一个建立向导,第一步是能够选择你要使用的语言,若是用C#语言可能会致使引入.net的依赖,这不是咱们所但愿,咱们但愿建立的插件尽量是本地代码,因此咱们选择了使用C++/ATL。
选择要支持的哪些软件,可选项不少。这里选择软件的意义,就是增长一些接口和注册表项,这里的选择对WPS系列软件的支持没有影响,推荐这里选一个就行了,后面咱们会使用手工自定义的方式来作。
填写你的插件名字和描述。
若是你但愿在应用程序启动时通知你的插件,你就勾选那个选项。
最后确认你的选择没有问题后,点击 Finish就能建立你的插件了。
3. 认识插件项目
下图是建立项目后文件分布,rpc文件夹是我本身建立的,请忽略。
咱们主要会对如下文件进行修改:
Addin.rgs文件 - 注册脚本(Register Script, 简 称RGS),该文件会主要用于将插件注册到相应注册表中。在ATL中,COM服务程序的注册是在工程编译链接的最后阶段,由ATL辅助完成的。在手工的COM编程中,服务程序的注册是比较麻烦的工做。在ATL中,系统经过读取在创建工程过程当中造成的注册脚本文件来完成注册工做。
Connect.h\cpp 文件 - 插件的事件通知接口均在该文件中定义。
其它文件几乎不用动,都是一些自动生成的代码。
4. 链接插件事件
Office系列软件的版本不少,从Office2003 到 Office2013 都有,好消息是,com插件是向下兼容的,不一样版本间的不一样点在于高版本一搬会增长更多的事件通知,根据你须要的事件通知来选择你要从哪一个版本的office系列开始支持。
我须要监控office打开某个文件的事件通知,选择了从Office10版本开始进行支持,该事件能够被所有版本兼容。
1) 添加com库类型文件
安装office07后,在安装目录下office10目录中,其中com库对应关系以下:
word - MSWORD.OLB
PPT - MSPPT.OLB
EXCEL – EXCEL.exe
其中EXCEL比较特殊,com库存在于其exe之中,其它office软件也有相应的com库,这里就不一一列出了。
把上述文件copy出来到你的目录中。
2)引入com库文件到项目
有了上述com类型库文件后,咱们就能够引入须要的com了。在Connect.h增长好下代码:
#import "..\3rdparty\Office12\MSO.DLL" rename_namespace("Office2010") rename("RGB","RGB2"), rename("DocumentProperties","DocumentProperties2")
MSO.DLL是咱们要用到的office系列com库的公共库文件,必需要先引入该库。
引入VBA,主要是为了防止编译不过去:
#import "..\3rdparty\VBA6\VBE6EXT.OLB"
一样方法,引入实际com:
#import "..\3rdparty\Office12\MSWORD.OLB" rename_namespace("MSWord"), rename("ExitWindows","WordExitWindows"),rename("FindText","WordFindText"), named_guids
#import "..\3rdparty\Office12\excel.tlb" rename_namespace("MSExcel"), rename("DialogBox","ExcelDialogBox"), rename("RGB", "ignorethis"), rename("DialogBox", "ignorethis"), rename("ReplaceText", "EReplaceText"), rename("CopyFile","ECopyFile"), rename("FindText", "EFindText"), rename("NoPrompt", "ENoPrompt") exclude("IFont","IPicture")
#import "..\3rdparty\Office12\MSPPT.OLB" rename_namespace("MSPowerPoint"), rename("RGB", "ignorethis")
编译代码,会在项目目录下生成众多相关文件。tlh、tli文件:他们是vc++编译器解析tlb文件生成的标准c++文件。由于odl和tlb并非C++标准的东东,有必要把它们翻译成标准的 C++类型,使得C++开发者可使用。相信vb和j++也会把tlb翻译成本身语言兼容的类型描述信息。
tlh至关于类型申明(头文件)
tli至关于定义实现(CPP文件)编译上面的com时,你的本机必需要安装了相应的office版本,不然颇有可能会出错。因为咱们的代码是在单独的构建机上编译,为了不在纯净的构建机上安装office10软件,我作了些处理,直接使用解析后的文件。相似于以下代码:
#include "..\3rdparty\Office12\include\msword.tlh"
#include "..\3rdparty\Office12\include\excel.tlh"
#include "..\3rdparty\Office12\include\msppt.tlh"wps相关:
#include "..\3rdparty\wps-office6\include\ksoapiv8.tlh"
#include "..\3rdparty\wps-office6\include\wpsapiv8.tlh"
#include "..\3rdparty\wps-office6\include\etapiv8.tlh"
#include "..\3rdparty\wps-office6\include\wppapiv8.tlh"tlh文件中,有相应tli文件的绝对位置,这个可能在其它机器上编译不经过,所以须要手动修改成引用相对地址,根据编译错误,很好修改。
3)链接com事件
经过上述步骤后,已经可使用com中的事件了。首先实现一个模板类:
typedef IDispEventSimpleImpl</*nID =*/ MSWord_ID, CConnect, &__uuidof(MSWord::ApplicationEvents2)> MSWordDispEventImpl;
MSWord_ID : 随意定义一个ID便可,用于下面区分不一样事件。
CConnect增长一个继承类MSWordDispEventImpl,增长以下一个消息循环:
BEGIN_SINK_MAP(CConnect)
// msword events
SINK_ENTRY_INFO(/*nID =*/ MSWord_ID, __uuidof(MSWord::ApplicationEvents2), /*dispid =*/ 0x4, OnDocumentOpen, &DocumentOpenInfo)END_SINK_MAP()
其中:
dispid - 事件ID号,查询MSDN官方文档,或者tlh中会有相关ID
OnDocumentOpen - 事件响应函数,函数类型:void __stdcall OnDocumentOpen(LPDISPATCH ptr); 这里的参数类型要根据这个事件实际的参数类型来建立
DocumentOpenInfo – 参数类型信息,_ATL_FUNC_INFO DocumentOpenInfo = {CC_STDCALL,VT_EMPTY,1,{VT_DISPATCH|VT_BYREF}};,具体参数信息,能够查询其它相关文档
上面操做完成后,CConnect已经能够收到Word打开文档的事件通知,关于该事件的详细触发时间点,能够查询相关MSDN文档。在OnDocumentOpen函数体中,你已经能够写下你想要的功能代码了。
其它各类事件采用相同方式完成便可。
4)注册插件
在AddIn.rgs文件中加入以下代码,完成注册过程:
HKLM
{
Software
{
Microsoft
{
Office
{
Word
{
Addins
{
ForceRemove 'YourAddin.Connect'
{
val Description = s 'Yourdesc'
val FriendlyName = s 'YourName'
val LoadBehavior = d '3'
}
}
}Excel
{
Addins
{
ForceRemove 'YourAddin.Connect'
{
val Description = s ''Yourdesc''
val FriendlyName = s 'YourName'
val LoadBehavior = d '3'
}
}
}
}
}
}}
有关rgs文件语法说明,须要参考其它相关文件。
5)调试插件
插件写好,咱们得要调试插件。首先你运行的vs必须是要以“管理员”方式启动的,把插件库设置为启动项,在启动参数里写入world.exe的绝对目录,启动调试后就能够调试插件中的事件响应了。
6. 总结
本篇是是对Office的插件技术实现的描述,特色是实现了兼容wps的插件事件。优势在于使用C++语言实现,生成的插件dll体积小,不依赖于.net ,方便安装使用;缺点是c++语言,对ATL com的知识也有必定要求,开发难道较高。