网上说法1:
IDispatch 接口是COM中比较常用的接口。
如果某个COM对象继承了IDispatch接口,那么这个COM对象所有的方法和属性都可以以它们的名称为参数,通过调用IDispatch接口的Invoke()方法来间接调用这些方法和属性。
在ATL中,如果想使某个COM对象支持IDispatch接口,那么必须在这个COM对象的属性中指定DUAL,即具有双重接口属性。在支持IDispatch接口的COM对象中,每个属性和方法都有一个唯一的DispID(这可以通过一个宏来获得),客户端可以以方法的名称为参数调用IDispatch接口的GetIDsOfNames()来获得这个DispID,然后以这个DispID为参数调用IDispatch接口的Invoke()函数来间接调用COM对象中方法,并获得返回结果。有意思的是,COM对象中DispID为0的方法是默认的方法,即客户调用IDispatch接口的Invoke()时如果没有给出DispID参数,则默认调用的是这个方法。
我猜测在COM子系统内部是这样支持通过名字调用COM对象方法的:首先调用IDispatch接口的GetIDsOfNames()获得该方法的DispID,这实际上是一个查表的操作,然后调用IDispatch接口的Invoke()函数来间接调用这个方法,这同样要先通过DispID来查表获得相应的方法入口地址,然后执行这个方法,获得可能的返回结果,再将这个结果传递到客户端。
设计IDispatch接口的本意是为了在VB和VBScript中使用COM,因为这样的语言不支持指针,所以不能像C++那样直接通过指向对象的指针来获得COM对象方法的入口,只能通过COM子系统来调用COM对象中的方法。实际上,在VB中调用COM对象的方法时,COM子系统会自动根据VB给出的函数名,调用该COM对象IDispatch接口的Invoke()方法来间接调用该方法,并将结果返回VB。当然这个过程对程序员来将是透明的,所以给人的感觉就像是由VB调用了COM对象的方法一样。
尽管在客户端看不出IDispatch接口的作用,可是在设计COM对象时却要考虑是否要支持IDispatch接口这个问题,如果支持则可以扩大该COM对象的使用范围,但对COM的性能会有一定的影响,如果这个COM对象规模比较大的话,通过IDispatch接口调用该对象的方法会耗费更多的时间,效率会损失10%-100%,原因是IDispatch接口通过方法名来查找它的DispID会需要较多的时间。
在ATL中同样也有一个IDispatch接口指针的封装类:CComDispatchDriver。你可以用一个IDispatch接口指针来初始化一个CComDispatchDriver对象,这个对象提供了一组方法来帮助你使用IDispatch接口指针。
网上说法2:
支持自动化。
用户只要有COM库,不需要任何说明, 便可知道该库所有对象及对象的方法属性,及方法、属性的参数、返回值 类型,及其ID。利用IDispatch,用户不需要获得COM
组件的方法实际指针。因为IDispatch::Invoke 提供了一切方法。这就是为什么叫做 dispatch(分发)接口的原因。如果没有IDispatch开发者必须提供 该COM虚类的原形。现在的大多数公用COM都被设计为支持IDispatch接口
网上说法3:
有实现IDispatch的接口组件才能够被脚本语言这种需要迟绑定使用COM组件使用,因为脚本语言和VB某种情况下是解释的方式使用组件的,称为迟绑定。而向VC这些语言使用COM组件时,对组件的调用方式是在编译期间决定的,称为早绑定。不管怎么样,反正各种开发COM组件的环境都支持直接实现Idispatch而不需要你做一点多余的工作,何乐而不为呢?
网上说法4:
GetIDsOfNames & Invoke are the most interest function of IDispatch. GetIDsOfNames will read a name of function and return it's DISPID.then Automation will transfer the DISPID to Invoke. Invoke regard DISPID as the index of function ptr array and loop the function to execute it.
See the defination by IDL(Microsoft):
interface IDispatch : IUnknown
{
......
HRESULT GetIDsOfNames(
[in] const IID& riid,
[in,size_is(cNames)] LPOLESTR* rgszNames,
[in] UINT cNames,
[in] LCID lcid,
[out,size_is(cNames)] DISPID* rgDispd);
HRESULT Invoke(
[in] DISPID dispIdMember,
[in] const IID& riid,
[in] WORD wFlags,out] DISPPARAMS* pDispParams,
[out] VARIANT* pVarResult,
[out] EXCEPINFO* pExcepInfo,
[out] UINT* puArgErr);
......
}
网上说法5: