http://www.vcgood.com/bbs/forum_posts.asp?tid=1534

最近因?yàn)楣ぷ鞯男枰瑢W(xué)習(xí)了一下Office插件的編寫方法。在走了不少?gòu)澛芬院?,最后終于把編寫插件的原理給搞清楚了,不敢獨(dú)享,拿出來(lái)跟大家共享一下。下面就以Word 2003為例,向大家簡(jiǎn)單介紹一下。
第一步,利用向?qū)梢粋€(gè)ATL COM AppWizard的新工程。


圖1

在向?qū)У牡谝粋€(gè)對(duì)話框中,服務(wù)器類型選擇Dynamic Link Library(DLL),然后單擊Finish即可。

圖2

然后,選取菜單Insert->New ATL Object項(xiàng),在彈出的ATL對(duì)象向?qū)?duì)話框中選中相應(yīng)Objects對(duì)應(yīng)右側(cè)的Simple Object選項(xiàng),點(diǎn)擊下一步。

圖3

在彈出的對(duì)話框中ShortName中輸入相應(yīng)名稱,點(diǎn)確定完成插入ATL對(duì)象。

圖4

這樣一個(gè)簡(jiǎn)單的基于ATL的COM組件工程就建立成功了。

第二步,通過(guò)導(dǎo)入類型庫(kù)來(lái)實(shí)現(xiàn)_IDTExtensibility2接口。在ClassView中的新加的類上點(diǎn)鼠標(biāo)右鍵,在彈出的右鍵菜單中選Implement Interface項(xiàng)。

圖5

在彈出的實(shí)現(xiàn)接口對(duì)話框中點(diǎn)擊Add Typelib

圖6

在彈出的Browse Type Libraries對(duì)話框中,選取Microsoft Add-in Designer(1.0)子項(xiàng),點(diǎn)OK按鈕

圖7

在彈出的接口列表對(duì)話框中選中_IDTExtensibility2接口,點(diǎn)OK按鈕完成導(dǎo)入

這樣的話,系統(tǒng)將會(huì)自動(dòng)為你生成空的五個(gè)所需接口函數(shù),分別是OnConnection、OnDisconnection、OnAddInsUpdate、OnStartupComplete、OnBeginShutdown。

第三步,通過(guò)上面的兩個(gè)步驟,我們的插件框架已經(jīng)形成,但是Office怎么知道啟動(dòng)的時(shí)候要來(lái)把我們的插件Load起來(lái)呢?Office的不同組件,例如Word、Excel、Outlook等怎么知道去Load自己的插件呢?答案就是在注冊(cè)表中加入相應(yīng)的鍵值。打開(kāi)文件視圖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''
                 }
               }
             }
          }
       }
   }
}
以上代碼由三個(gè)需要注意的地方:
1. Office下面的那個(gè)子項(xiàng)代表了這個(gè)插件是屬于那個(gè)組件,Word、Excel、Outlook等等。
2. Addins下面的那個(gè)子項(xiàng)要寫成你添加的COM組件的名字,千萬(wàn)不要照著我的工程的名字照抄。
3. 所有的值兩邊加的都是單引號(hào),而且要用英文下的單引號(hào),不能用雙引號(hào)。
這樣一個(gè)Office插件的框架才算完成,你可以在OnConnection函數(shù)中加一些測(cè)試代碼,看看有沒(méi)有執(zhí)行到,如果執(zhí)行成功才能繼續(xù),否則檢查上面的步驟有沒(méi)有錯(cuò)誤。

第四步,同時(shí)需要import兩個(gè)office的文件,一個(gè)是MSO.dll,另一個(gè)是MSWORD.OLB。這兩個(gè)文件可以在以下位置找到(具體位置與office安裝路徑有關(guān)):
C:\Program Files\Common Files\Microsoft Shared\OFFICE11
C:\Program Files\Microsoft Office\OFFICE11
然后在stdafx.h中加入如下語(yǔ)句:

#import "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE11\\mso.dll" 
        \
        rename_namespace("Office") named_guids,exclude("Pages")
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("Pages")
using namespace Word;
加完以上代碼以后一定要編譯一下,看看是否能夠成功。引入這兩個(gè)文件的原因,主要是為了引入一些變量類型,為后面的創(chuàng)建UI作準(zhǔn)備。
最后一步,編寫代碼。在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)::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("myAddin")); 
      spCmdButton->PutEnabled(VARIANT_TRUE);
      spCmdButton->PutTooltipText(OLESTR("test1")); 
      spCmdButton->PutTag(OLESTR("test1")); 
      spNewCmdBar->PutVisible(VARIANT_TRUE); 
        
      m_spCmdButton = spCmdButton;

這樣,再次打開(kāi)word,就可以看到如圖一所示的界面效果了。

圖9

但是點(diǎn)擊時(shí)沒(méi)有響應(yīng),最后就讓我們來(lái)解決這個(gè)問(wèn)題。
1. 在COutlookAddin繼承類中加入IDispEventSimpleImpl繼承,代碼如下:

class ATL_NO_VTABLE COutlookAddin : 
        public CComObjectRootEx<CComSingleThreadModel>,
        ……
        public IDispEventSimpleImpl<1,COutlookAddin,&__uuidof(Office::_CommandBarButtonEvents)>
        
2. 聲明_ATL_SINK_INFO結(jié)構(gòu)回調(diào)參數(shù)信息。在OutlookAddin.h文件中加入下面語(yǔ)句:
// 按鈕事件響應(yīng)信息聲明
extern _ATL_FUNC_INFO OnClickButtonInfo;
在OutlookAddin.cpp文件中加入定義語(yǔ)句,如下:
// 按鈕事件響應(yīng)信息定義
_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. 加入事件函數(shù)。在OutlookAddin.h中加入聲明:

void __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ 
Ctrl,VARIANT_BOOL * CancelDefault);
在OutlookAddin.cpp中加入實(shí)現(xiàn):
// 工具條按鈕1點(diǎn)擊事件響應(yīng)函數(shù)
void __stdcall CWordAddin::OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault) { MessageBox(NULL, "hello", "world", MB_OK); }

5. 最后,打開(kāi)或斷開(kāi)與接口的連接。方法如下
在OnConnection接口函數(shù)的最后部分,加入下面代碼來(lái)打開(kāi)連接:
  • 在OnConnection接口函數(shù)的最后部分,加入下面代碼來(lái)打開(kāi)連接:
    CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton);
  • 在OnDisconnection接口函數(shù)中,加入下面代碼來(lái)斷開(kāi)連接:
    CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);

綜上所述,編寫一個(gè)簡(jiǎn)單的office的插件,其實(shí)并不難,只要按照步驟一步一步進(jìn)行,肯定能成功,如果大家在使用過(guò)程中有什么疑問(wèn),歡迎一起探討。