實現Office的COM插件
作者: ZDNet China
2003-08-11 01:43 PM

當軟件開發者考慮擴展Microsoft Office時,最常想到方法就是是使用VB了。實際上,用C++和ATL擴展Office是相當容易的。

我將向你演示如何建立項目以及如何把Office插件作為COM對象注冊到Office中去。(本文用Outlook來作為插件的主程序。)

項目的建立

讓我們建立一個新的ATL項目。首先,添加一個叫著Plugin的簡單COM對象。用類向導把_IDTExtensibility2接口添加到Plugin類中。(別忘了選中IsupportErrorInfo復選框。)

類向導會把下述方法添加到Plugin類中:OnConnection、OnDisconnection、OnAddinsUpdate、 OnStartupComplete、OnBeginShutdown。本文只處理OnConnection和OnDisconnection方法。

然后,在Plugin頭文件中創建一個類型定義(typedef,它只是用來簡化代碼的),如下所示:

#define APPID 102
class CPlugin;
typedef IDispEventImpl<APPID, CPlugin, &DIID_ApplicationEvents,
&LIBID_Outlook,9,0> OutLookSink;

我們現在需要在Plugin類的繼承列表中添加OutLookSink類:

class ATL_NO_VTABLE CPlugin :
???????public IPlugin,
???????public IDispatchImpl<_IDTExtensibility2, &__uuidof(_IDTExtensibility2),
???????????????&LIBID_AddInDesignerObjects, /* wMajor = */ 1, /* wMinor = */ 0>,
????public OutLookSink

在Plugin.h文件中為Mso9.dll和Msoutl9.olb引入聲明,如下所示:

#import "C:\Program Files\Common Files\Designer\msaddndr.dll" \
raw_interfaces_only, \
raw_native_types, no_namespace, named_guids, auto_search

#import "C:\Program Files\Microsoft Office\Office\mso9.dll" \
rename_namespace("Office") named_guids

#import "C:\Program Files\Microsoft \
?Office\Office\msoutl9.olb" \
rename_namespace("Outlook"), raw_interfaces_only, \
?named_guids

using namespace Office;
using namespace Outlook;

注意,你需要按照你的系統中的文件路徑來修正上面那些被引入文件的路徑。



你必須把Office擴展作為COM組件注冊到Office中去。Visual C++自動產生.rgs文件來控制COM注冊。我們將需要添加額外的項目加到.rgs文件來支持Office的自動注冊。

在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office鍵下,有若干個子鍵,每一個子鍵對應于系統中安裝的每一個Office應用程序。在這些鍵中有插件關鍵字(add-in key)。


為了在Office中注冊我們的COM對象,我們需要在插件鍵中添加新鍵。在HKEY_CURRENT_USER下同樣有對應的樹來控制單個用戶的插件,而HKEY_LOCAL_MACHINE控制整個機器的插件。

下面是.rgs文件中Outlook的插件注冊碼:

HKCU
{
????NoRemove Software
????{
????????NoRemove Microsoft
????????{
????????????NoRemove Office
????????????{
????????????????NoRemove Outlook
????????????????{
????????????????????NoRemove Addins
????????????????????{
????????????????????????ForceRemove OutlookDemo.Plugin.1
????????????????????????{
????????????????????????????val FriendlyName = s 'Outlook Plugin'
????????????????????????????val Description = s 'An Outlook plugin'
????????????????????????????val LoadBehavior = d 3
????????????????????????????val CommandLineSafe = d 0
????????????????????????}
????????????????????????
????????????????????}
????????????????}
????????????}
????????}
????}
}

這套注冊項是為特定用戶注冊插件的,為了把插件設置為整個系統范圍之內可用,只需把HKCU改為HKLM。



LoadBehavior的值控制了主程序運行后插件的行為。為了讓主程序開始運行后插件自動運行,把LoadBehavior的值設置為3。如果想讓用戶手工載入插件,就把LoadBehavior設置為8。

如果你編譯了這個項目,你就有了一個Outlook 插件;這個插件實際上沒有做任何事,但是它的確是一個可以工作的插件。

我們現在向這個插件添加一些功能。首先,添加一個新的成員變量:

?????_ApplicationPtr m_pApp;

然后設置OnConnection方法:

????STDMETHOD(OnConnection)(LPDISPATCH Application,
?????????????????????????????????????????????????????? ?????ext_ConnectMode ConnectMode,
?????????????????????????????????????????????????????? ?????LPDISPATCH AddInInst,
?????????????????????????????????????????????????????? ?????SAFEARRAY * * custom)
????{
?????????????HRESULT rslt = S_OK;
?????????????try{
??????????????????m_pApp = Application;
????????????????? OutLookSynch::DispEventAdvise(m_pApp);
?????????????}catch(_com_error &e){
????????????????? OutputDebugString(e.ErrorMessage());
????????????????????????????????????rslt = E_UNEXPECTED;????????????????
?????????????}
?????????????return rslt;
????}

再設置與之對應的OnDisconnection方法,如下所示:

??? STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode,SAFEARRAY
* * custom)
??????{
?????????????????HRESULT rslt = S_OK;
?????????????????try{
?????????????????????? OutLookSynch::DispEventUnadvise(m_pApp);
?????????????????}catch(_com_error &e){
?????????????????????? OutputDebugString(e.ErrorMessage());
???????????????????????rslt = E_UNEXPECTED;
?????????????????}
?????????????????return rslt;
????}

現在我們可以收到好幾種事件,例如,OnNewMail事件。得到事件的信息以及它們的ID的最好方法就是打開MSVC工具菜單上的OLE/COM觀察器(viewer)。

  1. 進入File菜單并選擇View Typelib。
  2. 瀏覽Msoutl9.olb文件。
  3. Scroll down ApplicationEvents的dispinterface。注意OnNewMail事件的ID號為0x0000f003.。
  4. 向Cplugin類添加一個叫著OnNewMail的方法。
  5. 建立一個Sink映射來把OnNewMail方法關聯到OnNewMail事件。

????BEGIN_SINK_MAP(CPlugin)
??????????????SINK_ENTRY_EX(APPID, Outlook::DIID_ApplicationEvents, /
0x0000f003, OnNewMail)
????END_SINK_MAP()

現在我們只需要完成OnNewMail事件處理函數了。

????void _stdcall OnNewMail(){
???????????MessageBox(NULL, "New Mail", NULL, MB_OK);
????}

如果你想消除這些討厭的彈出式對話框(由該項目所創建),那么最簡單方法就是遵循下面的步驟:

  1. 進入Outlook的Tools | Options菜單項。
  2. 選擇Other標簽。
  3. 點擊Advanced Options按鈕。
  4. 點擊COM 的插件按鈕。
  5. 選中Outlook的Plugin項并點擊Remove。
如你所見,Office的COM插件實現起來相對來說并不麻煩甚至可以說很輕松。創建一個COM Office插件最困難的計算出所有參數的初值。不過一旦你發現可以在OLE/COM查看器中得到這些信息,這也就不成問題了。