Office2000下内部COM插件的编程实现 下载示例源代码 ProgID键值下主要创建以下四个键值: 具体实现 接着,选取菜单Insert->New ATL Objec项,在弹出的ATL对象向导对话框中选中相应Objects对应右侧的Simple Object选项,点击下一步,在弹出的对话框中ShortName中输入OutlookAddin,如果需要的话,还可以在Attributes(属性页)中选中Support ISupportErrorInfo复选框选项,点OK完成插入ATL对象。 接着通过导入类型库来实现_IDTExtensibility2接口,编释好上面所建工程后,在ClassView中的COutlookAddin类上点鼠标右键,在弹出的右键菜单中选Implement Interface项。在弹出的实现接口对话框中点击Add Typelib,在弹出的Browse Type Libraries对话框中,向下滚动选取Microsoft Add-in Designer(1.0)子项,点OK按钮。在弹出的接口列表对话框中选中_IDTExtensibility2接口,点OK按钮完成导入。系统会自动为你生成空的上面所提到的五个所需接口函数。 接着注册编译好的插件,在FileView->Resource Files中,打开OutlookAddin.rgs注册文件,在该文件的最下面加入下面新的内部插件注册码: // 新增Outlook2k内部插件注册键值 HKCU { Software { Microsoft { Office { Outlook { Addins { ''OutlAddin.OutlookAddin'' { val FriendlyName = s ''Outlook2000插件'' val Description = s ''使用ATL开发的Outlook2000的插件'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000000'' } } } } } } }编译此工程,如果注册正确的话,将可以在Outlook中COM加载项的插件对话框中看到它的相应名称。在Office2000中加载或卸载COM插件,一般可以按下面步骤进行: 1. 如果已经将“COM 加载项”命令添加到了“工具”菜单,请跳到第 6 步。 2. 单击“工具”菜单中的“自定义”命令,然后单击“命令”选项卡。 3. 在“类别”框中,选择“工具”。 4. 将“COM 加载项”从“命令”框拖动到“工具”菜单。当“工具”菜单显示菜单命令时,将鼠标指针指向希望“COM 加载项”命令出现在菜单上的位置,然后释放鼠标按钮。 5. 单击“关闭”按钮。 6. 单击“工具”菜单中的“COM 加载项”命令,并执行下列操作之一: ● 要添加加载项,请选中“可使用的加载项”列表中加载项名称旁边的复选框。如果所需的加载项不在“可使用的加载项”列表中,请单击“添加”按钮,找到要添加的加载项,然后单击“确定”按钮。 ● 要从内存中卸载加载项,但希望在列表中保持其名称,请清除加载项名称旁边的复选框。 ● 若要从列表中删除加载项的同时,将其从注册表文件中注册的加载项列表中删除,请选择加载项的名称,然后单击“删除”按钮。 在Office应用程序中,尽管菜单和工具栏按钮看上去不太一样,但实质上它们是相同类型的对象。CommandBars集合包含程序中的所有命令条,如:工具条和菜单条。每一个CommandBars集合都有一个CommandBar对象和它对应,CommandBar 对象可以包含其它的 CommandBar 对象,这些对象是作为按钮或菜单命令来用的。每一个CommandBar都将通过CommandBarControls 对象被引用,CommandBarControls又可以包含一组CommandBarControl对象。每一个CommandBarControl可以包含一个CommandBar对象,并可以通过它来存取控件属性。每一个CommandBarControl对象,实际是对应CommandBarControls中的控件集合。CommandBarControl可以有三种表现形式: ● 弹出式(CommandBarPopup):相当于菜单条的一个菜单项。 ● 组合框(CommandBarComboBox):类似于工具条中组合框控件。它包括一个工具栏和紧接着工具栏的一个下拉箭头。单击该按钮,将显示出更多的带图标的菜单命令。 ● 按钮(CommandBarButton):相当于标准的工具栏按钮,即带有图标的按钮。 在下面的示例程序中,我们将在Outlook2K中新建一个工具条并在其上添加二个按钮,并且在其菜单“工具”中新建一个菜单条,这些操作都可以在OnConnection接口函数中完成。 首先,我们需要在工程中导入Office和Outlook类型库,可以在Stdafx.h文件中加入下面语句(注意:其中路径可根据Office所装路径自行设定): // 导入工程所需Office2K及Outlook2K类型库 #import "e:/Program Files/Microsoft Office/Office/mso9.dll" rename_namespace("Office"), named_guids using namespace Office; #import "e:/Program Files/Microsoft Office/Office/MSOUTL9.olb" rename_namespace("Outlook"), raw_interfaces_only, named_guids using namespace Outlook;其次,让我们来在Outlook中新建一个工具条,并且在其上添加两个按钮。 代码如下: // 装卸插件时处理 STDMETHOD(OnConnection)(IDispatch * Application, ext_ConnectMode ConnectMode, IDispatch * AddInInst, SAFEARRAY * * custom) { CComPtr < Office::_CommandBars> spCmdBars; // Outlook应用接口_Application CComQIPtr <Outlook::_Application> spApp(Application); ATLASSERT(spApp); // 获取CommandBars接口 CComPtr<Outlook::_Explorer> spExplorer; spApp->ActiveExplorer(&spExplorer); HRESULT hr = spExplorer->get_CommandBars(&spCmdBars); if(FAILED(hr)) return hr; ATLASSERT(spCmdBars); // 新增一个工具条及其上两个位图按钮 CComVariant vName("新增Outlook2K工具条插件"); CComPtr <Office::CommandBar> spNewCmdBar; // 新增工具条位置 CComVariant vPos(1); CComVariant vTemp(VARIANT_TRUE); // 临时 CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); // 用Add方法在指定位置新增一工具条并让spNewCmdBar指向它 spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp); // 获取新增工具条的CommandBarControls,从而在其上添加按钮 CComPtr < Office::CommandBarControls> spBarControls; spBarControls = spNewCmdBar->GetControls(); ATLASSERT(spBarControls); //MsoControlType::msoControlButton = 1 CComVariant vToolBarType(1); //显示工具条 CComVariant vShow(VARIANT_TRUE); CComPtr < Office::CommandBarControl> spNewBar; CComPtr < Office::CommandBarControl> spNewBar2; // 用CommandBarControls中的Add方法新增第一个按钮,并让spNewBar指向它 spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar); // 用CommandBarControls中的Add方法新增第二个按钮,并让spNewBar2指向它 spNewBar2 = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar2); // 为每一个按钮指定_CommandBarButton接口,从面可以指定按钮的显示风格等 CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar); CComQIPtr < Office::_CommandBarButton> spCmdButton2(spNewBar2); ATLASSERT(spCmdButton); ATLASSERT(spCmdButton2); // 设置位图按钮风格,位图为32x32大小,将其放入剪切板中用PasteFace()贴在指定按钮上 HBITMAP hBmp =(HBITMAP)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDB_BITMAP),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS); ::OpenClipboard(NULL); ::EmptyClipboard(); ::SetClipboardData(CF_BITMAP, (HANDLE)hBmp); ::CloseClipboard(); ::DeleteObject(hBmp); // 粘贴前设置显示风格 spCmdButton->PutStyle(Office::msoButtonIconAndCaption); hr = spCmdButton->PasteFace(); if (FAILED(hr)) return hr; spCmdButton->PutVisible(VARIANT_TRUE); spCmdButton->PutCaption(OLESTR("按钮1")); spCmdButton->PutEnabled(VARIANT_TRUE); spCmdButton->PutTooltipText(OLESTR("按钮1提示信息")); spCmdButton->PutTag(OLESTR("按钮1标志")); // 显示新增工具条 spNewCmdBar->PutVisible(VARIANT_TRUE); // 设置第二个工具条按钮风格 spCmdButton2->PutStyle(Office::msoButtonIconAndCaption); // 第二个按钮指定位图为Outlook2K中预先定义的位图 spCmdButton2->PutFaceId(1760); spCmdButton2->PutVisible(VARIANT_TRUE); spCmdButton2->PutCaption(OLESTR("按钮2")); spCmdButton2->PutEnabled(VARIANT_TRUE); spCmdButton2->PutTooltipText(OLESTR("按钮2提示信息")); spCmdButton2->PutTag(OLESTR("按钮2标志")); spCmdButton2->PutVisible(VARIANT_TRUE); m_spButton = spCmdButton; m_spButton2 = spCmdButton2; ……接着,让我们在菜单"工具"中新建一个菜单条。 代码如下: _bstr_t bstrNewMenuText(OLESTR("新增菜单条")); CComPtr < Office::CommandBarControls> spCmdCtrls; CComPtr < Office::CommandBarControls> spCmdBarCtrls; CComPtr < Office::CommandBarPopup> spCmdPopup; CComPtr < Office::CommandBarControl> spCmdCtrl; CComPtr < Office::CommandBar> spCmdBar; // 通过CommandBar获取Outlook主菜单 hr = spCmdBars->get_ActiveMenuBar(&spCmdBar); if (FAILED(hr)) return hr; // 获取菜单条的CommandBarControls spCmdCtrls = spCmdBar->GetControls(); ATLASSERT(spCmdCtrls); // 在第5个"工具"菜单下新增一菜单条 CComVariant vItem(5); spCmdCtrl= spCmdCtrls->GetItem(vItem); ATLASSERT(spCmdCtrl); IDispatchPtr spDisp; spDisp = spCmdCtrl->GetControl(); // 获取菜单条CommandBarPopup接口 CComQIPtr < Office::CommandBarPopup> ppCmdPopup(spDisp); ATLASSERT(ppCmdPopup); spCmdBarCtrls = ppCmdPopup->GetControls(); ATLASSERT(spCmdBarCtrls); CComVariant vMenuType(1); // 控件类型 - menu CComVariant vMenuPos(6); CComVariant vMenuEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); CComVariant vMenuShow(VARIANT_TRUE); // 菜单将显示 CComVariant vMenuTemp(VARIANT_TRUE); // 临时 CComPtr < Office::CommandBarControl> spNewMenu; // 用Add方法创建新的菜单条 spNewMenu = spCmdBarCtrls->Add(vMenuType, vMenuEmpty, vMenuEmpty, vMenuEmpty, vMenuTemp); ATLASSERT(spNewMenu); spNewMenu->PutCaption(bstrNewMenuText); spNewMenu->PutEnabled(VARIANT_TRUE); spNewMenu->PutVisible(VARIANT_TRUE); // 利用CommandBarButton来在菜单条前显示位图 CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu); ATLASSERT(spCmdMenuButton); spCmdMenuButton->PutStyle(Office::msoButtonIconAndCaption); // 同新增工具条第一个按钮位图相同方法 spCmdMenuButton->PasteFace(); // 显示菜单 spNewMenu->PutVisible(VARIANT_TRUE); m_spMenu = spCmdMenuButton;这样,通过在Outlook中通过上面提到的方法加载COM插件,就可以看到如图一所示的界面效果了,但是点击时没有响应,最后就让我们来解决这个问题。 工具条按钮CommandBarButton派发接口的响应事件是_CommandBarButtonEvents。ATL提供了二种模板类IDispEventImpl<>和IDispEventSimpleImpl<>来实现接口事件的接收,这里我们使用IDispEventSimpleImpl来实现(因为它不需要额外的类型库信息)。它需要设置SINK(接收)映射,通过_ATL_SINK_INFO结构来回调参数信息,最终通过DispEventAdvise和DispEventUnadvise来与源接口连接或断开。实现方法如下: 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 COutlookAddin::OnClickButton1(IDispatch* /*Office::_CommandBarButton* */ Ctrl, VARIANT_BOOL * CancelDefault) { USES_CONVERSION; CComQIPtr<Office::_CommandBarButton> pCommandBarButton(Ctrl); HINSTANCE result=ShellExecute(NULL, _T("open"), _T("http://www.vckbase.com"), NULL,NULL, SW_SHOW); }5. 最后,打开或断开与接口的连接。方法如下 ● 在OnConnection接口函数的最后部分,加入下面代码来打开连接: CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton);● 在OnDisconnection接口函数中,加入下面代码来断开连接: CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);到此就完成一个Office内部插件的最小需求了,大家可以编译后打开Outlook2000看看效果如何,详细代码可参看文章所带示例源码,内有详细注释。 参考文献: 译者联系方式: |