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

ATL 連接點簡化實作:模板類自動管理 Sink 介面

     編輯:C++ 入門

背景

COM 連接點讓物件能主動回呼客戶端。傳統手寫 IConnectionPointContainer、IConnectionPoint、Advise/Unadvise 代碼冗長且易錯。ATL 提供 AtlAdvise/AtlUnadvise 及 CComPtrBase::Advise 封裝,配合模板技術可將連接點管理壓縮至百行以內。

核心類別設計

// 通用 Sink 實作基類
template <typename T, typename EventInterface, const GUID* evtLibID = NULL>
class ATL_NO_VTABLE CSinkImpT :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CSinkImpT<T, EventInterface, evtLibID>, &__uuidof(T)>,
    public IDispatchImpl<EventInterface, &__uuidof(EventInterface), evtLibID>
{
public:
    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
        DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
    {
        T* pThis = static_cast<T*>(this);
        return pThis->DoInvoke(dispidMember, riid, lcid, wFlags,
            pdispparams, pvarResult, pexcepinfo, puArgErr);
    }
    // 衍生類別需實作 DoInvoke 處理具體事件
    virtual HRESULT DoInvoke(...) { return E_NOTIMPL; }
};

// 連接點輔助類別:RAII 管理 Advise/Unadvise
template <typename EventInterface, typename EventProcessor>
class ConnectionPointHelper {
    CComPtr<IUnknown> m_spInterface;
    DWORD m_dwCookie = 0;
public:
    ConnectionPointHelper(IUnknown* pInterface) : m_spInterface(pInterface) { Connect(); }
    ~ConnectionPointHelper() { Disconnect(); }

    void Connect() {
        HRESULT hr = E_FAIL;
        do {
            if (!m_spInterface || m_dwCookie) break;
            CComObject<EventProcessor>* pTmp = nullptr;
            hr = CComObject<EventProcessor>::CreateInstance(&pTmp);
            if (FAILED(hr)) break;
            CComQIPtr<IUnknown, &IID_IUnknown> spSink(pTmp);
            hr = m_spInterface->Advise(spSink, __uuidof(EventInterface), &m_dwCookie);
        } while (FALSE);
    }

    void Disconnect() {
        if (m_dwCookie) {
            AtlUnadvise(m_spInterface, __uuidof(EventInterface), m_dwCookie);
            m_dwCookie = 0;
        }
    }
};

使用範例

// 介面指標方式
ComDllLib::ITestComPtr pCom;
HRESULT hr = pCom.CreateInstance(L"Test.Com");
if (SUCCEEDED(hr)) {
    CComPtr<IUnknown> spUnk;
    hr = pCom->QueryInterface(IID_IUnknown, (void**)&spUnk);
    if (SUCCEEDED(hr)) {
        ConnectionPointHelper<ComDllLib::_ITestComEvent, CSink3> cph(spUnk);
        LONG c = pCom->Add(1, 5);  // 觸發事件回呼
    }
}

// IDispatch 晚期繫結方式
CComPtr<IDispatch> spDisp;
hr = spDisp.CoCreateInstance(L"Test.Com");
if (SUCCEEDED(hr)) {
    CComQIPtr<IUnknown> spUnk(spDisp);
    ConnectionPointHelper<ComDllLib::_ITestComEvent, CSink3> cph(spUnk);
    _variant_t ret, m=2, n=3;
    spDisp.Invoke2(L"Add", &m, &n, &ret);
}

優勢總結

  • 程式碼極度精簡:核心邏輯不足 100 行,消除樣板代碼。
  • RAII 自動管理:建構時 Advise,解構時 Unadvise,杜絕洩漏。
  • 型別安全:模板參數綁定事件介面與 Sink 實作類別,編譯期檢查。
  • 支援早/晚期繫結:同一輔助類別適用 IUnknown 與 IDispatch 兩種呼叫模式。

適用場景

所有需實作 COM 事件回呼的專案,特別是大量使用 ActiveX 元件、Office 自動化、或自訂 COM 伺服器的 C++ 應用程式。

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