COM組件(Component)與介面(Interface)
許式偉 (著作權聲明)
2007-3-12
在前文,我預告了我將開始介紹WINX對自動化(Automation)的支援。首先我打算解釋一下自動化(Automation)相關的概念。
我們會分為3個篇幅介紹:
- COM基礎:COM組件(Component)與介面(Interface)
- 變體(Variant)與IDispatch(Dispatch調用)
- 自動化(Automation)、OA(Office Automation)、二次開發介面(應用程式開發介面,API)與類型庫(TypeLib)
這是第一篇。
COM組件(Component)直觀理解就是一個類,但這不是嚴謹的定義。原因有二:
- 有的語言(例如C語言)沒有類,但是它可以實現COM組件。
- COM組件(Component)通常是一個類,但是它也可能是用多個類來實現。但是對於組件的客戶而言,它是一個類,還是多個類進行實現,它不知道,也不關心。關於用多個類實現COM組件的詳細內容,您可以瞭解一下COM組件中的“嵌套類實現COM介面(如MFC喜歡的,ATL則傾向於用多重繼承)”、“彙總(AGGREGATION)”方面的內容。
COM組件(Component)是一種基於二進位對象協議的概念。也可以理解為,這是一個二進位意義上的“類”。一個COM組件,對外暴露的不是一組方法(Method),而是一組介面(Interface)。
介面(Interface)這個概念被廣泛用運,一般意義上說是指“類的規格(契約)”。從COM意義上理解的介面(Interface),指的是一種和目前vtbl機制相容的二進位協議,並且vtbl的前三項與IUnknown介面相容(從繼承角度上來講,可以理解為要求從IUnknown繼承,但只是這樣理解而已)。例如,你可以定義如下介面:
interface IFoo : IUnknown
{
virtual void __stdcall fooA() = 0;
virtual int __stdcall fooB(int arg1, int arg2) = 0;
};
但是你也可以不這樣寫,而是這樣純C風格的:
struct IFooVtbl
{
HRESULT (__stdcall *QueryInterface)(void* pThis, const GUID* iid, void** ppv);
ULONG (__stdcall *AddRef)(void* pThis);
ULONG (__stdcall *Release)(void* pThis);
void (__stdcall *fooA)(void* pThis);
int (__stdcall *fooB)(void* pThis, int arg1, int arg2);
};
struct IFoo
{
struct IFooVtbl* vptr;
};
QueryInterface是COM組件(Component)的核心部分,有了它,才使得組件有了發展升級的可能。我們知道,COM中介面(Interface)有一個GUID(全球唯一識別碼)與其對應,理論上說,一旦一個介面被發布,那麼它就不應該被修改,以便舊的客戶可以升級使用到新版本的COM組件。要升級你的組件,你應該這樣:
interface IFoo2 : IFoo
{
virtual HRESULT __stdcall newFoo() = 0;
};
客戶需要用到IFoo2中的功能時,就需要用QueryInterface切換到IFoo2:
IFoo* pFoo;
...
IFoo2* pFoo2;
HRESULT hr = pFoo->QueryInterafce(IID_IFoo2, (void**)&pFoo2);
if (SUCCEEDED(hr))
{
// use pFoo2 ...
pFoo2->Release();
}
而IUnknown中的AddRef、Release,則用於管理COM組件的生命週期。對於它我在《C++記憶體管理變革》中也聊過,這裡不多解釋。