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

《COM 原理與應用》學習筆記(一):COM 基礎架構與核心機制

     編輯:Delphi

第一章 COM 概述

什麼是 COM

COM (Component Object Model) 是 Microsoft 提出的元件標準,不僅定義元件程式間交互的標準,也提供元件執行所需的環境。在 COM 標準中,一個元件程式稱為一個模組,可以是動態連結庫 (DLL),稱為進程內元件;也可以是可執行程式 (EXE),稱為進程外元件。一個元件程式可包含一或多個元件物件。COM 以物件為基本單元,程式間通訊的雙方是元件物件 (COM 物件),而元件程式是提供 COM 物件的程式碼載體。

COM 物件不同於 C++ 等語言的物件概念:COM 物件建立在二進位可執行碼層級,語言無關;C++ 物件建立在原始碼層級。這使不同程式語言開發的元件能相互交互。

COM 物件與介面

  • 介面:一組邏輯相關的函數集合 (介面成員函數),慣例以 "I" 為前綴。物件透過介面成員函數為客戶端提供服務。
  • 不可見性:COM 模型中,物件本身對客戶端不可見,客戶端只能透過介面請求服務。
  • GUID 識別:每個介面由 128-bit 全域唯一識別碼 (GUID/IID) 識別。客戶端透過 GUID 取得介面指標,再呼叫成員函數。
  • CLSID:每個元件類別也用 128-bit GUID 識別,稱為 CLSID,保證全域唯一性。
  • 多介面導航:COM 物件至少實作一個介面。客戶端建立物件後取得某介面指標,若物件實作多介面,可從任一介面查詢取得其他介面 (QueryInterface)。

COM 進程模型

模型說明優點缺點
進程內 (In-process)DLL 載入客戶端進程空間效率高,無進程間通訊開銷元件不穩定會導致客戶端當機
本地進程外 (Local Server)同機器獨立 EXE 進程穩定性好,一個元件可服務多客戶端開銷大,呼叫效率較低
遠端進程外 (Remote Server)不同機器,可為 DLL 或 EXE分散式部署網路延遲,複雜度高

對客戶端程式而言,這三種模型是透明的,使用方式一致。

COM 可重用性:包容與聚合

假設物件 1 (外部物件) 想重用物件 2 (內部物件) 功能:

  1. 包容:物件 1 包含物件 2,實作相同介面時內部委派給物件 2。
  2. 聚合:物件 1 直接將物件 2 的介面暴露給客戶端,客戶端不知物件 2 存在。聚合體現真正的元件重用。

第二章 COM 物件模型

全域唯一識別碼 GUID

128-bit 隨機數,結構定義:

typedef struct _GUID {
  DWORD Data1;
  WORD  Data2;
  WORD  Data3;
  BYTE  Data4[8];
} GUID;

範例:{64BF4372-1007-B0AA-444553540000}。Visual C++ 提供 UUIDGen.exe、GUIDGen.exe 產生 GUID;COM 函式庫提供 CoCreateGuid API。

COM 介面與虛函數表

技術上介面是包含函數指標的資料結構 (虛函數表 vtable)。介面指標 → pVtable → 函數指標陣列 → 物件實作。每個介面成員函數第一個參數為 this 指標。COM 規範要求使用 Unicode (BSTR),若內部用 ANSI 需轉換。Visual C++ 提供 _com_util::ConvertStringToBSTR / ConvertBSTRToString。

介面描述語言 IDL

COM 採用擴展的 OSF DCE IDL 描述介面,不依賴特定語言。MIDL 編譯器可將 .idl 編譯為 C/C++ 相容的 .h 檔。IDL 可定義參數類型、輸入輸出屬性、可變長度陣列等。

IUnknown 介面:所有 COM 介面的基礎

interface IUnknown {
  HRESULT QueryInterface([in] REFIID iid, [out] void **ppv);
  ULONG AddRef(void);
  ULONG Release(void);
}

