IDispatch接口介绍
1. C程序调用时,调用者必须预先知道接口规范(如,参数类型、参数字节长度、参数顺序等)。由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口。
2. IDispatch要求其实例必须自我描述,即拿到一个对象后,可从对象中直接获取调用方式,而无须预先明确。
3. IDispatch中通过VT_TYPE来指定相关类型,如 VT_I4为4字节整形、VT_BSTR为unicode字符串,VT_DISPATCH表示是一个IDispatch对象
4. 给对象中每一属性或函数(Method)分配一个整形Id和一个字符串name,调用者可以通过name字符串确定如何调用。如,若name为"length"的属性,调用者就理解为长度。由于这里通常是根据name来理解相应属性,因此name描述应足够准确。如,以"length()"为名称的函数实现整数相加功能就是不恰当的。
5. 使用IDispatch对象时,首相调用 IDispatch::GetIDsOfNames()将属性、函数名称作为参数,获取对应的属性、函数id。
6. 再调用IDispatch::Invoke() 将id作为参数,实际调用功能。
7. 若为获取属性值,则 Invoke()调用时,传入 Dispatch_PropertyGet标志。
8. 若为设置属性值,则Invoke()调用时,传入 Dispatch_PropertyPut标志。并在 DispParams参数中指定修该属性改为何值。DispParams结构说明见后。
9. 若为调用函数,则 Invoke()调用时,传入 Dispatch_Method标志。若该Method需要参数,则通过IDispatch::Invoke()的DispParams参数指定。
10. DispParams结构使用举例:
DISPPARAMS dispparams;
dispparams.rgdispidNamedArgs = &dispidOfNamedArgs;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 1;
dispparams.rgvarg = new VARIANTARG[1];
dispparams.rgvarg[0].vt = VT_I4;
dispparams.rgvarg[0].intVal = 123;
a. 上面代码是一个用于给Method传参的简单例子,先创建一个DispParams对象
b. cArgs指定Method中的参数个数。
c. cNamedArgs指定Method中已经命名的参数个数。(命名参数是对应无名参数的概念。有些语言可定义不定参数,此时IDispatch的描述中不会给参数分配名称,而是调用时以无名参数存在。如,JS中 Array对象的push()方法,可支持不定个数的参数)
d. rgvarg 为实际参数数组,每一元素表示一个参数,其中.vt表明此元素的数据类型,intVal项是一个C++联合结构,如vt == VT_I4时,应以intVal = xxx方式赋值;若 vt == VT_BSTR,则应以 bstrVal = xxx方式赋值
11. 举例:两个参数,都是无名称参数,第一个为整形,第二个为BSTR型
DISPPARAMS dispparams;
dispparams.rgdispidNamedArgs = NULL;
dispparams.cArgs = 2;
dispparams.cNamedArgs = 0;
dispparams.rgvarg = new VARIANTARG[2]; // 2个参数,分配2个空间
dispparams.rgvarg[0].vt = VT_I4; // 整形
dispparams.rgvarg[0].intVal = 123;
dispparams.rgvarg[1].vt = VT_BSTR; // 字符串型
dispparams.rgvarg[1].bstrVal = L"abcd";
IDispatch接口是COM自动化的核心。其实,IDispatch这个接口本身也很简单,只有4个方法:
- IDispatch : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
- /* [out] */ __RPC__out UINT *pctinfo) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
- /* [in] */ UINT iTInfo,
- /* [in] */ LCID lcid,
- /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
- /* [in] */ __RPC__in REFIID riid,
- /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
- /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
- /* [in] */ LCID lcid,
- /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = 0;
- virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
- /* [annotation][in] */
- _In_ DISPID dispIdMember,
- /* [annotation][in] */
- _In_ REFIID riid,
- /* [annotation][in] */
- _In_ LCID lcid,
- /* [annotation][in] */
- _In_ WORD wFlags,
- /* [annotation][out][in] */
- _In_ DISPPARAMS *pDispParams,
- /* [annotation][out] */
- _Out_opt_ VARIANT *pVarResult,
- /* [annotation][out] */
- _Out_opt_ EXCEPINFO *pExcepInfo,
- /* [annotation][out] */
- _Out_opt_ UINT *puArgErr) = 0;
- };
GetTypeInfoCount和GetTypeInfo以后再说。
先来看看比较熟悉的GetIDsOfNames和Invoke。
GetIDsOfNames
这个函数的主要功能就是:把COM接口的方法名字和参数(可选)映射成一组DISPID。DISPID就是一个LONG型:
- typedef LONG DISPID;
GetIDsOfNames()可以获取方法和属性。先来看一个例子,COM接口IMyCar
- [
- object,
- uuid(21B794E2-4857-4576-8FC2-CDAB2A486600),
- dual,
- nonextensible,
- pointer_default(unique)
- ]
- interface IMyCar : IDispatch{
- [id(1)] HRESULT Run();
- [id(2)] HRESULT AddGas([in] LONG add, [out] LONG* total);
- [propget, id(3)] HRESULT Gas([out, retval] LONG* pVal);
- };
这个接口里面有一个方法AddGas,它有两个参数,一个输入,一个输出。它的实现基本如下:
- STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total)
- {
- // TODO: Add your implementation code here
- m_Gas += add;
- *total = m_Gas;
- return S_OK;
- }
试试如何获取AddGas函数的id和参数index,看下面的代码。数组里面的第一个元素是方法名字,第二个/第三个是参数名字。
- CComPtr<IMyCar> spCar;
- spCar.CoCreateInstance(CLSID_MyCar);
- DISPID PropertyID[3] = {0};
- BSTR PropName[3];
- PropName[0] = SysAllocString(L"AddGas");
- PropName[1] = SysAllocString(L"add");
- PropName[2] = SysAllocString(L"total");
- HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID);
- SysFreeString(PropName[0]);
- SysFreeString(PropName[1]);
- SysFreeString(PropName[2]);
运行一下,可以得到如下结果:
PropertyID数组里面可以得到3个值:2, 0, 1.
2代表的是AddGas的id,跟idl文件里面的一样。
0表示"add"是第一个参数,1表示"total"是第二个参数。
Invoke
Invoke是IDispatch里面非常重要的一样函数,方法调用就靠这个函数了。函数原型:
- HRESULT Invoke(
- [in] DISPID dispIdMember,
- [in] REFIID riid,
- [in] LCID lcid,
- [in] WORD wFlags,
- [in, out] DISPPARAMS *pDispParams,
- [out] VARIANT *pVarResult,
- [out] EXCEPINFO *pExcepInfo,
- [out] UINT *puArgErr
- );
每一个参数的说明,看下面,从MSDN截来的。
先来看一个调用例子:
- CComVariant avarParams[2];
- avarParams[1].vt = VT_I4;
- avarParams[1] = 4;
- LONG vTotal = 0;
- avarParams[0].vt = VT_I4 | VT_BYREF;
- avarParams[0] = &vTotal;
- DISPPARAMS params = { avarParams,
- NULL, // Dispatch identifiers of named arguments.
- 2, // Number of arguments.
- 0 }; // Number of named arguments.
- hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
avarParams是一个具有2个元素的数组,它表示要调用的方法的参数,注意这里的参数是逆序的。比如avarParam[1]存放的是第一个参数,是一个输入参数;avarParams[0]存放的是第二个参数,是一个输出参数。
运行一下:
spCar里面的m_Gas初始值是0,我们调用的时候给它加了4,那么输出就应该是4.看上面的运行结果,vTotal确实是4.
这样我们就通过Invoke成功调用了COM组件的方法(而不是通过spCar->AddGas)。注意Invoke里面的第一个参数是一个DISPID,这个是从GetIDsOfNames来的。
使用Invoke也可以调用COM对象的属性。
比如上面的idl里面的Gas。
调用属性跟方法差不多,如果我们想读取一个属性,那么可以这么调:
- DISPID PropertyID2[1] = { 0 };
- BSTR PropName2[1];
- PropName2[0] = SysAllocString(L"Gas");
- hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2);
- SysFreeString(PropName2[0]);
- DISPPARAMS params2 = { NULL,
- NULL,
- 0,
- 0
- };
- CComVariant Result;
- hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL);
运行可以得到结果:
注意,Invoke的第五个参数啥都没,属性值是从第六个参数返回出来的。如果是方法调用的话,那么COM方法参数需要从第五个参数传进入,第六个参数是函数调用的返回值HRESULT.
我没有试过属性的put,不知道是从哪里传入,当有需要的时候查一下MSDN就行了。
以上就是GetIDsOfNames和Invoke的简要说明以及例子。之后再来分析更多的细节。
完整客户端代码:
- // ConsoleApplication4.cpp : Defines the entry point for the console application.
- //
- #include "stdafx.h"
- #include <thread>
- #include <atlbase.h>
- #include <atlcom.h>
- #include <algorithm>
- #include <vector>
- #include <memory>
- #include "../MyCom/MyCom_i.h"
- #include "../MyCom/MyCom_i.c"
- int _tmain(int argc, _TCHAR* argv[])
- {
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
- CComPtr<IMyCar> spCar;
- spCar.CoCreateInstance(CLSID_MyCar);
- // use IDispatch
- DISPID PropertyID[3] = {0};
- BSTR PropName[3];
- PropName[0] = SysAllocString(L"AddGas");
- PropName[1] = SysAllocString(L"add");
- PropName[2] = SysAllocString(L"total");
- HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, 3, LOCALE_SYSTEM_DEFAULT, PropertyID);
- SysFreeString(PropName[0]);
- SysFreeString(PropName[1]);
- SysFreeString(PropName[2]);
- CComVariant avarParams[2];
- avarParams[1].vt = VT_I4;
- avarParams[1] = 4;
- LONG vTotal = 0;
- avarParams[0].vt = VT_I4 | VT_BYREF;
- avarParams[0] = &vTotal;
- DISPPARAMS params = { avarParams,
- NULL, // Dispatch identifiers of named arguments.
- 2, // Number of arguments.
- 0 }; // Number of named arguments.
- hr = spCar->Invoke(PropertyID[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
- DISPID PropertyID2[1] = { 0 };
- BSTR PropName2[1];
- PropName2[0] = SysAllocString(L"Gas");
- hr = spCar->GetIDsOfNames(IID_NULL, PropName2, 1, LOCALE_SYSTEM_DEFAULT, PropertyID2);
- SysFreeString(PropName2[0]);
- DISPPARAMS params2 = { NULL,
- NULL,
- 0,
- 0
- };
- CComVariant Result;
- hr = spCar->Invoke(PropertyID2[0], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL);
- spCar.Release();
- CoUninitialize();
- return 0;
- }
相关的COM组件的代码:
- STDMETHODIMP CMyCar::AddGas(LONG add, LONG* total)
- {
- // TODO: Add your implementation code here
- m_Gas += add;
- *total = m_Gas;
- return S_OK;
- }
- STDMETHODIMP CMyCar::get_Gas(LONG* pVal)
- {
- // TODO: Add your implementation code here
- *pVal = m_Gas;
- return S_OK;
- }
IDispatch接口介绍的更多相关文章
- 【转载】COM 组件设计与应用(十)——IDispatch 接口 for VC.NET
原文:http://vckbase.com/index.php/wv/1225.html 一.前言 终于写到了第十回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常用 ...
- 【转载】COM 组件设计与应用(九)——IDispatch 接口 for VC6.0
原文: http://vckbase.com/index.php/wv/1224.html 一.前言 终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常 ...
- IDispatch接口 - GetIDsOfNames和Invoke(转)
IDispatch接口是COM自动化的核心.其实,IDispatch这个接口本身也很简单,只有4个方法: IDispatch : public IUnknown { public: virtual H ...
- Hive 接口介绍(Web UI/JDBC)
Hive 接口介绍(Web UI/JDBC) 实验简介 本次实验学习 Hive 的两种接口:Web UI 以及 JDBC. 一.实验环境说明 1. 环境登录 无需密码自动登录,系统用户名shiyanl ...
- 基于C#的SolidWorks插件开发(1)--SolidWorks API接口介绍
这是两年前毕业时写的一篇关于SolidWorks插件开发与公司PDM集成的毕业设计,最近闲来无事拿出来整理一下,大神们可以略过. 1.1 SolidWorks API接口 正确调用SolidWor ...
- SSH动态查询封装接口介绍
SSH动态查询封装接口介绍 1.查询记录总条数 public int count(Class c,Object[][] eq,Object[][] like,String[] group,String ...
- TComponent明明实现了IDispatch接口,但是却不加上声明,难道是因为FVCLComObject实体对象不存在?
TComponent明明实现了IDispatch接口,可是它的声明却是: TComponent = class(TPersistent, IInterface, IInterfaceComponent ...
- 【百度地图API】如何在地图上添加标注?——另有:坐标拾取工具+打车费用接口介绍
原文:[百度地图API]如何在地图上添加标注?--另有:坐标拾取工具+打车费用接口介绍 摘要: 在这篇文章中,你将学会,如何利用百度地图API进行标注.如何使用API新增的打车费用接口. ------ ...
- 如何删除要素类 IFeatureWorkspace 接口介绍(1)
如何删除要素类 要想删除一个要素类,那么必须先得到这个,在得到这个要素类的时候,我们要学习一个新的接口IFeatureWorkspace. IFeatureWorkspace 接口介绍 这个接口主要 ...
随机推荐
- shape的属性
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...
- apache开源项目--HttpComponents
HttpComponents 也就是以前的httpclient项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端/服务器编程工具包,并且它支持 HTTP 协议最新的版本和建议.不 ...
- 使用ServiceStackRedis链接Redis简介
注:关于如何在windows,linux下配置redis,详见这篇文章:) 目前网上有一些链接Redis的C#客户端工具,这里介绍其中也是目前我们企业版产品中所使用的ServiceStackRedis ...
- MySQL join的实现原理及优化思路
Join 的实现原理 在MySQL 中,只有一种Join 算法,也就是Nested Loop Join,没有其他很多数据库所提供的Hash Join,也没有Sort Merge Join.顾名思义,N ...
- CXF之四 cxf集成Spring
CXF原生支持spring,可以和Spring无缝集成.WebService框架CXF实战一在Tomcat中发布WebService(二)通过Spring Web实现CXFServlet.下面将Spr ...
- Java程序版权保护解决方案
通常C.C++等编程语言开发的程序都被编译成目标代码,这些目标代码都是本机器的二进制可执行代码.通常所有的源文件被编译.链接成一个可执行文件.在这些可执行文件中,编译器删除了程序中的变量名称.方法名称 ...
- MemoryMappedFile 内存映射文件 msdn
http://msdn.microsoft.com/zh-cn/library/dd997372%28v=vs.110%29.aspx 内存映射文件 .NET Framework 4.5 其他版本 1 ...
- bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)
Description Input Output Sample Input 4 1 2 2 3 3 4 0 0 1 1 1 0 0 0 Sample Output 1 HINT [思路] Hash,D ...
- codeforce 605B. Lazy Student
题意:n点,m条边.m条边里面标记为1的最小生成树的边,0为非最小生成树的边.给了每条边的权,如果能构成一个最小生成树则输出图,否则-1. 思路:先按权值小,为生成数边的顺序排序.(根据kruskal ...
- 【转载】free查看内存
http://blog.csdn.net/hylongsuny/article/details/7742995 free 命令相对于top 提供了更简洁的查看系统内存使用情况:$ free ...