Office2000下内部COM插件的编程实现(之二)

原创 2003年02月08日 12:01:00
 

Office应用程序中,尽管菜单和工具栏按钮看上去不太一样,但实质上它们是相同类型的对象。CommandBars集合包含程序中的所有命令条,如:工具条和菜单条。每一个CommandBars集合都有一个CommandBar对象和它对应,CommandBar 对象可以包含其它的 CommandBar 对象,这些对象是作为按钮或菜单命令来用的。每一个CommandBar都将通过CommandBarControls 对象被引用,CommandBarControls又可以包含一组CommandBarControl对象。每一个CommandBarControl可以包含一个CommandBar对象,并可以通过它来存取控件属性。每一个CommandBarControl对象,实际是对应CommandBarControls中的控件集合。CommandBarControl可以有三种表现形式:

l         弹出式(CommandBarPopup):相当于菜单条的一个菜单项。

l         组合框(CommandBarComboBox):类似于工具条中组合框控件。它包括一个工具栏和紧接着工具栏的一个下拉箭头。单击该按钮,将显示出更多的带图标的菜单命令。

l         按钮(CommandBarButton)相当于标准的工具栏按钮,即带有图标的按钮。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

在下面的示例程序中,我们将在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派发接口的响应事件是_CommandBarButtonEventsATL提供了二种模板类IDispEventImpl<>IDispEventSimpleImpl<>来实现接口事件的接收,这里我们使用IDispEventSimpleImpl来实现(因为它不需要额外的类型库信息)。它需要设置SINK(接收)映射,通过_ATL_SINK_INFO结构来回调参数信息,最终通过DispEventAdviseDispEventUnadvise来与源接口连接或断开。实现方法如下:

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.       最后,打开或断开与接口的连接。方法如下:

l         OnConnection接口涵数的最后部分,加入下面代码来打开连接:

CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton);

l         OnDisconnection接口涵数中,加入下面代码来断开连接:

CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);

 

到此就完成一个Office内部插件的最小需求了,大家可以编译后打开Outlook2000看看效果如何,详细代码可参看文章所带示例源码,内有详细注释。

 

参考文献:

Building an Office2K COM addin with VC++/ATL -- Amit Dey

ATL开发指南(第二版) – Tom Armstrong & Ron Patton

 

联系方式:

地址:陕西省西安市劳动路2号院六单元

邮编:710082

编者EMAIL:jingzhou_xu@163.net

未来工作室(Future Studio)

COM编程之二 接口

【1】接口 DLL的接口是它所输出的那些函数。 C++类的接口是该类的一个成员函数集。 COM接口是包含一个函数指针数组的内存结构。 每一个数组元素包含的是一个由组件所实现的函数的地址。 在...
  • liujiayu2
  • liujiayu2
  • 2015年04月16日 18:30
  • 320

COM组件编程—理论知识

1、 COM组件是一个C++类,他从一系列接口派生而来,并实现了所有的接口。这些接口都是纯虚类。 2、COM组件有三个最基本的接口类,分别是IUnknown、IClassFactory、IDispa...
  • u012905728
  • u012905728
  • 2015年02月11日 09:47
  • 1655

【C/C++多线程编程之二】pthread简介

声明:本人博客均为原创内容,未经许可严禁转载!                                                         多线程编程之pthread简介  ...
  • Jiangweihll
  • Jiangweihll
  • 2014年04月14日 15:08
  • 5703

COM编程精彩实例--COM通信

COM是如何进行通信的: ////// ①代理/桩子或COM DLL将方法的调用参数转化成NDR格式; ②COM调用RPC将格式化的数据传递给服务器端的COM代理/桩...
  • zang141588761
  • zang141588761
  • 2016年12月22日 14:17
  • 184

COM编程精彩实例--COM对象

创建一个COM对象的简单过程: 1)调用COM API接口 ::CoCreateInstance(),可以直接调用或通过一个已经包装的c++类调用。这个调用需要指定两个ID,一个是要实例化的类的ID...
  • zang141588761
  • zang141588761
  • 2016年12月20日 15:28
  • 678

COM组件编程—自动化

1、接口分类 调度接口(dispinterface) :IDispatch::Invoke的一个实现所能调用的函数集合,客户只能通过IDispatch::Invoke使用组件。 COM(vbtl)接口...
  • u012905728
  • u012905728
  • 2015年02月11日 17:41
  • 740

activeX com 组件开发 一

com组件开发  可以用vb   c#  vc++  等   个人对c#比较熟练 ,但是c# 做控件弊端太大,要求客户装framework 这比较强人所难 ,当然vb 也要装运行库库 ,vc++可以装...
  • studypower
  • studypower
  • 2014年09月27日 14:06
  • 1373

COM组件开发(一)—— 对象与接口

一.前言        在COM规范中,最基本的两个要素就是对象与接口,因为COM就是由这两者来共同实现的。COM对象在组件中是被封装起来的,客户代码只能通过接口来访问COM对象并享受其,由于客户与C...
  • q5806622
  • q5806622
  • 2015年01月19日 23:08
  • 3517

COM原理与实现之二: 聚合

COM原理与实现之二: 聚合C++没有同聚合等价的特性。聚合实际上是继承性的一种动态形式。而C++的继承总是静态的,是实现继承。COM是接口继承,通过聚合接口,可以做成动态配置。研究COM,主要是利用...
  • cheungmine
  • cheungmine
  • 2016年06月14日 00:05
  • 5901

【自学C++】魔兽世界之二:装备

魔兽系列又出新篇章~其实就是在上周的基础上加入几个属性(武器,shiqi)
  • u014025300
  • u014025300
  • 2014年04月06日 20:22
  • 2863
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Office2000下内部COM插件的编程实现(之二)
举报原因:
原因补充:

(最多只允许输入30个字)