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()用于获取指针ITypeInfo接口的指针,通过该指针将能够判断自动化服务程序所提供的自动化支持。剩下的这两个函数是比较重要的,其中GetIDsOfNames()将读取一个函数的名称并返回其调度ID(DISPID),DISPID只是一个long类型的数据,对于IDispatch的一个特定实现,此DISPID值应该是唯一的。其参数riid为保留参数,必须设置为IID_NULL,在rgszNames中指定了成员的函数名及其参数,由cNames标识了名字的个数,lcid参数用于指定本地化标识,得到的DISPID 将保存到rgdispid中。Invoke()提供了访问自动化对象暴露出来的方法和属性的方法。可以将DISPID作为函数指针数组的索引传入dispidMember参数,Invoke()将实现一组按此索引来访问的函数。riid和lcid的含义与在GetIDsOfNames()中的定义相同,分别为保留参数和本地化标识。WFlags参数指定了要访问的是接口的属性还是方法,pdispparams参数包括了方法和属性调用的参数数组、DISPID数组以及数组中参数个数等信息。pvarResult参数保存有返回值信息。pexcepinfo指向一个有效的异常信息结构,puArgErr参数包含了第一个产生错误的参数指针。通过GetIDsOfNames()和Invoke()的结合使用,将可以根据函数名称对方法和属性进行调用。这样,函数地址、AddRef()、Release()以及接口指针等细节问题将无需考虑。下面结合一段实例代码来说明对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()成员函数执行此方法。