首先需要决定的是是否使用type infomation?
那些简化IDispatch实现的helper method需要使用类型信息。
IDispatch接口的四个方法从客户端的角度来看是关联的。
1. IDispatch::GetTypeInfoCount
IDispatch::GetTypeInfoCount function is called by the client to determine whether type information is available for the object.
如果有类型信息,则实现类在初始化时需要获取IDispatch子接口的类型信息以便后续方法使用。
2. IDispatch::GetTypeInfo
获取IDispatch子接口的类型信息。
3. IDispatch::GetIDsOfNames
实现IDispatch的子接口可能有多个方法,此方法即根据输入的method name及其参数名得到DISPID以便调用Invoke方法。
4. IDispatch::Invoke
第一个参数就是上面得到的DISPID,代表要调用哪个方法。第五个参数是一个DISPPARAMS结构体数组,包含相应的参数信息。
如果client端通过调用GetIDsOfNames获取dispid然后再调用invoke则称为late binding。如果通过type library获取dispid缓存以便使用则成为early binding。
由于IDispatch自动化接口不容易使用,微软推荐采用dual interface, 即面向不同的客户端,如果client采用VB Script则通过自动化接口访问,如果client端采用C++则使用custom interface即COM技术。
DISPPARAMS:
typedef struct tagDISPPARAMS { // Array of arguments [size_is(cArgs)] VARIANTARG* rgvarg; // Array of DISPIDs of named arguments [size_is(cNamedArgs)] DISPID* rgdispidNamedArgs; // Total number of arguments UINT cArgs; // Number of named arguments UINT cNamedArgs; } DISPPARAMS;
理解position arguments VS named arguments
首先理解
1) Parameters are automatically assigned a DISPID value based on their position within the argument list. For example, the Sum method has three parameters that are implicitly assigned DISPIDs as follows:
HRESULT Sum([optional, defaultvalue(-1)] int x, // DISPID = 0 [optional, defaultvalue(-1)] int y, // DISPID = 1 [out, retval] int* retvalue); // DISPID = 2 |
2) 参数数组rgvarg是按照相反的顺序来存储参数的。so that rgvarg[0] contains the last argument and rgvarg[cArgs _ 1] contains the first argument。
以named arguments为例来说明DISPPARAMS的设置。
To omit an optional parameter when you use named arguments, you just don't pass it.
VARIANTARG SumArg; VariantInit(&SumArg); SumArg.vt = VT_I4; SumArg.lVal = 5;
//用DispId=1来表明上面设置的参数5是针对y的。
DISPID DispId = 1; // The y parameter
DISPPARAMS Params = { &SumArg, &DispId, 1, 1 };
// Now IDispatch::Invoke calls ISum::Sum(-1, 5)
Building Automation Clients in C++
1. Initializing COM (CoInitializeEx)
2. Instantiating a class (CoCreateInstance)
3. 调用QueryInterface获取IDispatch的实现类
4. 如果第三步成功则调用IDispatch::GetIDsOfNames获取DISPID
5. 调用invoke
此方法最困难的部分就是准备包含方法参数的DISPPARAMS结构体。
这里需要注意的是参数是按照从右到左的顺序存储在dispparams结构体中的数组中的。
namedarguments & positionarguments两种方式构建DISPPARAMS。