首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Office 2000 內部 COM 增益集開發實戰:以 ATL 打造 Outlook 2000 增益集

     編輯:關於VC++

前言

在使用 Office 2000 套件(Word、Excel、PowerPoint、Access、Outlook)時,你或許注意到工具列或功能表上會出現其他軟體新增的自訂按鈕或選單項目。點擊它們會觸發特定功能。本文將示範如何使用 Visual C++ 6.0 搭配 ATL 3.0(Active Template Library)開發 Office 2000 內部 COM 增益集,以 Outlook 2000 為例完整呈現開發流程。Office 2000 各元件的增益集開發方式高度相似,僅在登錄機碼與匯入類型庫上略有差異。

核心基礎:_IDTExtensibility2 介面

所有 Office 2000 內部 COM 增益集必須實作 _IDTExtensibility2 分派介面,該介面定義於 MSADDN Designer 類型庫(MSADDNDR.dll/MSADDNDR.tlb),通常位於 <磁碟機>/Program Files/Common Files/Designer。此介面包含五個必須實作的方法:

  1. OnConnection:增益集載入記憶體時觸發(可設定啟動時自動載入)。
  2. OnDisconnection:增益集從記憶體卸載時觸發。
  3. OnAddInsUpdate:COM 增益集集合變更時觸發。
  4. OnStartupComplete:應用程式啟動完成、增益集剛載入完畢時觸發。
  5. OnBeginShutdown:應用程式關閉、增益集剛卸載完畢時觸發。

實務上主要在 OnConnection 與 OnDisconnection 撰寫邏輯。

增益集登錄機制

唯有正確在登錄表建立對應機碼,宿主應用程式才會載入增益集。路徑格式為:

HKEY_CURRENT_USER\Software\Microsoft\Office\<TheOfficeApp>\Addins\<ProgID>

TheOfficeApp 為應用程式名稱(如 Word、Outlook),ProgID 為增益集的唯一識別字串(如 Outlook2000Addin.Addin)。其下需建立四個主要值:

  • FriendlyName(字串):顯示於 COM 增益集對話方塊的名稱。
  • Description(字串):增益集描述資訊。
  • LoadBehavior(DWORD):0x03 為應用程式啟動時自動載入(常用);0x08 為使用者手動控制載入。
  • CommandLineSafe(DWORD):0x01(真)或 0x00(假)。

實作範例:建立 Outlook 2000 COM 增益集

以下以建立一個可在 Outlook 2000 新增工具列按鈕與功能表項目的增益集為例。

步驟 1:建立 ATL COM 專案

  1. 開啟 VC6.0,選擇 ATL COM AppWizard,專案命名為 OutlAddin。
  2. 保持預設 Dynamic Link Library (DLL),可勾選 Allow merging of proxy-stub code,點擊完成。

步驟 2:新增 ATL 簡單物件

  1. 選單 Insert → New ATL Object,選取 Simple Object。
  2. Short Name 輸入 OutlookAddin,可勾選 Support ISupportErrorInfo,確定完成。

步驟 3:實作 _IDTExtensibility2 介面

  1. 先編譯專案產生類型庫。
  2. 在 ClassView 中對 COutlookAddin 類別右鍵選擇 Implement Interface。
  3. 點擊 Add Typelib,選取 Microsoft Add-in Designer (1.0)。
  4. 從介面清單選取 _IDTExtensibility2,確定。系統自動產生五個空的介面函式。

步驟 4:撰寫登錄腳本 (.rgs)

在 FileView 的 Resource Files 中開啟 OutlookAddin.rgs,於檔案末尾加入:

// 新增 Outlook 2000 內部增益集登錄機碼
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'
                        }
                    }
                }
            }
        }
    }
}

步驟 5:匯入 Office 與 Outlook 類型庫

在 Stdafx.h 加入(路徑依實際安裝位置調整):

// 匯入 Office 2000 及 Outlook 2000 類型庫
#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;

步驟 6:在 OnConnection 中建立工具列與按鈕

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(L"新增Outlook2K工具列增益集");
    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); // msoControlButton
    CComVariant vShow(VARIANT_TRUE);
    CComPtr<Office::CommandBarControl> spNewBar, spNewBar2;
    spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
    spNewBar2 = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
    ATLASSERT(spNewBar); ATLASSERT(spNewBar2);

    // 取得 _CommandBarButton 介面設定按鈕外觀
    CComQIPtr<Office::_CommandBarButton> spCmdButton(spNewBar);
    CComQIPtr<Office::_CommandBarButton> spCmdButton2(spNewBar2);
    ATLASSERT(spCmdButton); ATLASSERT(spCmdButton2);

    // 按鈕 1:使用自訂點陣圖 (32x32)
    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(L"按鈕1"));
    spCmdButton->PutEnabled(VARIANT_TRUE);
    spCmdButton->PutTooltipText(OLESTR(L"按鈕1提示訊息"));
    spCmdButton->PutTag(OLESTR(L"按鈕1標誌"));

    // 顯示工具列
    spNewCmdBar->PutVisible(VARIANT_TRUE);

    // 按鈕 2:使用 Outlook 內建圖示 (FaceId 1760)
    spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
    spCmdButton2->PutFaceId(1760);
    spCmdButton2->PutVisible(VARIANT_TRUE);
    spCmdButton2->PutCaption(OLESTR(L"按鈕2"));
    spCmdButton2->PutEnabled(VARIANT_TRUE);
    spCmdButton2->PutTooltipText(OLESTR(L"按鈕2提示訊息"));
    spCmdButton2->PutTag(OLESTR(L"按鈕2標誌"));
    spCmdButton2->PutVisible(VARIANT_TRUE);

    // 保存按鈕介面指標供後續事件連接使用
    m_spButton = spCmdButton;
    m_spButton2 = spCmdButton2;
    // ...
}

