编写office插件
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Keyword:addin,word
Abstract:word插件
0.在VC中导入Office类
1.从类型库中导入
VS2003/5/8从菜单选择项目-添加类,在弹出的对话框中选择TypeLib中的MFC类。
然后根据注册表或exe/dll/olb/tlb/ocx类型的文件,添加类。
选择好注册表选项或文件后,会出现很多可以添加的类,选择需要的添加到项目中。
然后向导会添加新类文件,
// 从类型库向导中用“添加类”创建的计算机生成的IDispatch 包装类
#import"安装目录\\OFFICE(11)\\MSWORD.OLB" no_namespace
class XXX
{
//... ...
};
被cpp包含并编译后,Debug或生成目录里会自动产生*.tlh/*.tli两个文件。
里面包括所有的类的导出。
2.从动态库直接导入
在stdafx.h文件中写入
#import"系统盘:\\Program Files\\Common Files\\Microsoft
Shared\\Office(11)\\mso.dll" rename_namespace("MSOffice")
using namespace MSOffice;
Debug或生成目录里会自动产生mso.tlh/mso.tli两个文件,里面包括所有的类的导出,只是自己要费力区别一下。
这些是关于office的。
里面的类可以直接用。
还有这些是可选的,根据需要,酌情添加。
#import"系统盘:\\Program Files\\Common Files\\Microsoft
Shared\\VBA\\VBA6\\VBE6EXT.olb" rename_namespace("VBE6")
using namespace VBE6;
#import"安装目录\\OFFICE(11)\\MSWORD.olb"
rename("ExitWindows","ExitWindowsEx"),
named_guids,rename_namespace("MSWord")
using namespace MSWord;
3.二者区别
类型库中导入方法,与第二中方法相比,给我们产生了包装好的C++类,非常容易使用,是常用的手段。
在其头文件里有#import xx。
import dll的方法是COM标准方法。
共同点编译之后,Debug或生成目录里会自动产生*.tlh/*.tli文件,里面包括所有的类的导出。
Office及其组件的常量枚举在mso.tlh/mso.tli中可以查阅到。
1.简介
你也许曾在Office2000下的Word2000、Access2000、Excel2000、PowerPoint2000等软件中的工具条或菜单条资源中,看到一些其它软件加入的新的自定义工具条按钮或菜单条,当点击它们时,会有其不同的响应发生。
下面,让我们也来实现这些功能,需要说明的是,在这里我们不用VB/VBA来实现它,而是用VC6中所带ATL(活动模板库)3.0来开发具有这种效果的Office2000内部COM插件。
在 Office2000中,不管是Word2000、Access2000、Excel120000、PowerPoint2000还是Outlook2000等,它们COM插件的编程方法及步骤都是极其相似的(除注册表中键值及导入相应类型库不同外)。
2.基础知识
一个Office2000下的内部COM插件必须实现一个_IDTExtensibility2派发接口,_IDTExtensibility2派发接口被定义在MSADDin Designer类型库(MSADDNDR.dll/MSADDNDR.tlb)中,通常位于<盘符>/Program Files/Common Files/Designer下。
_IDTExtensibility2接口中必须实现下面五个接口函数(一般只需编写OnConnection和OnDisconnection中代码),分别如下:
1. OnConnection: 装载插件到内存时处理(可以通过自动化在程序启动时自动装载插件)。
2. OnDisconnection: 从内存中缷载插件时处理。
3. OnAddinsUpdate: COM插件改变时处理。
4. OnStartupComplete: 当应用程序启动时插件刚装载完成时处理。
5. OnBeginShutdown: 当应用程序关闭时插件刚缷载完成时处理。
3.组件注册
只有在正确注册了相应应用程序的内部COM插件时,才能被其应用程序加载上。
需要在注册表中创建以下键值:
HKEY_CURRENT_USER\Software\Microsoft\Office\<TheOfficeApp>\Addins \<ProgID>
其中,TheOfficeApp表示相应程序名,如:Word、Outlook等,ProgID表示内部COM 插件程序的唯一标识符的字符串表示形式,如:Outlook2000Addin.Addin等。
ProgID键值下主要创建以下四个键值:
1. FriendlyName: 字符串类型,插件的名称,将在相应程序的COM加载对话框中看到。
2. Description: 字符串类型,插件的描述信息。
3. LoadBehavior: DWORD类型,决定插件将以什么形式被装载。
当其值为0x03时,为应用程序装载时被自动装载(一般使用此值)、当其值为0x08时,为用户控制激活装载。
4. CommandLineSafe: DWORD类型,命令行方式,可以设置为0x01(真)或0x00(假)。
4.编写Office插件的框架
1.创建工程
用VC6创建一个ATL COM类型的DLL工程,名称WordAddinFrame。
2.添加组件
VC6:菜单Insert->New ATL Object插入一个简单的Object,名称为WordAddin。
VS2005:添加类-ATL-简单对象。
3.实现_IDTExtensibility2接口
在ClassView中的CWordAddin类上点鼠标右键,在弹出的右键菜单中选Implement Interface项(实现接口)。
在弹出的实现接口对话框中点击Add Typelib(添加类型库),在弹出的Browse Type Libraries对话框中,向下滚动选取Microsoft Add-in Designer(1.0)子项,点OK按钮。
在弹出的接口列表对话框中选中_IDTExtensibility2接口,点OK按钮完成导入。
系统会自动为你生成空的上面所提到的五个所需接口函数。
4.修改工程下的WordAddin.rgs文件,
在下面增加如下代码
HKLM
{
Software
{
Microsoft
{
Office
{
Word
{
Addins
{
'WordAddinFrame.WordAddin'
{
val FriendlyName = s 'My WORD Addin'
val Description = s 'It is a Word addin'
val LoadBehavior = d '00000003'
val CommandLineSafe = d '00000001'
}
}
}
}
}
}
}
要注意的是:1.以上内容写如注册表,第一个也可以是HKCU主键。
2.Office下面的那个子项代表了这个插件是属于那个组件,Word、Excel、Outlook等等。
3.Addins下面的那个子项要写成你添加的COM组件的名字。
4.所有的值两边加的都是单引号,而且要用英文下的单引号,不能用双引号。
这样一个Office插件的框架才算完成。
5.编写Word按钮插件
1.引入文件
在Stdafx.h文件里,为工程导入office文件。
加入后要保证编译成功。
引入这两个文件的原因,主要是为了引入一些变量类型,为后面的UI创建作准备。
/*
三个文件顺序不能错,否则编译出错
前面两个是系统目录下,第三个是安装目录
Office 2007下导入使用的例子
*/
#import "E:\\Program Files\\Common Files\\Microsoft Shared
\\Office12\\mso.dll" rename_namespace("Office")
using namespace Office;
#import "E:\\Program Files\\Common Files\\Microsoft Shared
\\VBA\\VBA6\\VBE6EXT.olb" rename_namespace("VBE6")
using namespace VBE6;
#import "D:\\Program Files\\Microsoft Office\\Office12\\MSWORD.olb" rename("ExitWindows","ExitWindowsEx"),
named_guids,rename_namespace("MSWord")
using namespace MSWord;
2.Office界面知识
在Office应用程序中,尽管菜单和工具栏按钮看上去不太一样,但实质上它们是相同类型的对象。
CommandBars集合包含程序中的所有命令条,如:工具条和菜单条。
每一个CommandBars集合都有多个CommandBar对象和它对应。
CommandBar 对象可以包含其它的 CommandBar 对象,这些对象是作为按钮或菜单命令来用的。
每一个CommandBar都将通过CommandBarControls 对象被引用。
CommandBarControls又可以包含一组CommandBarControl对象。
每一个CommandBarControl可以包含一个CommandBar对象,并可以通过它来存取控件属性。
每一个CommandBarControl对象,实际是对应CommandBarControls 中的控件集合。
CommandBarControl可以有三种表现形式:
*弹出式(CommandBarPopup):相当于菜单条的一个菜单项。
*组合框(CommandBarComboBox):类似于工具条中组合框控件。
它包括一个工具栏和紧接着工具栏的一个下拉箭头。
单击该按钮,将显示出更多的带图标的菜单命令。
*按钮(CommandBarButton):相当于标准的工具栏按钮,即带有图标的按钮。
3.创建按钮等界面
CWordAddin::OnConnection这个函数里面可以创建按钮和装载的最佳地点。
代码比较多,具体见例程。
4.按钮响应函数知识
工具条按钮CommandBarButton派发接口的响应事件是_CommandBarButtonEvents。
ATL提供了二种模板类IDispEventImpl<>和IDispEventSimpleImpl<>来实现接口事件的接收,这里我们使用IDispEventSimpleImpl来实现(因为它不需要额外的类型库信息)。
它需要设置SINK(接收)映射,通过_ATL_SINK_INFO结构来回调参数信息。
5.给按钮添加响应函数
为CWordAddin增加父类
public
IDispEventSimpleImpl<1,CWordAddin,&__uuidof(Office::_CommandBarBu ttonEvents)>
public
IDispEventSimpleImpl<2,CWordAddin,&__uuidof(Office::_CommandBarBu ttonEvents)>
//每一个代表一个菜单项或者按钮的事件,有几个按钮就要有几个父类
在.cpp中
_ATL_FUNC_INFO OnClickButtonInfo =
{
CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL} };
在.h中
extern _ATL_FUNC_INFO OnClickButtonInfo;
在.h类里面加入
BEGIN_SINK_MAP(CWordAddin)
END_SINK_MAP()
这样,消息映射等框架就写好了。
添加相应函数的声明、定义、映射(注意映射添加在.h的SINK_MAP映射表之间)void __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
void __stdcall OnClickButton2(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
SINK_ENTRY_INFO(1,
__uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton1, &OnClickButtonInfo)
SINK_ENTRY_INFO(2,
__uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x02, OnClickButton2, &OnClickButtonInfo)
void __stdcall CWordAddin::OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault) {
MessageBox(NULL, "hello", "world", MB_OK);
}
void __stdcall CWordAddin::OnClickButton2(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault) {
::MessageBox(NULL, "hello 2", "world", MB_OK);
}
6.连接与断开
最终通过DispEventAdvise和DispEventUnadvise来与源接口连接或断开。
把产生好的按钮保存为成员变量,并在CWordAddin::OnDisconnection的时候释放。
CWordAddin.h中加入
CComQIPtr < Office::_CommandBarButton> m_spCmdButton;
CComQIPtr < Office::_CommandBarButton> m_spCmdButton2;
typedef IDispEventSimpleImpl</*nID =*/ 1,CWordAddin1, &__uuidof(Office::_CommandBarButtonEvents)> CommandButton1Events; typedef IDispEventSimpleImpl</*nID =*/ 2,CWordAddin1, &__uuidof(Office::_CommandBarButtonEvents)> CommandButton1Events; 创建的时候OnConnection加入代码
m_spCmdButton1 = spCmdButton1;
m_spCmdButton2 = spCmdButton2;
CommandButton1Events::DispEventAdvise((IDispatch*)m_spCmdButton1) ;
CommandButton2Events::DispEventAdvise((IDispatch*)m_spCmdButton2) ;
释放的时候OnDisconnection加入代码
CommandButton1Events::DispEventUnadvise((IDispatch*)m_spCmdButton 1);
CommandButton2Events::DispEventUnadvise((IDispatch*)m_spCmdButton 2);
6.编写word菜单插件
创建菜单和创建按基本相似,在创建、响应、连接的地方添加代码,见例程。
注意:
1.添加按钮和菜单的时候,设置文本、图片、标题、可见、可用,但是不能设置Tag属性,否则点击菜单和按钮的时候,会执行两遍。
2.添加右键菜单的时候会有问题,不知道如何响应。
3.关于一些office常量。
按照ole方式加入:添加类-类型库里面的MFC类-从文件或者注册表导入-选择要添加的类。
这种添加方式不会产生常量。
按照atl方式加入:#import mso.dll方式加入,这样编译器会产生常量。
7.注意事项
1.注册
ATL注册:如果本模块基于其他的DLL,注册的时候会链接相应的DLL,一定要能找到。
编译器链接之后会自动注册,如果拷贝到其他地方,要重新注册。
2.连接返回值
MS插件,OnConnection连接的时候,一定要返回S_OK。
否则系统会认为连接的时候出现了异常,没有连接上,下次不会加载插件。
3.加载好的按钮、菜单
已经加载好的按钮、菜单,WSoffice会保存起来,下次加载模块的时候,应该检验,不要重新加载好几次。
可以断开前删除:用户拖动工具条的位置不会保存。
可以加载前删除以前的:缺点同上,还有就是不加载也会有按钮。
可以加载时候检查修改:还有就是不加载也会有按钮。
8.word编程知识点
1.调用word对话框
CDialogs dlgs = wordApp->get_Dialogs();
CDialog0 dlg = dlgs.Item(wdDialogFileSaveAs);//另存为
dlg.Display(timeout)//之显示窗口,不执行代码
dlg.Show(timeout)//显示窗口,如果是保存对话框,真正保存
timeout可选参数,对话框自动关闭,不添为不自动关闭,单位毫秒。
返回值
-2 “关闭”按钮。
-1 “确定”按钮。
0 “取消”按钮。
>0 命令按钮,1 代表第一个按钮,2 代表第二个按钮,以此类推。
2.目录的自动生成
1.把光标定位到文章第1页的首行第1个字符左侧(目录应在文章的前面);
2.执行菜单命令“插入/引用/索引和目录”打开“索引的目录”对话框;
3.在对话框中单击“目录”选项卡,进行相关设置后,单击“确定”按钮,文章的目录自动生成完成。
**友情提示**
目录页码应该与正文页码编码不同。
把光标定位在目录页末,执行“插入/分隔符/下一页/确定”操作,在目录与正文之间插入分页符;
执行“视图/页眉和页脚”命令,把光标定位到正文首页的页脚处,单击“页眉和页脚”工具栏上的“链接到前一个”按钮正文页脚与目录页脚的链接;
执行“插入/页码”命令,在“格式”中选择页码格式、选中“起始页码”为“1”,单击“确定。
至此完成正文的页码插入。
目录如果是多页,插入页码时可以选择与正文页码不同的页码格式。
当然,如果目录只有一页,没有必要插入页码。