COM 物件理解
COM 物件類似 C++ 類別實例,封裝狀態(屬性)與行為(方法)。關鍵差異:
| 特性 | COM 物件 | C++ 物件 |
| 封裝性 | 二進制層級完全封裝,僅透過介面存取 | 語義層級封裝(public/private) |
| 重用機制 | 包容與聚合 | 繼承 |
| 多型機制 | 介面查詢 | 虛擬函式 |
| 標識 | CLSID (GUID) | 型別名稱 |
COM 物件標識:CLSID / GUID
- GUID:128 位全域唯一識別碼,由網卡位址(空間值)與時間戳(時間值)生成,重複機率極低。
- 工具:
GUIDGen.exe、UUIDGen.exe、API CoCreateGuid()。
- CLSID:類別 ID,註冊表
HKEY_CLASSES_ROOT\CLSID 下以 GUID 命名。
COM 介面核心
介面定義與 vtable
- 介面 = 一組函數的虛函數表指標 (vtable pointer) 集合。
- 用戶端持有介面指標
pInterface,實際指向 pVtable,再指向函數指標陣列。
- 呼叫慣例:
__stdcall(Callee 清理堆疊,跨語言相容)。
- 參數:字串必須用 Unicode (BSTR/OLECHAR)。
C/C++ 定義介面
// C 風格:需顯式 this 指標
typedef struct IMyInterface {
void (__stdcall *Method1)(IMyInterface* this, int arg);
HRESULT (__stdcall *Method2)(IMyInterface* this, BSTR* pOut);
} IMyInterface;
// C++ 風格:類別隱含 this,vtable 結構天然相容
class IMyInterface {
public:
virtual void __stdcall Method1(int arg) = 0;
virtual HRESULT __stdcall Method2(BSTR* pOut) = 0;
};
C++ class 定義介面最直觀,編譯器自動產生符合 COM 規範的 vtable 結構。
介面與物件的關聯
物件實作介面,需將介面指標暴露給客戶端。實作方式:
- 多重繼承:物件類別繼承所有介面類別,直接實作虛擬函式。
- 內嵌介面類別成員:物件包含介面實作類別實例,透過
this 調整指標回傳介面。
- MFC/ATL 採用不同機制連接介面指標與物件資料。
介面描述語言 IDL
- 平台無關、語言中立的介面定義語言(基於 DCE IDL 擴展)。
- 定義介面、方法、參數屬性、自訂資料結構、可變長度陣列。
MIDL 編譯器產生 .h、_i.c (GUID)、_p.c (Proxy/Stub)、dlldata.c。
介面特性
- 二進制相容:跨語言、跨編譯器、跨進程/機器。
- 介面不變性:一旦發布不可修改(新功能新增介面,如
IInterface2、IInterfaceEx)。
- 繼承性(單繼承):所有介面直接/間接繼承
IUnknown。介面繼承僅繼承「宣告」,不繼承「實作」。
- 多型性:透過
QueryInterface 在執行期發現並使用不同介面。
IUnknown:COM 的基石
interface IUnknown {
HRESULT QueryInterface(REFIID riid, void** ppvObject); // 介面查詢
ULONG AddRef(); // 參考計數 +1
ULONG Release(); // 參考計數 -1,歸零時銷毀物件
};
- 生命週期控制:參考計數機制。
- 介面查詢:執行期發現物件支援的介面。
參考計數實作層級選擇
- 組件層級(全域變數):粒度太粗。
- 物件層級(C++ 成員變數):最適合,精確控制單一物件生命週期。
- 介面層級(類別成員變數):粒度太細,管理複雜。
參考計數規則速查
| 情境 | 規則 |
| 函數輸入參數 (in) | 呼叫端負責有效性,被呼叫端不增減計數 |
| 函數輸出參數 (out) | 被呼叫端回傳前 AddRef;同樣適用於介面指標回傳值 |
| 輸入輸出參數 (in/out) | 修改前 Release 舊值,修改後 AddRef 新值 |
| 區域介面指標 | 作用域內有效,無需增減 |
| 全域/成員介面指標 | 傳遞前 AddRef,用畢 Release(防止他處釋放) |
| 一般賦值 | 賦值後 AddRef 新值;賦值前若舊值仍存活 Release 舊值 |
| 結束使用 | Release |
介面查詢規範
QueryInterface 回傳:S_OK、E_NOINTERFACE、E_UNEXPECTED。
- 原則:同一物件的所有介面查詢
IUnknown 應回傳同一指標;對稱性、自反性、傳遞性、時間無關性。
多介面物件實作
- C++ 多重繼承:
class Obj : public IFace1, public IFace2,需處理 this 指標調整(QueryInterface 回傳正確介面位址)。
- 內嵌介面實作類:物件包含實作類實例,
QueryInterface 回傳內嵌實例位址。
- 上一篇文章: COM 原理與應用:ActiveX 控制項完整指南
- 下一篇文章: COM 原理與應用:COM+ 架構與系統服務
Copyright ©
程式碼之家 All Rights Reserved