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

