IDispatch接口 - GetIDsOfNames和Invoke(转)
IDispatch接口是COM自动化的核心。其实,IDispatch这个接口本身也很简单,只有4个方法:
IDispatch : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo) = ; virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = ; virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ __RPC__in REFIID riid,
/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
/* [range][in] */ __RPC__in_range(,) UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = ; 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) = ; };
GetTypeInfoCount和GetTypeInfo以后再说。
先来看看比较熟悉的GetIDsOfNames和Invoke。
GetIDsOfNames
这个函数的主要功能就是:把COM接口的方法名字和参数(可选)映射成一组DISPID。DISPID就是一个LONG型:
typedef LONG DISPID;
GetIDsOfNames()可以获取方法和属性。先来看一个例子,COM接口IMyCar
[
object,
uuid(21B794E2---8FC2-CDAB2A486600),
dual,
nonextensible,
pointer_default(unique)
]
interface IMyCar : IDispatch{
[id()] HRESULT Run();
[id()] HRESULT AddGas([in] LONG add, [out] LONG* total);
[propget, id()] 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[] = {};
BSTR PropName[]; PropName[] = SysAllocString(L"AddGas");
PropName[] = SysAllocString(L"add");
PropName[] = SysAllocString(L"total");
HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, , LOCALE_SYSTEM_DEFAULT, PropertyID); SysFreeString(PropName[]);
SysFreeString(PropName[]);
SysFreeString(PropName[]);
运行一下,可以得到如下结果:
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[];
avarParams[].vt = VT_I4;
avarParams[] = ; LONG vTotal = ;
avarParams[].vt = VT_I4 | VT_BYREF;
avarParams[] = &vTotal; DISPPARAMS params = { avarParams,
NULL, // Dispatch identifiers of named arguments.
, // Number of arguments.
}; // Number of named arguments. hr = spCar->Invoke(PropertyID[], 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[] = { };
BSTR PropName2[]; PropName2[] = SysAllocString(L"Gas"); hr = spCar->GetIDsOfNames(IID_NULL, PropName2, , LOCALE_SYSTEM_DEFAULT, PropertyID2); SysFreeString(PropName2[]); DISPPARAMS params2 = { NULL,
NULL,
, }; CComVariant Result;
hr = spCar->Invoke(PropertyID2[], 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[] = {};
BSTR PropName[]; PropName[] = SysAllocString(L"AddGas");
PropName[] = SysAllocString(L"add");
PropName[] = SysAllocString(L"total");
HRESULT hr = spCar->GetIDsOfNames(IID_NULL, PropName, , LOCALE_SYSTEM_DEFAULT, PropertyID); SysFreeString(PropName[]);
SysFreeString(PropName[]);
SysFreeString(PropName[]); CComVariant avarParams[];
avarParams[].vt = VT_I4;
avarParams[] = ; LONG vTotal = ;
avarParams[].vt = VT_I4 | VT_BYREF;
avarParams[] = &vTotal; DISPPARAMS params = { avarParams,
NULL, // Dispatch identifiers of named arguments.
, // Number of arguments.
}; // Number of named arguments. hr = spCar->Invoke(PropertyID[], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); DISPID PropertyID2[] = { };
BSTR PropName2[]; PropName2[] = SysAllocString(L"Gas"); hr = spCar->GetIDsOfNames(IID_NULL, PropName2, , LOCALE_SYSTEM_DEFAULT, PropertyID2); SysFreeString(PropName2[]); DISPPARAMS params2 = { NULL,
NULL,
, }; CComVariant Result;
hr = spCar->Invoke(PropertyID2[], IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms2, &Result, NULL, NULL); spCar.Release(); CoUninitialize(); return ;
}
相关的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;
}
上述摘自:http://blog.csdn.net/zj510/article/details/39494873
引申(原创):
1、CHtmlDialog调用js ,无参数有返回值情况:
js函数如下:
function GetFileChange()
{
return bFileChange;
}
MFC调用js函数如下:
BOOL CHTMLDialogDlg::CallJSScript(const CString strFunc, _variant_t* pVarResult)
{
CComPtr<IDispatch> spScript;
if(m_spHtmlDoc==NULL)
return FALSE;
HRESULT hr = m_spHtmlDoc->get_Script(&spScript);
if(!SUCCEEDED(hr))
{
return FALSE;
}
CComBSTR bstrFunc(strFunc);
DISPID dispid = NULL;
HRESULT hrr = spScript->GetIDsOfNames(IID_NULL, &bstrFunc, , LOCALE_SYSTEM_DEFAULT, &dispid);
if(FAILED(hrr))
{
return FALSE;
} DISPPARAMS dispparams;
memset(&dispparams, , sizeof(dispparams)); EXCEPINFO excepInfo;
memset(&excepInfo, , sizeof(excepInfo));
_variant_t vaResult;
UINT nArgErr = (UINT)-;
hrr = spScript->Invoke(dispid, IID_NULL, , DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);
delete[] dispparams.rgvarg; if(FAILED(hrr))
{
return FALSE;
}
if(pVarResult)
{
*pVarResult = vaResult;
}
return TRUE;
}
调用如下
if(CallJSScript("GetFileChange", &retParam)){
if(retParam.vt == VT_BOOL){
bFileChange = retParam.boolVal;
}
}
2、CHtmlDialog调用js ,有参数有返回值情况:
function OpenArgvFile(strArgvFile){
.....
return bRet;
}
MFC调用js函数如下:
BOOL CHtmlDialogDlg::CallJSScript(const CString strFunc, const CStringArray ¶mArray, _variant_t* pVarResult)
{
CComPtr<IDispatch> spScript;
if(m_spHtmlDoc==NULL)
return FALSE;
HRESULT hr = m_spHtmlDoc->get_Script(&spScript);
if(!SUCCEEDED(hr))
{
return FALSE;
}
CComBSTR bstrFunc(strFunc);
DISPID dispid = NULL;
HRESULT hrr = spScript->GetIDsOfNames(IID_NULL, &bstrFunc, , LOCALE_SYSTEM_DEFAULT, &dispid);
if(FAILED(hrr))
{
return FALSE;
}
//
const int arraySize = paramArray.GetSize(); DISPPARAMS dispparams;
memset(&dispparams, , sizeof dispparams);
dispparams.cArgs = arraySize;
dispparams.rgvarg = new VARIANT[dispparams.cArgs]; for( int i = ; i < arraySize; i++)
{
CComBSTR bstr = paramArray.GetAt(arraySize - - i); // back reading
bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
dispparams.rgvarg[i].vt = VT_BSTR;
}
dispparams.cNamedArgs = ;
//
//DISPPARAMS dispparams;
//memset(&dispparams, 0, sizeof(dispparams)); EXCEPINFO excepInfo;
memset(&excepInfo, , sizeof(excepInfo));
_variant_t vaResult;
UINT nArgErr = (UINT)-;
hrr = spScript->Invoke(dispid, IID_NULL, , DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);
delete[] dispparams.rgvarg; if(FAILED(hrr))
{
return FALSE;
}
if(pVarResult)
{
*pVarResult = vaResult;
}
return TRUE;
}
调用如下
CStringArray paramArray;
_variant_t retParam;
paramArray.Add(m_strArgvFile);
if(!CallJSScript("OpenArgvFile", paramArray, &retParam)){...}
附:
COM接口指针很危险,因为使用过程中需要每一个使用者都要严格并且正确的AddRef和Release,一旦出现问题,就会造成对象不能被正常释放,或者对象被重复删除,造成程序崩溃。所以使用COM接口,必须小心翼翼才行。
但是,即使所有的代码中,都正确的AddRef和Release,也不一定能保证万无一失,例如:
void SomeApp( IHello * pHello )
{
IHello* pCopy = pHello;
pCopy->AddRef();
OtherApp();
pCopy->Hello();
pCopy->Release();
}
看起来好像无懈可击,但是假设OtherApp中抛出了异常,那么pCopy->Release不就被跳过去了吗?
幸好,所有的问题都从简单到复杂,再从复杂到简单的,因为我们有CComPtr!
CComPtr被称为智能指针,是ATL提供的一个模版类,能够从语法上自动完成AddRef和Release。(源代码在atlbase.h中)
CComPtr的用法很简单,以IHello*为例,将程序中所有接口指针类型(除了参数),都使用CComPtr<IHello> 代替即可。即程序中除了参数之外,再也不要使用IHello*,全部以CComPtr<IHello>代替。
CComPtr的用法和普通COM指针几乎一样,另外使用中有以下几点需要注意。
1. CComPtr已经保证了AddRef和Release的正确调用,所以不需要,也不能够再调用AddRef和Release。
2. 如果要释放一个智能指针,直接给它赋NULL值即可。(这一点要牢记曾因为没有设置为null而出错)
3. CComPtr本身析构的时候会释放COM指针。
4. 当对CComPtr使用&运算符(取指针地址)的时候,要确保CComPtr为NUL。(因为通过CComPtr的地址对CComPtr赋值时,不会自动调用AddRef,若不为NULL,则前面的指针不能释放,CComPtr会使用assert报警)
以刚才的程序为例:
void SomeApp( IHello * pHello )
{
CComPtr<IHello> pCopy = pHello;
OtherApp();
pCopy->Hello();
}
由于pCopy是一个局部的对象,所以即使OtherApp()抛出异常,pCopy也会被析构,指针能够被释放。
如果不想在程序临近发布前,还因为COM指针的引用计数造成崩溃的话,就牢记这一点吧:程序中除了参数之外,不要直接使用COM指针类型,一定要全部以CComPtr<IXXX>代替。
IDispatch接口 - GetIDsOfNames和Invoke(转)的更多相关文章
- IDispatch接口介绍
1. C程序调用时,调用者必须预先知道接口规范(如,参数类型.参数字节长度.参数顺序等).由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口. 2 ...
- 【转载】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)是非常常 ...
- TComponent明明实现了IDispatch接口,但是却不加上声明,难道是因为FVCLComObject实体对象不存在?
TComponent明明实现了IDispatch接口,可是它的声明却是: TComponent = class(TPersistent, IInterface, IInterfaceComponent ...
- wpf 错误 执行了 QueryInterface 调用,请求提供 COM 可见的托管类“BoilerMonitoringV1._0.MapControl”的默认 IDispatch 接口。
在做wpf嵌入地图时,在自定义的WebBrowser 里面使用JavaScript调用外部方法的时报的错误 在原来的WinForm里 我们只要在窗体类设置的头部设置个 [System.Runtime. ...
- vs2019 Com组件初探-通过IDispatch接口调用Com
vs2019 Com组件初探-简单的COM编写以及实现跨语言调用 上一篇实现了如何编写基于IDipatch接口的COM以及vbs如何调用编写的COM 本次主要是实现VBS的CreateObject函数 ...
- CEF3开发者系列之外篇——IE中JS与C++交互
使用IE内核开发客户端产品,系统和前端页面之间的交互,通常给开发和维护带来很大的便利性.但操作系统和前端之间的交互却是比较复杂的.具体来说就是脚本语言和编译语言的交互.在IE内核中html和css虽然 ...
- 利用Delphi编写IE扩展
就是如何使IE扩展组件可以响应事件. 在自己的程序中使用过WebBrowser控件的朋友都知道,WebBrowser控件定义了诸如BeforeNavigate.DownloadComplete ...
- COM组件 IDispatch 及双接口的调用
转自:http://blog.csdn.net/cnhk1225/article/details/50555647 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和 ...
随机推荐
- bjfu1211 推公式,筛素数
题目是求fun(n)的值 fun(n)= Gcd(3)+Gcd(4)+…+Gcd(i)+…+Gcd(n).Gcd(n)=gcd(C[n][1],C[n][2],……,C[n][n-1])C[n][k] ...
- 《Python核心编程》 第六章 序列 - 课后习题
课后习题 6–1.字符串.string 模块中是否有一种字符串方法或者函数可以帮我鉴定一下一个字符串是否是另一个大字符串的一部分? 答:成员关系操作符(in.not in) import string ...
- Oracle中错误代码ORA-02292 违反了完整性约束条件解决
百度处理: A表被B表引用,删除A表的时候提示ORA-02292,A表的主键被引用了,虽然已经把B表的数据全部删除掉,但仍然删除不了A表的数据.解决办法: 用禁用约束语句把A表的主键约束给禁用掉.1. ...
- UNDO表空间设置
flashback query和flashback table都是以用UNDO表空间的内容来进行恢复数据 查看undo内容保存的时间: SQL> show parameter undo_re N ...
- SeaJS学习笔记(一) ./ 和 ../ 区别
最近要去实习,公司里使用sea.js进行模块化开发 具体下载安装就不多说了,请参见SeaJS官网 <!DOCTYPE html> <html> <head> < ...
- web前端开发分享-css,js入门篇(转)
转自:http://www.cnblogs.com/jikey/p/3600308.html 关注前端这么多年,没有大的成就,就入门期间积累了不少技巧与心得,跟大家分享一下,不一定都适合每个人,毕竟人 ...
- java的动态代理机制
前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...
- Web Service学习之六:CXF解决无法处理的数据类型
CXF不能够处理像Map复杂的数据类型,需要单独转换处理. 总体思路:创建一个转换器和一个对应的可以处理的数据结构类型,将不能处理的类型转换成可以处理的类型: 步骤: 一.创建一个可以处理的类型 举例 ...
- thymeleaf中的内联[ [ ] ]
一.文本内联 [[…]]之间的表达式在Thymeleaf被认为是内联表达式,在其中您可以使用任何类型的表达式,也会有效th:text属性. <p>Hello, [[${session.us ...
- [iOS基础控件 - 6.9.2] 静态单元格 QQ功能列表
使用storyboard设计静态的表格数据 A.实现步骤 1.控制器继承UITableViewController 2.在storyboard中使用TableViewController,删除原来 ...