IDispatch接口 - GetIDsOfNames和Invoke(转)
2016-07-28 11:07
309 查看
IDispatch接口是COM自动化的核心。其实,IDispatch这个接口本身也很简单,只有4个方法:
GetTypeInfoCount和GetTypeInfo以后再说。
先来看看比较熟悉的GetIDsOfNames和Invoke。
GetIDsOfNames
这个函数的主要功能就是:把COM接口的方法名字和参数(可选)映射成一组DISPID。DISPID就是一个LONG型:
GetIDsOfNames()可以获取方法和属性。先来看一个例子,COM接口IMyCar
这个接口里面有一个方法AddGas,它有两个参数,一个输入,一个输出。它的实现基本如下:
试试如何获取AddGas函数的id和参数index,看下面的代码。数组里面的第一个元素是方法名字,第二个/第三个是参数名字。
运行一下,可以得到如下结果:
PropertyID数组里面可以得到3个值:2, 0, 1.
2代表的是AddGas的id,跟idl文件里面的一样。
0表示"add"是第一个参数,1表示"total"是第二个参数。
Invoke
Invoke是IDispatch里面非常重要的一样函数,方法调用就靠这个函数了。函数原型:
每一个参数的说明,看下面,从MSDN截来的。
先来看一个调用例子:
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。
调用属性跟方法差不多,如果我们想读取一个属性,那么可以这么调:
运行可以得到结果:
注意,Invoke的第五个参数啥都没,属性值是从第六个参数返回出来的。如果是方法调用的话,那么COM方法参数需要从第五个参数传进入,第六个参数是函数调用的返回值HRESULT.
我没有试过属性的put,不知道是从哪里传入,当有需要的时候查一下MSDN就行了。
以上就是GetIDsOfNames和Invoke的简要说明以及例子。之后再来分析更多的细节。
完整客户端代码:
相关的COM组件的代码:
上述摘自:http://blog.csdn.net/zj510/article/details/39494873
引申(原创):
1、CHtmlDialog调用js ,无参数有返回值情况:
js函数如下:
MFC调用js函数如下:
调用如下
2、CHtmlDialog调用js ,有参数有返回值情况:
MFC调用js函数如下:
调用如下
附:
CComPtr用法
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 : 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;
}
上述摘自: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, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if(FAILED(hrr)) { return FALSE; } DISPPARAMS dispparams; memset(&dispparams, 0, sizeof(dispparams)); EXCEPINFO excepInfo; memset(&excepInfo, 0, sizeof(excepInfo)); _variant_t vaResult; UINT nArgErr = (UINT)-1; hrr = spScript->Invoke(dispid, IID_NULL, 0, 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, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if(FAILED(hrr)) { return FALSE; } // const int arraySize = paramArray.GetSize(); DISPPARAMS dispparams; memset(&dispparams, 0, sizeof dispparams); dispparams.cArgs = arraySize; dispparams.rgvarg = new VARIANT[dispparams.cArgs]; for( int i = 0; i < arraySize; i++) { CComBSTR bstr = paramArray.GetAt(arraySize - 1 - i); // back reading bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); dispparams.rgvarg[i].vt = VT_BSTR; } dispparams.cNamedArgs = 0; // //DISPPARAMS dispparams; //memset(&dispparams, 0, sizeof(dispparams)); EXCEPINFO excepInfo; memset(&excepInfo, 0, sizeof(excepInfo)); _variant_t vaResult; UINT nArgErr = (UINT)-1; hrr = spScript->Invoke(dispid, IID_NULL, 0, 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)){...}
附:
CComPtr用法
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>代替。
相关文章推荐
- codevs 1913 数字梯形问题 费用流
- 音频播放AVFoundation框架
- 城市三级联动
- 自动化前与自动化后(关于KPI轻谈)
- POJ1569 Myacm Triangles 判断点与三角形的关系
- POJ1569 Myacm Triangles 判断点与三角形的关系
- 基于Android的百度地图开发
- Android编程之ListView分页加载数据
- PHY驱动心得
- Intellij IDEA 自动生成 serialVersionUID
- NSString的使用
- 24条货真价实的PHP代码优化技巧
- 题目171 聪明的kk
- Error:(20, 45) 错误: <匿名com.example.administrator.androidthread.MainActivity$1>不是抽象的, 并且未覆盖Handler中的抽象
- 键盘遮挡输入框的问题
- 窗口、窗体之间的关系
- Window对象
- 数组切分 动态规划 递归
- SUSE 11中安装gcc开发环境
- Camera.PreviewCallback讲解