在前文,我預告了我將開始介紹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++內存管理變革》中也聊過,這裡不多解釋。