最近因为工作的需要,学习了一下Office插件的编写方法。在走了不少弯路以后,最后终于把编写插件的原理给搞清楚了,不敢独享,拿出来跟大家共享一下。下面就以Word 2003为例,向大家简单介绍一下。 第一步,利用向导生成一个ATL COM AppWizard的新工程。
图1
在向导的第一个对话框中,服务器类型选择Dynamic Link Library(DLL),然后单击Finish即可。
图2
然后,选取菜单Insert->New ATL Object项,在弹出的ATL对象向导对话框中选中相应Objects对应右侧的Simple Object选项,点击下一步。
图3
在弹出的对话框中ShortName中输入相应名称,点确定完成插入ATL对象。
图4
这样一个简单的基于ATL的COM组件工程就建立成功了。
第二步,通过导入类型库来实现_IDTExtensibility2接口。在ClassView中的新加的类上点鼠标右键,在弹出的右键菜单中选Implement Interface项。
图5
在弹出的实现接口对话框中点击Add Typelib
图6
在弹出的Browse Type Libraries对话框中,选取Microsoft Add-in Designer(1.0)子项,点OK按钮
图7
在弹出的接口列表对话框中选中_IDTExtensibility2接口,点OK按钮完成导入
这样的话,系统将会自动为你生成空的五个所需接口函数,分别是OnConnection、OnDisconnection、OnAddInsUpdate、OnStartupComplete、OnBeginShutdown。
第三步,通过上面的两个步骤,我们的插件框架已经形成,但是Office怎么知道启动的时候要来把我们的插件Load起来呢?Office的不同组件,例如Word、Excel、Outlook等怎么知道去Load自己的插件呢?答案就是在注册表中加入相应的键值。打开文件视图FileView—>Resource File中的rgs文件,加入以下代码:
HKCU { Software { Microsoft { Office { Word { Addins { ''TestAddin.SimAddin'' { val FriendlyName = s ''WORD Custom Addin'' val Description = s ''Word Custom Addin'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000001'' } } } } } } }
以上代码由三个需要注意的地方: 1. Office下面的那个子项代表了这个插件是属于那个组件,Word、Excel、Outlook等等。 2. Addins下面的那个子项要写成你添加的COM组件的名字,千万不要照着我的工程的名字照抄。 3. 所有的值两边加的都是单引号,而且要用英文下的单引号,不能用双引号。 这样一个Office插件的框架才算完成,你可以在OnConnection函数中加一些测试代码,看看有没有执行到,如果执行成功才能继续,否则检查上面的步骤有没有错误。 第四步,同时需要import两个office的文件,一个是MSO.dll,另一个是MSWORD.OLB。这两个文件可以在以下位置找到(具体位置与office安装路径有关): C:\Program Files\Common Files\Microsoft Shared\OFFICE11 C:\Program Files\Microsoft Office\OFFICE11 然后在stdafx.h中加入如下语句:
#import "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE11\\mso.dll" \ rename_namespace("Office") named_guids,exclude(" ages") using namespace Office;
#import "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.olb" rename_namespace("VBE6") using namespace VBE6;
#import "C:\\Program Files\\Microsoft Office\\OFFICE11\\MSWORD.OLB" rename("ExitWindows","ExitWindowsEx") #import "C:\\Program Files\\Microsoft Office\\OFFICE11\\MSWORD.OLB" \ rename_namespace("Word"), raw_interfaces_only, named_guids ,exclude(" ages") using namespace Word; 加完以上代码以后一定要编译一下,看看是否能够成功。引入这两个文件的原因,主要是为了引入一些变量类型,为后面的创建UI作准备。 最后一步,编写代码。在OnConnection加入如下代码: CComPtr < Office::_CommandBars> spCmdBars; CComQIPtr <Word::_Application> spApp(Application); ATLASSERT(spApp); HRESULT hr = spApp->get_CommandBars(&spCmdBars); if(FAILED(hr)) return hr; ATLASSERT(spCmdBars); CComVariant vName("MyAddin"); CComPtr <Office::CommandBar> spNewCmdBar; CComVariant vPos(1); CComVariant vTemp(VARIANT_TRUE); CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp); CComPtr < Office::CommandBarControls> spBarControls; spBarControls = spNewCmdBar->GetControls(); ATLASSERT(spBarControls); CComVariant vToolBarType(1); CComVariant vShow(VARIANT_TRUE); CComPtr < Office::CommandBarControl> spNewBar; spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar); CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar); ATLASSERT(spCmdButton); HBITMAP hBmp =(HBITMAP): oadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDB_BITMAP),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS); ::OpenClipboard(NULL); ::EmptyClipboard(); ::SetClipboardData(CF_BITMAP, (HANDLE)hBmp); ::CloseClipboard(); : eleteObject(hBmp); spCmdButton-> utStyle(Office::msoButtonIconAndCaption); hr = spCmdButton->PasteFace(); if (FAILED(hr)) return hr; spCmdButton->PutVisible(VARIANT_TRUE); spCmdButton->PutCaption(OLESTR("myAddin")); spCmdButton->PutEnabled(VARIANT_TRUE); spCmdButton->PutTooltipText(OLESTR("test1")); spCmdButton->PutTag(OLESTR("test1")); spNewCmdBar->PutVisible(VARIANT_TRUE); m_spCmdButton = spCmdButton; 这样,再次打开word,就可以看到如图一所示的界面效果了。
图9
但是点击时没有响应,最后就让我们来解决这个问题。 1. 在COutlookAddin继承类中加入IDispEventSimpleImpl继承,代码如下:
class ATL_NO_VTABLE COutlookAddin : public CComObjectRootEx<CComSingleThreadModel>, …… public IDispEventSimpleImpl<1,COutlookAddin,&__uuidof(Office::_CommandBarButtonEvents)> 2. 声明_ATL_SINK_INFO结构回调参数信息。在OutlookAddin.h文件中加入下面语句: // 按钮事件响应信息声明 extern _ATL_FUNC_INFO OnClickButtonInfo; 在OutlookAddin.cpp文件中加入定义语句,如下: // 按钮事件响应信息定义 _ATL_FUNC_INFO OnClickButtonInfo ={CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}}; 3. 加入Sink映射,如下: EGIN_SINK_MAP(COutlookAddin) SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton1, &OnClickButtonInfo) SINK_ENTRY_INFO(2, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton2, &OnClickButtonInfo) SINK_ENTRY_INFO(3, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickMenu, &OnClickButtonInfo) END_SINK_MAP() 4. 加入事件函数。在OutlookAddin.h中加入声明:
void __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault); 在OutlookAddin.cpp中加入实现: // 工具条按钮1点击事件响应函数 void __stdcall CWordAddin::OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault) { MessageBox(NULL, "hello", "world", MB_OK); }
5. 最后,打开或断开与接口的连接。方法如下 在OnConnection接口函数的最后部分,加入下面代码来打开连接: 在OnConnection接口函数的最后部分,加入下面代码来打开连接: CommandButton1Events: ispEventAdvise((IDispatch*)m_spButton); 在OnDisconnection接口函数中,加入下面代码来断开连接: CommandButton1Events: ispEventUnadvise((IDispatch*)m_spButton); 综上所述,编写一个简单的office的插件,其实并不难,只要按照步骤一步一步进行,肯定能成功,如果大家在使用过程中有什么疑问,欢迎一起探讨。 |
|
天地茫茫不知所止 日月循环周而复始
|
|
|
|
|
![Rank: 9](http://pubimage.360doc.com/wz/default.gif) ![Rank: 9](http://pubimage.360doc.com/wz/default.gif) ![Rank: 9](http://pubimage.360doc.com/wz/default.gif)
|
2#
发表于 2007-9-10 11:14 | 只看该作者
仔细研究一下C++代码就可以了解如何编写Office 2000插件。
Office 2000插件其实就是一个实现了IDTExtensibility2接口的自动化对象。IDTExtensibility2 接口相当简单,插件需要实现接口定义的全部5个函数:
OnConnection:当应用程序连接到插件时会调用这个函数。插件在函数中接收下列初始化信息——应用程序对象模型进入点的指针,连接模式(是手工加入还是通过命令行载入), 应用程序的对象模型指针和用户自定义的信息。
OnDisconnection:当应用程序断开插件时被调用,插件应该在这里清除先前分配的资源,删除它添加到应用程序的界面元素。
OnStartupComplete:这个函数是当应用程序自动启动插件时被调用的。调用时,其他的插件都已经被加载到了内存,这时可以同其他插件进行通信。这个函数还适合添加用户界面元素。
OnBeginShutdown:当应用程序准备关闭并将要断开插件时会被调用,这时插件应该停止接收用户输入。
OnAddInsUpdate:当注册的插件列表被改变后会被调用。如果我们的插件不依赖于其他插件,这个函数可以为空。
|
|
|