`
cuijiemin
  • 浏览: 265423 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Office2000下内部COM插件的编程实现

阅读更多


译者:徐景周

下载示例源代码

简介
你也许曾在Office2000下的Word2000、Access2000、Excel2000、PowerPoint2000等软件中的工具条或菜单条资源中,看到一些其它软件加入的新的自定义工具条按钮或菜单条,当点击它们时,会有其不同的响应发生。下面,让我们也来实现这些功能,需要说明的是,在这里我们不用VB/VBA来实现它,而是用VC6中所带ATL(活动模板库)3.0来开发具有这种效果的Office2000内部COM插件。在Office2000中,不管是Word2000、Access2000、Excel120000、PowerPoint2000还是Outlook2000等,它们COM插件的编程方法及步骤都是极其相似的(除注册表中键值及导入相应类型库不同外)。

基础知识
一个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: 当应用程序关闭时插件刚缷载完成时处理。

注册插件
只有在正确注册了相应应用程序的内部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(假)。
其它键值的完整描述可参看最新MSDN。

具体实现
下面,我们将以创建一个Outlook2000的内部COM插件为示例,向你一步步的展现如何最小化的创建一个Office2000的内部插件的全过程。效果图如下所示:

打开VC6.0,在新建工程中选中ATL COM Appwizard,在右侧工程名中输入OutlAddin,点击下一步,接受默认选项Dynamic Link Library(DLL)不变,可以选中下面的Allow merging of proxy-stub code(允许合并代理/占位)复选框选项,点击Finish(完成)按钮完成工程创建。

接着,选取菜单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看看效果如何,详细代码可参看文章所带示例源码,内有详细注释。

参考文献:
Building an Office2K COM addin with VC++/ATL -- Amit Dey
ATL开发指南(第二版) – Tom Armstrong & Ron Patton

分享到:
评论

相关推荐

    Office2000下内部COM插件的编程实现.zip_com实例_com组件_office2000

    本教程将深入探讨如何在Office 2000环境下使用COM组件进行插件编程。 首先,我们需要了解COM的基本概念。COM是基于接口的,允许对象之间进行通信而无需知道对方的具体实现。它定义了一套标准,使得不同语言编写的...

    Word2000/XP中内部COM插件的编程实现(源码)

    使用ATL开发Word2000/XP中内部COM插件的编程实现,可以在Word中插入新的工具条按钮并响应其操作。 未来工作室(Future Studio)

    Access2000下内部COM插件编程示例 .zip_COM_com 插件

    在Access2000下进行内部COM插件编程,主要是为了扩展Access的功能,允许开发者创建自定义的命令、工具栏、菜单等,以满足特定的业务需求。下面将详细介绍Access2000下COM插件编程的相关知识点。 1. **COM组件基础**...

    Word2000&XP中内部COM插件的编程实现(源码)(1[1].0).zip

    本资料主要探讨如何在Word2000和XP中实现内部COM插件的编程,并提供了源码供学习参考。 COM插件,也称为COM组件或ActiveX控件,是遵循COM规范的动态链接库(DLL),可以在运行时被不同程序调用,实现功能扩展。在...

    Excel2000&XP和PowerPoint2000&XP下内部COM插件实现(源码)

    标题中的“Excel2000&XP和PowerPoint2000&XP下内部COM插件实现(源码)”指的是在Microsoft Office的两个主要组件,Excel 2000和2002(XP)以及PowerPoint 2000和2002(XP)中,利用COM(Component Object Model)技术...

    ASP+office在线编辑插件,支持手写签名等

    在实际应用中,ASP+Office在线编辑插件可以广泛应用于各种业务场景,比如企业内部文档协作、在线教育平台的作业提交、电子表格的共享编辑等。通过这种方式,用户无需安装Office软件,只需通过浏览器即可完成文档操作...

    细细品味C#(Office插件开发)

    1、Office2000 下内部COM插件的编程实现.................................................................................2 1.1、版权声明......................................................................

    visual c++ VC COM ATL为Office PowerPoint PPT编写插件.zip

    插件通常通过实现IDTExtensibility2接口与PowerPoint进行交互,这是Office VSTO(Visual Studio Tools for Office)的一部分。 4. **工程结构**:PPTAddin可能包含以下几个部分:主程序(addin)文件,定义了插件的...

    Office编程手册合集(CHM)

    Office编程是IT领域中一个重要的细分方向,尤其对于那些需要自动化办公流程、开发定制化办公应用的程序员来说,掌握Office编程技巧是必不可少的。本"Office编程手册合集(CHM)"提供了一份详尽的资源,涵盖了Excel、...

    office插件开发

    虽然VSTO插件提供了丰富的功能,但它们是运行在Office进程内部的,可能会影响应用程序的性能。开发者应优化代码,避免不必要的资源消耗。此外,VSTO插件通常只适用于Windows环境,对macOS或移动设备的支持有限。 ##...

    关于插件编写的一个例子--word2003

    1. **COM编程**:Word 2003是基于COM(Component Object Model)架构的,因此,大多数插件开发会涉及到COM组件的创建。这通常使用Visual Basic 6、C++或.NET Framework(如C#、VB.NET)来完成。COM组件允许我们的...

    weboffice控件的使用

    例如,在企业内部,WebOffice 控件可以用于在线文档编辑和共享,提高工作效率和协作效率。在教育领域,WebOffice 控件可以用于在线学习和协作,提高教学效率和学生参与度。 4. WebOffice 控件的主要功能 WebOffice...

    Visual Basic实现Office助手

    Visual Basic可以与Microsoft Office应用程序进行深度集成,利用Microsoft Office的对象模型,我们可以访问到Word、Excel、PowerPoint等组件的内部对象,实现对文档的读写、格式修改、宏执行等功能。例如,使用VB...

    WPS office-VBA插件

    总之,WPS Office VBA插件通过引入VBA功能,极大地增强了WPS Office的可扩展性和定制性,让办公软件不仅仅局限于基础的文档编辑,还能实现高效的工作流自动化。无论是个人用户还是企业开发者,都能从中受益。

    PowerPoint2000/XP内部COM插件VC源代码

    在"PowerPoint2000/XP内部COM插件VC源代码"中,开发者可能使用了MFC的COleObjectFactory和COleServerDoc等类来实现COM服务器的部分。COleObjectFactory用于创建和注册COM对象,而COleServerDoc则处理服务器文档,...

    Office插件开发-为Outlook添加Ribbon按钮源代码

    在IT领域,开发Office插件是一项常见的任务,它允许用户自定义Microsoft Office应用程序,如Outlook,以增强其功能或适应特定的工作流程。本资源主要关注如何为Outlook添加Ribbon按钮,这是一个实用的增强功能,可以...

    WPS支持VBA 7.1版本 VBA For WPS 2019(1)插件,大家打开vba组件所需

    总之,WPS Office通过支持VBA 7.1和提供相应的插件,为用户提供了与Microsoft Office相当的编程能力,使用户能够利用宏来增强其办公软件的功能。了解和掌握VBA,无论对于个人还是企业用户,都是提升办公效率的有效...

    java插件

    Java插件是编程领域中的一种特定工具,它扩展了Java平台的功能,允许开发者在特定环境中增加自定义功能或实现特定任务。这篇博文链接指向的是ITEYE博主“易信乎”的一篇博客,虽然描述部分为空,但我们可以从标题和...

    Visual2010开发Excel2010插件

    通过学习这个主题,开发者不仅可以提升自己的编程技能,还能深入理解Office应用程序的内部机制,为日常办公带来更多的便利和效率提升。提供的资源文件涵盖了从概念到实践的全部内容,对于想要涉足此领域的学习者来说...

Global site tag (gtag.js) - Google Analytics