在工作中我们要实现一个功能,需要创建MS Office 和 WPS 兼容插件,也就是创建一个DLL,可以同时兼容office和wps。这样带来的好处就是只需要维护同一份代码,大大降低维护的工作!

1. 我们先看看要创建office插件都有哪些技术可以用

  • VSTO

VSTO = Visual Studo Tools for Office,基于.net framework框架的Office开发技术。相对于传统的VBA(Visual Basic Application)开发,VSTO为中高级开发人员提供了更加强大的开发平台和语言,并部分解决了传统Office开发中的诸多问题(难于更新、可扩展性差、难以维护、安全性低等),开发人员可以使用熟悉的技术来构建更加灵活的、强大的、跨平台的企业级解决方案。

下图是我的机器上VS2013的创建项目:

主要采用C#语言开发,功能强大,感兴趣的同学可以去google更多相关知识。

我的需求是要创建可兼容两大办公软件平台的插件,很显然这种技术在WPS下不大可能支持,而且对于XP系统的用户,我们不可能让用户再去安装一个几百M的.net框架,毕竟国内使用XP的量还比较大。因此这个方案不属于我们的要求,继续寻找中。。。

  • Shared Add-in

这是VS2010的项目创建截图:

在扩展插件项目下,有两种类型的插件可以创建。

1) Visual Studio Add-in 顾名思义,这个项目类型是用于创建 Visual Studio IDE插件的项目,不是我们的菜。

2) Shared Add-in 字面意思是共享插件 项目,这个正是我们所需要的插件类型。

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的知识也有一定要求,开发难道较高。

创建MS Office 和 WPS 兼容插件的更多相关文章

  1. 怎么用OCR图文识别软件在MS Office中创建PDF文件

    ABBYY PDF Transformer+是一款可创建.编辑及将PDF文件转换为其他可编辑格式的OCR图文识别软件,不仅可以从纸质文档.图像文件和任何其他流行格式创建PDF文件(相关文章请参考如何从 ...

  2. (翻译)OpenDocument and Open XML security (OpenOffice.org and MS Office 2007)

    标题:Open Document 和 Open XML安全性(OpenOffice.org and MS Office 2007) 摘要,OpenDocument 和 Open XML 都是 Offi ...

  3. java读取pdf和MS Office文档

    有时候PDF中的文字无法复制,这可能是因为PDF文件加密了,不过使用PDFBox开源软件就可以把它读出来. 还有一个用于创建PDF文件的项目----iText. PDFBox下面有两个子项目:Font ...

  4. 跟着未名学Office – 整体了解 Ms Office 2010

    目录 MS Office 2010    2 Microsoft Office System    2 Ribbon(功能区)    2 文件选项卡    3 SmartArt    3 屏幕截图   ...

  5. MS Word2016加载EndnoteX6插件

    我的软件环境是:Win10 x64.MS Office 2016 x64.Endnote X6 32位. 在安装完MSO和Endnote后,Word中未能自动加载Endnote插件.现将启用方法记录如 ...

  6. C#将Word转换成PDF方法总结(基于Office和WPS两种方案)

    有时候,我们需要在线上预览word文档,当然我们可以用NPOI抽出Word中的文字和表格,然后显示到网页上面,但是这样会丢失掉Word中原有的格式和图片.一个比较好的办法就是将word转换成pdf,然 ...

  7. MS OFFICE 2010破解版安装

    受人所托发布一个MS OFFICE 2010破解版的傻瓜安装教程,刚好新本本也没有安装,安装过程中截了几个图就搞定了. 安装包: http://www.itopdog.cn/soft/office20 ...

  8. [转]设置MS Office Word for mac的默认显示比例

    由于mac os的分辨率比较大,在PC上显示正常的word文档(显示比例100%),在mac下打开会很小,需要经常调整显示比例,如调到125%.130%或150%,可以通过下面方法来设置默认显示比例, ...

  9. MS OFFICE WORD 绝招

    以MS OFFICE WORD 2010为例. 1.WORD 文件夹连接线(标准称呼:前导符)为什么有的稀,有的密? 答案:文件夹格式字体不同. 2.首页.文件夹页.正文有的要页眉,有的不要,首页不要 ...

随机推荐

  1. 项目常用jquery/easyui函数小结

    #项目常用jquery/easyui函数小结 ##背景 项目中经常需要使用到一些功能,封装.重构.整理后形成代码沉淀,在此进行分享 ##代码 ```javascript /** * @author g ...

  2. PySpark调用自定义jar包

    在开发PySpark程序时通常会需要用到Java的对象,而PySpark本身也是建立在Java API之上,通过Py4j来创建JavaSparkContext. 这里有几点是需要注意的 1. Py4j ...

  3. AWS Summit 2014 San Francisco站总结

    我上个月去San Francisco参加了AWS Summit 2014 会议,总结一下参加这个会议的情况. 什么是AWS Summit? AWS Summit 是AWS在全球各地举办的,针对AWS用 ...

  4. 通过AopTestUtils对切面对象进行mock

    概述   当对一个切面类进行测试时,由于Spring对切面对象生成了proxy对象,此时对切面对象使用ReflectionTestUtils赋值,操作的是proxy对象,而不是真实对象,会使得赋值出问 ...

  5. intel xdk 打ios的ipa包

    1.打包 2.点击edit.下载csr文件,然后上传到苹果开发者网址,生成cer文件 上面两步搞完,把最后的按钮设置成"yes" 3.上传配置文件

  6. Ubuntu下gdb远程调试--warning: Could not load vsyscall page because no executable was specified解决方案

    1. 首先安装gdbserver apt-get install gdbserver 2. 编译-g 程序 gcc -g test_gdb.c -o test_gdb 源码如下: #include & ...

  7. HD1064Financial Management

    Problem Description Larry graduated this year and finally has a job. He's making a lot of money, but ...

  8. New Objective-C Feature

    [Advance Objective-C Feature] 1.@import避免反复解析头文件,本地宏对框架API定义无影响. 2.可以导入单独一个头文件. 3.使用了@import后,不再需要选择 ...

  9. conn,stmt,rset 的关闭(规范)

    Connection conn = null; Statement stmt = null; ResultSet rset = null; try { conn = dataSource.getCon ...

  10. CloudStack4.2 更新全局参数API

    测试更新全局参数API http://192.168.153.34:8080/client/api?command=updateConfiguration&response=json& ...