所謂自動化對象,指的是實現了IDispatch接口的COM對象,IDispatch接口是自動化對象的一個重要標志。使用自動化技術的一個主要目的就是對COM的一些底層操作進行簡化。包括自動化組件和自動化客戶兩方面的內容,分別用來定義和使用自動化對象。自動化對象包含有屬性和方法這兩種重要的組成。屬性類似于類中的數據成員,方法則類似于類成員函數,只不過這里的屬性只能被讀取而不允許被寫入。自動化組件除了定義自動化對象外,還將內部可編程對象展現給自動化客戶,而自動化客戶則對這些暴露的自動化對象進行操作。具體的操作包括:創(chuàng)建一個新的自動化對象,獲取、設置自動化對象的屬性,以及調用自動化對象的方法等。IDispatch接口作為自動化對象的重要特征,可以通過QuereyInterface()函數查詢此接口來確定組件是否是自動化對象。IDispatch接口直接從IUnknown接口派生,接口定義如下:
其中,接口成員函數GetTypeInfoCount()用于獲取自動化組件支持的ITypeInfo接口的數目。GetTypeInfo()用于獲取指針I(yè)TypeInfo接口的指針,通過該指針將能夠判斷自動化服務程序所提供的自動化支持。剩下的這兩個函數是比較重要的,其中GetIDsOfNames()將讀取一個函數的名稱并返回其調度ID(DISPID),DISPID只是一個long類型的數據,對于IDispatch的一個特定實現,此DISPID值應該是唯一的。其參數riid為保留參數,必須設置為IID_NULL,在rgszNames中指定了成員的函數名及其參數,由cNames標識了名字的個數,lcid參數用于指定本地化標識,得到的DISPID 將保存到rgdispid中。Invoke()提供了訪問自動化對象暴露出來的方法和屬性的方法??梢詫ISPID作為函數指針數組的索引傳入dispidMember參數,Invoke()將實現一組按此索引來訪問的函數。riid和lcid的含義與在GetIDsOfNames()中的定義相同,分別為保留參數和本地化標識。WFlags參數指定了要訪問的是接口的屬性還是方法,pdispparams參數包括了方法和屬性調用的參數數組、DISPID數組以及數組中參數個數等信息。pvarResult參數保存有返回值信息。pexcepinfo指向一個有效的異常信息結構,puArgErr參數包含了第一個產生錯誤的參數指針。通過GetIDsOfNames()和Invoke()的結合使用,將可以根據函數名稱對方法和屬性進行調用。這樣,函數地址、AddRef()、Release()以及接口指針等細節(jié)問題將無需考慮。下面結合一段實例代碼來說明對IDispatch接口的使用:
這段代碼使用的是Calendar組件,并通過IDispatch接口完成對Today()方法的調用。CLSIDFromProgID()將Calendar組件的ProgID轉換為CLSID,并以此CLSID和IID_IDispatch作為參數去調用CoCreateInstance()以得到IDispatch接口指針。通過其成員函數GetIDsOfNames()得到將要調用的Today方法的DISPID,最后使用Invoke()成員函數執(zhí)行此方法。
interface IDispatch : IUnknown { virtual HRESULT GetTypeInfoCount(UINT* pctinfo) = 0; virtual HRESULT GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) = 0; virtual HRESULT GetIDsOfNames (REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) = 0; virtual HRESULT Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr ) = 0; } |
其中,接口成員函數GetTypeInfoCount()用于獲取自動化組件支持的ITypeInfo接口的數目。GetTypeInfo()用于獲取指針I(yè)TypeInfo接口的指針,通過該指針將能夠判斷自動化服務程序所提供的自動化支持。剩下的這兩個函數是比較重要的,其中GetIDsOfNames()將讀取一個函數的名稱并返回其調度ID(DISPID),DISPID只是一個long類型的數據,對于IDispatch的一個特定實現,此DISPID值應該是唯一的。其參數riid為保留參數,必須設置為IID_NULL,在rgszNames中指定了成員的函數名及其參數,由cNames標識了名字的個數,lcid參數用于指定本地化標識,得到的DISPID 將保存到rgdispid中。Invoke()提供了訪問自動化對象暴露出來的方法和屬性的方法??梢詫ISPID作為函數指針數組的索引傳入dispidMember參數,Invoke()將實現一組按此索引來訪問的函數。riid和lcid的含義與在GetIDsOfNames()中的定義相同,分別為保留參數和本地化標識。WFlags參數指定了要訪問的是接口的屬性還是方法,pdispparams參數包括了方法和屬性調用的參數數組、DISPID數組以及數組中參數個數等信息。pvarResult參數保存有返回值信息。pexcepinfo指向一個有效的異常信息結構,puArgErr參數包含了第一個產生錯誤的參數指針。通過GetIDsOfNames()和Invoke()的結合使用,將可以根據函數名稱對方法和屬性進行調用。這樣,函數地址、AddRef()、Release()以及接口指針等細節(jié)問題將無需考慮。下面結合一段實例代碼來說明對IDispatch接口的使用:
// 從ProgID得到CLSID wchar_t progid[] = L"MSCAL.Calendar.7"; CLSID clsid; if (FAILED(::CLSIDFromProgID(progid, &clsid))) return; // 得到IDispatch接口指針 IDispatch* pIDispatch = NULL; if (FAILED(::CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (void**)&pIDispatch))) return; // 得到DISPID DISPID dispid; OLECHAR* func = L"Today"; if (FAILED(pIDispatch->GetIDsOfNames(IID_NULL, &func, 1, GetUserDefaultLCID(), &dispid))) return; // 通過DISPID使用Today方法 DISPPARAMS dispparams = {NULL}; if (FAILED(pIDispatch->Invoke(dispid, IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &dispparams, NULL, NULL, NULL))) return; // 將日期移動到今天 AfxMessageBox("日期成功移動到今天"); |
這段代碼使用的是Calendar組件,并通過IDispatch接口完成對Today()方法的調用。CLSIDFromProgID()將Calendar組件的ProgID轉換為CLSID,并以此CLSID和IID_IDispatch作為參數去調用CoCreateInstance()以得到IDispatch接口指針。通過其成員函數GetIDsOfNames()得到將要調用的Today方法的DISPID,最后使用Invoke()成員函數執(zhí)行此方法。