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

COM 連接點機制深度解析與 MFC 實現全攻略

     編輯:關於VC++

核心概念

連接點讓 COM 物件能「主動」回呼客戶端,實現雙向通訊。關鍵角色:

  • 可連接物件:實作 IConnectionPointContainer,管理多個輸出介面。
  • 輸出介面:定義事件方法(如 IEventSink::EventHandle()),由客戶端實作。
  • 連接點:每個輸出介面對應一個實作 IConnectionPoint 的物件,維護連接清單(CONNECTDATA)。
  • 事件接收器:客戶端實作輸出介面的 COM 物件。

連接建立流程

  1. 客戶端 QueryInterface(IID_IConnectionPointContainer) 取得容器介面。
  2. 呼叫 FindConnectionPoint(IID_IEventSink, &pCP) 取得特定連接點。
  3. 呼叫 pCP->Advise(pSink, &dwCookie) 註冊接收器,取得連接識別碼 Cookie。
  4. 伺服器端透過 pCP->GetConnections() 列舉連接,逐一呼叫 pSink->EventHandle() 觸發事件。
  5. 解除連接:pCP->Unadvise(dwCookie) + Release()。

MFC 封裝優勢

MFC 內建支援:

  • CCmdTarget 實作 IConnectionPointContainer + IEnumConnectionPoints。
  • CConnectionPoint 實作 IConnectionPoint + IEnumConnections。
  • 提供巨集 BEGIN_CONNECTION_PART、CONNECTION_IID、DECLARE_CONNECTION_MAP 簡化宣告。

完整 MFC 實現範例

1. 定義 GUID 與介面

// GUIDs.h
const IID CLSID_ConnObject = {0xEE888B01,...};
const IID IID_IEventServer = {0xEE888B02,...}; // 一般服務介面
const IID IID_IEventSink   = {0xEE888B03,...}; // 輸出介面

// IConnObject.h
DECLARE_INTERFACE_(IEventServer, IUnknown) { STDMETHOD(DoSomething)() PURE; };
DECLARE_INTERFACE_(IEventSink, IUnknown)   { STDMETHOD(EventHandle)() PURE; };

2. 可連接物件類別宣告


class CConnObject : public CCmdTarget {
    DECLARE_DYNCREATE(CConnObject)
protected:
    // 一般介面
    BEGIN_INTERFACE_PART(EventServer, IEventServer)
        STDMETHOD(DoSomething)();
    END_INTERFACE_PART(EventServer)

    // 連接點
    BEGIN_CONNECTION_PART(CConnObject, SampleConnPoint)
        CONNECTION_IID(IID_IEventSink)
    END_CONNECTION_PART(SampleConnPoint)

    DECLARE_INTERFACE_MAP()
    DECLARE_CONNECTION_MAP()
    DECLARE_OLECREATE(CConnObject)

    void FireEvent(); // 觸發事件內部函式
};

3. 實作映射與事件觸發


IMPLEMENT_OLECREATE(CConnObject, "ConnObject", 0xEE888B01,...)

BEGIN_INTERFACE_MAP(CConnObject, CCmdTarget)
    INTERFACE_PART(CConnObject, IID_IEventServer, EventServer)
    INTERFACE_PART(CConnObject, IID_IConnectionPointContainer, ConnPtContainer) // 必須
END_INTERFACE_MAP()

BEGIN_CONNECTION_MAP(CConnObject, CCmdTarget)
    CONNECTION_PART(CConnObject, IID_IEventSink, SampleConnPoint)
END_CONNECTION_MAP()

CConnObject::CConnObject() { EnableConnections(); }

STDMETHODIMP CConnObject::XEventServer::DoSomething() {
    METHOD_PROLOGUE(CConnObject, EventServer);
    pThis->FireEvent();
    return S_OK;
}

void CConnObject::FireEvent() {
    const CPtrArray* pConns = m_xSampleConnPoint.GetConnections();
    for (int i=0; iGetSize(); ++i) {
        IEventSink* pSink = (IEventSink*)pConns->GetAt(i);
        if (pSink) pSink->EventHandle();
    }
}

4. 客戶端對話框實作事件接收器


class CClientDlg : public CDialog {
    BEGIN_INTERFACE_PART(EventSink, IEventSink)
        STDMETHOD(EventHandle)();
    END_INTERFACE_PART(EventSink)

    LPCONNECTIONPOINTCONTAINER m_pCPC = NULL;
    LPCONNECTIONPOINT m_pCP = NULL;
    DWORD m_dwCookie = 0;
    LPUNKNOWN m_pUnk = NULL;
    // ...
};

STDMETHODIMP CClientDlg::XEventSink::EventHandle() {
    AfxMessageBox(_T("收到伺服器事件通知!"));
    return S_OK;
}

// 連接按鈕
void CClientDlg::OnConnect() {
    m_pUnk->QueryInterface(IID_IConnectionPointContainer, (void**)&m_pCPC);
    m_pCPC->FindConnectionPoint(IID_IEventSink, &m_pCP);
    IUnknown* pSinkUnk; m_xEventSink.QueryInterface(IID_IUnknown, (void**)&pSinkUnk);
    m_pCP->Advise(pSinkUnk, &m_dwCookie);
}

// 觸發事件按鈕
void CClientDlg::OnEvent() {
    IEventServer* pSrv; m_pUnk->QueryInterface(IID_IEventServer, (void**)&pSrv);
    pSrv->DoSomething(); // 伺服器內部會呼叫 FireEvent → 回呼 EventHandle
}

進階提示

  • 輸出介面若改用 IDispatch(自動化介面),支援腳本語言與 VB 客戶端,更靈活。
  • ATL 實作連接點更精簡(IConnectionPointContainerImpl + IConnectionPointImpl),適合高效能元件。
  • 多執行緒環境下,連接點清單存取需加關鍵區同步。

總結

連接點機制是 COM 事件模型的基石,MFC 透過 CCmdTarget 與連接點巨集大幅降低實作複雜度。掌握「容器→連接點→Advise/Unadvise→FireEvent」流程,即可在元件中建立強大的事件驅動架構。

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