三大核心規則:

  1. 同一性:同一物件的不同介面查詢 IUnknown 必須傳回相同指標,可藉此判斷兩介面是否屬於同一物件。
  2. 自反性:查詢自身介面必須成功。
  3. 對稱性:從介面 A 查詢到 B,則從 B 查回 A 必成功。
  4. 傳遞性:A→B、B→C 則 C→A。
  5. 時間無關性:某時刻可查詢到的介面,未來任何時刻查詢同介面必成功。

第三章 COM 實作機制

元件註冊資訊 (登錄表)

  • 所有元件:HKEY_CLASSES_ROOT\CLSID
  • 進程內:...CLSID\{GUID}\InprocServer32
  • 進程外:...CLSID\{GUID}\LocalServer32
  • 介面資訊:HKEY_CLASSES_ROOT\Interface
  • Proxy/Stub:...CLSID\{GUID}\ProxyStubClsid32
  • 類型庫:HKEY_CLASSES_ROOT\TypeLib
  • ProgID:HKEY_CLASSES_ROOT\ProgID (如 COMCTL.TreeCtrl)

類別工廠與 DllGetClassObject

類別工廠是 COM 物件的生產基地,支援 IClassFactory 介面:

interface IClassFactory : IUnknown {
  HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
  HRESULT LockServer(BOOL fLock);
}

DLL 匯出函數 DllGetClassObject 建立類別工廠:

HRESULT DllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv);

COM 程式庫調用流程:CoCreateInstance → CoGetClassObject → DllGetClassObject → IClassFactory::CreateInstance。

三大物件建立 API

  • CoGetClassObject:取得類別工廠介面指標,支援遠端伺服器資訊。
  • CoCreateInstance:封裝 CoGetClassObject + CreateInstance,最常用,不支援遠端建立。
  • CoCreateInstanceEx:支援遠端建立與一次取得多介面指標 (MULTI_QI 陣列)。

COM 程式庫初始化與記憶體管理

HRESULT CoInitialize(LPVOID pvReserved);  // 必須在呼叫 COM API 前呼叫
void CoUninitialize(void);                  // 程式結束前呼叫,配對使用

記憶體管理介面 IMalloc 及封裝 API:CoTaskMemAlloc、CoTaskMemFree、CoTaskMemRealloc。跨模組記憶體配置/釋放必須使用 COM 記憶體管理器 (如 ProgIDFromCLSID 分配的字串需 CoTaskMemFree 釋放)。

元件載入與卸載

  • 進程內:LoadLibrary → DllGetClassObject。卸載條件:物件數=0 且類別工廠鎖計數=0,DllCanUnloadNow 傳回 TRUE。客戶端定期呼叫 CoFreeUnusedLibraries 觸發卸載。
  • 進程外:CreateProcess /Embedding → CoRegisterClassObject。進程退出條件類似,但需處理使用者介面互動 (旗標控制)。

HRESULT 回傳值結構

32-bit:嚴重性 (2 bits) + 客戶位 (1 bit) + 設施代碼 (12 bits) + 錯誤代碼 (16 bits)。常用值:S_OK (0)、S_FALSE (1)、E_FAIL、E_OUTOFMEMORY、E_NOINTERFACE。判斷成功/失敗必須用 SUCCEEDED/FAILED 巨集,不可直接比較 S_OK。

第四章 COM 進階特性

  • 可重用性:包容 (Containment) 與聚合。聚合關鍵在 pUnkOuter 參數與內部物件的特殊參考計數處理。
  • 進程透明性、安全性、多執行緒:(待補充)

第五章 Visual C++ 開發 COM 應用

關鍵標頭檔:Unknwn.h、Wtypes.h、Objidl.h、Comdef.h、ObjBase.h、Ole2.h。常用巨集:DECLARE_INTERFACE、DECLARE_INTERFACE_、STDMETHOD、STDMETHOD_。

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