步驟 7:在「工具」功能表新增選單項目

_bstr_t bstrNewMenuText(OLESTR(L"新增選單項目"));
CComPtr<Office::CommandBarControls> spCmdCtrls, spCmdBarCtrls;
CComPtr<Office::CommandBarPopup> spCmdPopup;
CComPtr<Office::CommandBarControl> spCmdCtrl;
CComPtr<Office::CommandBar> spCmdBar;

// 取得主功能列
hr = spCmdBars->get_ActiveMenuBar(&spCmdBar);
if(FAILED(hr)) return hr;
spCmdCtrls = spCmdBar->GetControls();
ATLASSERT(spCmdCtrls);

// 定位第 5 項「工具」選單
CComVariant vItem(5);
spCmdCtrl = spCmdCtrls->GetItem(vItem);
ATLASSERT(spCmdCtrl);
IDispatchPtr spDisp = spCmdCtrl->GetControl();
CComQIPtr<Office::CommandBarPopup> ppCmdPopup(spDisp);
ATLASSERT(ppCmdPopup);
spCmdBarCtrls = ppCmdPopup->GetControls();
ATLASSERT(spCmdBarCtrls);

// 新增選單項目
CComVariant vMenuType(1), vMenuPos(6), vMenuEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
CComVariant vMenuShow(VARIANT_TRUE), vMenuTemp(VARIANT_TRUE);
CComPtr<Office::CommandBarControl> spNewMenu;
spNewMenu = spCmdBarCtrls->Add(vMenuType, vMenuEmpty, vMenuEmpty, vMenuEmpty, vMenuTemp);
ATLASSERT(spNewMenu);
spNewMenu->PutCaption(bstrNewMenuText);
spNewMenu->PutEnabled(VARIANT_TRUE);
spNewMenu->PutVisible(VARIANT_TRUE);

// 為選單項目加上圖示
CComQIPtr<Office::_CommandBarButton> spCmdMenuButton(spNewMenu);
ATLASSERT(spCmdMenuButton);
spCmdMenuButton->PutStyle(Office::msoButtonIconAndCaption);
spCmdMenuButton->PasteFace(); // 同按鈕1貼上圖示
spNewMenu->PutVisible(VARIANT_TRUE);
m_spMenu = spCmdMenuButton;

步驟 8:處理按鈕點擊事件 (IDispEventSimpleImpl)

工具列按鈕的事件介面為 _CommandBarButtonEvents。使用 IDispEventSimpleImpl 實作事件接收(不需額外類型庫資訊)。

  1. 類別繼承:public IDispEventSimpleImpl<1, COutlookAddin, &__uuidof(Office::_CommandBarButtonEvents)>
  2. 宣告回調資訊 (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 Map 對應:
    BEGIN_SINK_MAP(COutlookAddin)
        SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents), 0x01, OnClickButton1, &OnClickButtonInfo)
        SINK_ENTRY_INFO(2, __uuidof(Office::_CommandBarButtonEvents), 0x01, OnClickButton2, &OnClickButtonInfo)
        SINK_ENTRY_INFO(3, __uuidof(Office::_CommandBarButtonEvents), 0x01, OnClickMenu, &OnClickButtonInfo)
    END_SINK_MAP()
  4. 事件處理函式 (OutlookAddin.h 宣告,OutlookAddin.cpp 實作):
    void __stdcall OnClickButton1(IDispatch* /*Office::_CommandBarButton*/ Ctrl, VARIANT_BOOL* CancelDefault)
    {
        USES_CONVERSION;
        CComQIPtr<Office::_CommandBarButton> pCommandBarButton(Ctrl);
        HINSTANCE result = ShellExecute(NULL, _T("open"), _T("https://www.vckbase.com"), NULL, NULL, SW_SHOW);
    }
  5. 連接/斷開事件源:
    • OnConnection 結尾:CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton);
    • OnDisconnection 中:CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);

測試與驗證

  1. 編譯專案,若登錄成功,Outlook 的「COM 增益集」對話方塊將列出該增益集。
  2. 載入增益集後,工具列與「工具」選單將出現新增的按鈕與項目。
  3. 點擊按鈕將觸發對應事件(範例中開啟瀏覽器連往指定網址)。

參考資源

  • Building an Office2K COM addin with VC++/ATL — Amit Dey
  • ATL 開發指南 (第二版) — Tom Armstrong & Ron Patton

Outlook 2000 增益集執行效果圖

本文配套原始碼可供下載參考,內含詳細註解。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved