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

VC++ 6.0 中使用 MFC 進行 COM 程式設計完整指南

     編輯:VC 教學

核心概念

MFC 透過巢狀類別而非多重繼承實作 COM 介面,利用介面對映機制將介面與實作該介面的巢狀類別關聯。MFC 透過 CCmdTarget 類別實作了 IUnknown 介面。本文以建立 TimeLogServer 元件為例,示範 COM 伺服器與用戶端關鍵程式碼。

第一節:COM 伺服器建立步驟

1. 宣告元件與介面 GUID

建立 GUIDs.h,使用 guidgen.exe 產生唯一識別碼:

// 元件 CLSID
static const IID CLSID_TimeLogServer = 
{0xa433e701, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};
// 介面 IID
static const IID IID_ITimeLog = 
{0xa433e702, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};

2. 宣告介面定義

建立 ITimeLogServer.h:

#include "GUIDs.h"
DECLARE_INTERFACE_(ITimeLog, IUnknown)
{
    STDMETHOD(OutputLog)(BSTR* varLogText) PURE;
};

說明:DECLARE_INTERFACE_ 宣告介面,STDMETHOD 宣告回傳 HRESULT 的純虛擬函數。

3. 宣告元件類別與巢狀類別

使用 ClassWizard 新增 CTimeLogServer(基類別 CCmdTarget),修改標頭檔:

#include "ITimeLogServer.h"
// 宣告實作 ITimeLog 的巢狀類別
BEGIN_INTERFACE_PART(TimeLog, ITimeLog)
    STDMETHOD(OutputLog)(BSTR* varLogText);
END_INTERFACE_PART(TimeLog)
// 宣告介面對映與類別工廠
DECLARE_INTERFACE_MAP()
DECLARE_OLECREATE(CTimeLogServer)

4. 實作類別工廠與介面對映

在實作檔中:

// 實作類別工廠
IMPLEMENT_OLECREATE(CTimeLogServer, "TimeLogServer",
0xa433e701, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28)

// 介面對映到巢狀類別
BEGIN_INTERFACE_MAP(CTimeLogServer, CCmdTarget)
    INTERFACE_PART(CTimeLogServer, IID_ITimeLog, TimeLog)
END_INTERFACE_MAP()

5. 全域物件計數管理

CTimeLogServer::CTimeLogServer() { ::AfxOleLockApp(); }
CTimeLogServer::~CTimeLogServer() { ::AfxOleUnlockApp(); }

6. 為巢狀類別實作 IUnknown

雖 CCmdTarget 已實作 IUnknown,仍需將巢狀類別的 IUnknown 對映至外部實作:

STDMETHODIMP_(ULONG) CTimeLogServer::XTimeLog::AddRef() {
    METHOD_PROLOGUE(CTimeLogServer, TimeLog)
    return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CTimeLogServer::XTimeLog::Release() {
    METHOD_PROLOGUE(CTimeLogServer, TimeLog)
    return pThis->ExternalRelease();
}
STDMETHODIMP CTimeLogServer::XTimeLog::QueryInterface(REFIID riid, void** ppvObj) {
    METHOD_PROLOGUE(CTimeLogServer, TimeLog)
    return pThis->ExternalQueryInterface(&riid, ppvObj);
}

7. 實作介面方法 OutputLog

元件功能:將日誌文字寫入以日期命名的日誌檔。

// 類別屬性中加入檔案指標
FILE* m_logfile;

// 建構函數初始化
CTimeLogServer::CTimeLogServer() {
    ::AfxOleLockApp();
    CTime TimeStamp = CTime::GetCurrentTime();
    CString FileName;
    FileName.Format(_T("%s.log"), TimeStamp.Format(_T("%Y%m%d")));
    m_logfile = fopen(FileName, _T("a"));
    if (m_logfile) {
        fprintf(m_logfile, _T("########## 開始於:%s "), (LPCTSTR)TimeStamp.Format(_T("%Y年%m月%d日%H:%M:%S")));
    }
}

// 解構函數關閉檔案
CTimeLogServer::~CTimeLogServer() {
    ::AfxOleUnlockApp();
    if (m_logfile) {
        CTime TimeStamp = CTime::GetCurrentTime();
        fprintf(m_logfile, _T(" 結束於:%s ##########"), (LPCTSTR)TimeStamp.Format(_T("%Y年%m月%d日%H:%M:%S")));
        fclose(m_logfile);
    }
}

// 實作 OutputLog
STDMETHODIMP CTimeLogServer::XTimeLog::OutputLog(BSTR* varLogText) {
    METHOD_PROLOGUE(CTimeLogServer, TimeLog)
    if (pThis->m_logfile) {
        CTime TimeStamp = CTime::GetCurrentTime();
        CString NowTime = TimeStamp.Format(_T("%Y年%m月%d日%H:%M:%S"));
        CString LogText((LPCWSTR)*varLogText);
        fprintf(pThis->m_logfile, " %s %s ", NowTime, LogText);
        return NOERROR;
    }
    AfxMessageBox(_T("沒有日誌檔案!"));
    return S_FALSE;
}

8. 完善伺服器應用程式

BOOL CTimeLogServerApp::InitInstance() {
    ::AfxOleLockApp();
    COleObjectFactory::RegisterAll(); // 註冊所有類別工廠
    return TRUE;
}
int CTimeLogServerApp::ExitInstance() {
    ::AfxOleUnlockApp();
    return CWinApp::ExitInstance();
}

第二節:用戶端關鍵程式碼

用戶端步驟:初始化 COM 庫 → 建立元件物件取得 IUnknown → 查詢介面使用 → 釋放資源。

#include "ITimeLogServer.h"
HRESULT hResult;
IUnknown* pIUnknown = NULL;

hResult = ::CoInitialize(NULL);
if (FAILED(hResult)) { AfxMessageBox(_T("無法初始化 COM 庫!")); return FALSE; }

hResult = ::CoCreateInstance(CLSID_TimeLogServer, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pIUnknown);
if (FAILED(hResult)) { AfxMessageBox(_T("無法建立 TimeLog 物件!")); return FALSE; }

if (pIUnknown) {
    ITimeLog* pITimeLog = NULL;
    hResult = pIUnknown->QueryInterface(IID_ITimeLog, (void**)&pITimeLog);
    if (FAILED(hResult)) { pIUnknown->Release(); return FALSE; }

    BSTR bstrLogText = m_logtext.AllocSysString();
    if (FAILED(pITimeLog->OutputLog(&bstrLogText))) {
        AfxMessageBox(_T("日誌輸出錯誤!"));
    } else {
        AfxMessageBox(_T("日誌已寫入!"));
    }
    pITimeLog->Release();
}
pIUnknown->Release();
::CoUninitialize();

總結

MFC 簡化了 COM 開發,透過巢狀類別、介面對映、CCmdTarget 內建 IUnknown 實作等機制,讓開發者專注於業務邏輯。本文完整展示從 GUID 定義、介面宣告、元件實作到用戶端呼叫的全流程,可作為 MFC COM 開發的範本參考。

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