让普通C++类轻松支持IDispatch自动化接口
2011-02-15 16:33
423 查看
熟悉MFC和ATL/WTL的人一定都很熟悉它们的消息映射表,利用宏进行填表是一种非常简洁非常优雅的编码方式,通俗易懂维护方便。
对于宏来说,使用越是简单,其内部构造就越复杂。很多人都说MFC把宏定义运用到了极致,以前我同意,现在有所保留,为什么?因为MFC的框架太古老了,
它使用的也是早期的宏特性。社会在发展,技术在进步,如今的标准已经对宏的特性进行了扩展。充分利用扩展的宏特性,能***出更加简洁的代码。
说了很多废话,言归正传。在大多数C++软件项目中,往往没有自动化需求,程序员通常会创造出下面的代码:
每个项目可能会出现一大堆的类定义,并且程序运作良好。忽然某一天,项目主管要求对代码执行自动化改造,或者为了***出能在浏览器里执行的OCX控件,或者能被某些脚本语言调用,或者能嵌入到IIS中的ASP
页面内执行……,总之,老板想让WEB开发人员也能逗这条小狗(CDog)。
我相信每个人针对这种需求都有这样一些感觉:我有很多改造方案,可是不管哪个方案,工作量都很大。MFC项目天然支持自动化,但它的个头太庞大了。ATL
也是纯种的自动化解决方案,但必须在纯种的ATL项目中才能使用它的各种向导功能,如果新建一个ATL项目,把这些现成的类搬进来再改造也不是一件容易的
事,尤其是涉及到类与类之间的继承关系调整。有些人可能会想直接利用ATL中的IDispatchImpl<>模板类,想法很好,但几乎实现
不了,因为IDispatchImpl内部严重依赖类型库,或者保存在注册表中,或者保存在程序资源中,总之IDL文件是必须要有的,而手工完成这些的工
作量还不如新建一个ATL项目来得划算。还有一种方法就是让CDog直接从IDispatch派生,然后实现IDispatch的所有7个方法,在它的
Invoke实现中根据DISPID不同分别访问CDog的成员变量或者调用成员函数,或者单独实现一个从IDispatch派生的类专门处理CDog,
但这种方式的工作量也是显而易见的,每个类都需要独立派生,或者每个类都需要创建一个对应的自动化支持类。
超级无敌的宏现身了,这就是我的解决方案。它的最大好处就是用最小的代价完成自动化改造,你的现有项目可以是SDK/MFC/ATL,无需创建新项目,无
需IDL文件,无需类型库,无需注册组件。这种解决方案也是类似于消息映射宏,填表即可完成。还是以CDog的改造为例,老板希望这个dog具备Name
和Height属性,也具备Drink和Eat方法,那么使用宏解决方案,将会是下面的代码:
填完上面的表格,这个CDog已经可以被WEB开发人员牵出来遛了。顺便解释一下这个映射表的用法:
1、映射表以Begin_Disp_Map开始,唯一的参数就是需要改造的类CDog
2、映射表以End_Disp_Map结束
3、每一个属性或者方法占用一行表项
4、
属性的用法 Disp_Property(dispid, property_name, property_type),以
Disp_Property(1, Name, CString)
为例,属性的DISPID是1,属性的名称是Name,属性的类型是CString。这个宏表示Name属性是可读写的,如果属性只读(如Height)
应该用 Disp_PropertyGet,如果属性只写应该用 Disp_PropertyPut,参数的含义是一致的
5、
方法的用法 Disp_Method(dispid, method_name, return_type, param_count,
param1_type, …, paramN_type),以 Disp_Method(3, Drink, void, 0)
为例,方法的DISPID是3,方法名称是Drink,方法函数返回值类型是void,方法没有参数。再看看 Disp_Method(4, Eat,
bool, 2, long, long)
示例,方法的DISPID是4,方法名称是Eat,方法函数返回类型是bool,方法有2个参数,第一个参数类型是long,第二个参数类型是long
这个自动化接口怎么用呢?通过下面的例子可以看出来,使用非常简单,因为映射宏暗中添加了 CDog::GetDispatch() 成员函数。
事
实上,我实现的自动化支持的宏定义曾经有两个版本,用法相似,但是实现方法完全不同。第一个版本采用的是自动创建类型库,然后通过
CreateStdDispatch() 函数创建 IDispatch
接口,这种方式有一些限制和缺陷,由于调用了API,我也不清楚内部有多少限制,因此导致第二个版本的诞生,这个版本完全在我自己的“掌控”中,可随时调
整和改造。此版本是一个独立的头文件macro.h,里面几乎全部是宏定义,还有几个作为辅助工具的模板类定义和数据结构。任何人都可以随意使用该文件,
也可以随意修改拷贝,不用交版税^_^。
还有必要提醒一下这套解决方案的使用条件:
1、Windows平台,VC版本至少是2005,VC6/2003等早期版本不能使用。
2、用到了少量的ATL头文件,如果实际使用,可能需要自己添加对这些头文件的包含。
3、不依赖任何其它库,任何项目类型都可以使用。
4、变量类型请尽量使用VARIANT中支持的基础类型。
如果用上面的示例代码进行实际的编译测试,你会发现编译通不过,原因在于 Name 的类型是
CString,这是一种其它库封装的高级类型,VARIANT不能识别。有两种解决方法:一种是懒人用的,把CString改成
CComBSTR,映射表中也需要对应修改;另一种是高级方法,这需要使用者完全理解了我实现的宏定义,通过自己创建特化模板类来扩充对 CString
的支持,例如 template<> class CVarTypeInfo< CString > { … };
最后,我得申明一下,这套宏代码我自己一直在使用,而且一直在根据自己的实际需求改进和扩充,但本人没有义务一定要把最新改进版本贡献出来。
最后的最后,提供整个宏定义的 macro.h 头文件,自动化支持的部分可以直接被使用,宏的使用供有心人研究吧。
对于宏来说,使用越是简单,其内部构造就越复杂。很多人都说MFC把宏定义运用到了极致,以前我同意,现在有所保留,为什么?因为MFC的框架太古老了,
它使用的也是早期的宏特性。社会在发展,技术在进步,如今的标准已经对宏的特性进行了扩展。充分利用扩展的宏特性,能***出更加简洁的代码。
说了很多废话,言归正传。在大多数C++软件项目中,往往没有自动化需求,程序员通常会创造出下面的代码:
class CDog : public CAnimal { public: CString Name; long Height; void Drink(); bool Eat(long lType, long lNum); };
每个项目可能会出现一大堆的类定义,并且程序运作良好。忽然某一天,项目主管要求对代码执行自动化改造,或者为了***出能在浏览器里执行的OCX控件,或者能被某些脚本语言调用,或者能嵌入到IIS中的ASP
页面内执行……,总之,老板想让WEB开发人员也能逗这条小狗(CDog)。
我相信每个人针对这种需求都有这样一些感觉:我有很多改造方案,可是不管哪个方案,工作量都很大。MFC项目天然支持自动化,但它的个头太庞大了。ATL
也是纯种的自动化解决方案,但必须在纯种的ATL项目中才能使用它的各种向导功能,如果新建一个ATL项目,把这些现成的类搬进来再改造也不是一件容易的
事,尤其是涉及到类与类之间的继承关系调整。有些人可能会想直接利用ATL中的IDispatchImpl<>模板类,想法很好,但几乎实现
不了,因为IDispatchImpl内部严重依赖类型库,或者保存在注册表中,或者保存在程序资源中,总之IDL文件是必须要有的,而手工完成这些的工
作量还不如新建一个ATL项目来得划算。还有一种方法就是让CDog直接从IDispatch派生,然后实现IDispatch的所有7个方法,在它的
Invoke实现中根据DISPID不同分别访问CDog的成员变量或者调用成员函数,或者单独实现一个从IDispatch派生的类专门处理CDog,
但这种方式的工作量也是显而易见的,每个类都需要独立派生,或者每个类都需要创建一个对应的自动化支持类。
超级无敌的宏现身了,这就是我的解决方案。它的最大好处就是用最小的代价完成自动化改造,你的现有项目可以是SDK/MFC/ATL,无需创建新项目,无
需IDL文件,无需类型库,无需注册组件。这种解决方案也是类似于消息映射宏,填表即可完成。还是以CDog的改造为例,老板希望这个dog具备Name
和Height属性,也具备Drink和Eat方法,那么使用宏解决方案,将会是下面的代码:
class CDog : public CAnimal { public: CString Name; long Height; void Drink(); bool Eat(long lType, long lNum); Begin_Disp_Map(CDog) Disp_Property(1, Name, CString) Disp_PropertyGet(2, Height, long) Disp_Method(3, Drink, void, 0) Disp_Method(4, Eat, bool, 2, long, long) End_Disp_Map() };
填完上面的表格,这个CDog已经可以被WEB开发人员牵出来遛了。顺便解释一下这个映射表的用法:
1、映射表以Begin_Disp_Map开始,唯一的参数就是需要改造的类CDog
2、映射表以End_Disp_Map结束
3、每一个属性或者方法占用一行表项
4、
属性的用法 Disp_Property(dispid, property_name, property_type),以
Disp_Property(1, Name, CString)
为例,属性的DISPID是1,属性的名称是Name,属性的类型是CString。这个宏表示Name属性是可读写的,如果属性只读(如Height)
应该用 Disp_PropertyGet,如果属性只写应该用 Disp_PropertyPut,参数的含义是一致的
5、
方法的用法 Disp_Method(dispid, method_name, return_type, param_count,
param1_type, …, paramN_type),以 Disp_Method(3, Drink, void, 0)
为例,方法的DISPID是3,方法名称是Drink,方法函数返回值类型是void,方法没有参数。再看看 Disp_Method(4, Eat,
bool, 2, long, long)
示例,方法的DISPID是4,方法名称是Eat,方法函数返回类型是bool,方法有2个参数,第一个参数类型是long,第二个参数类型是long
这个自动化接口怎么用呢?通过下面的例子可以看出来,使用非常简单,因为映射宏暗中添加了 CDog::GetDispatch() 成员函数。
CDog dog; CComPtr<IDispatch> disp = dog.GetDispatch(); disp.Invoke0(OLESTR("Drink")); // 这句自动化调用将导致 CDog::Drink() 函数被执行
事
实上,我实现的自动化支持的宏定义曾经有两个版本,用法相似,但是实现方法完全不同。第一个版本采用的是自动创建类型库,然后通过
CreateStdDispatch() 函数创建 IDispatch
接口,这种方式有一些限制和缺陷,由于调用了API,我也不清楚内部有多少限制,因此导致第二个版本的诞生,这个版本完全在我自己的“掌控”中,可随时调
整和改造。此版本是一个独立的头文件macro.h,里面几乎全部是宏定义,还有几个作为辅助工具的模板类定义和数据结构。任何人都可以随意使用该文件,
也可以随意修改拷贝,不用交版税^_^。
还有必要提醒一下这套解决方案的使用条件:
1、Windows平台,VC版本至少是2005,VC6/2003等早期版本不能使用。
2、用到了少量的ATL头文件,如果实际使用,可能需要自己添加对这些头文件的包含。
3、不依赖任何其它库,任何项目类型都可以使用。
4、变量类型请尽量使用VARIANT中支持的基础类型。
如果用上面的示例代码进行实际的编译测试,你会发现编译通不过,原因在于 Name 的类型是
CString,这是一种其它库封装的高级类型,VARIANT不能识别。有两种解决方法:一种是懒人用的,把CString改成
CComBSTR,映射表中也需要对应修改;另一种是高级方法,这需要使用者完全理解了我实现的宏定义,通过自己创建特化模板类来扩充对 CString
的支持,例如 template<> class CVarTypeInfo< CString > { … };
最后,我得申明一下,这套宏代码我自己一直在使用,而且一直在根据自己的实际需求改进和扩充,但本人没有义务一定要把最新改进版本贡献出来。
最后的最后,提供整个宏定义的 macro.h 头文件,自动化支持的部分可以直接被使用,宏的使用供有心人研究吧。
#ifndef __MACRO_H__ #define __MACRO_H__ #pragma once ////////////////////////////////////////////////////////////////////////// // 基础工具宏定义 #define __for_each_number(v, ...) / v(0, __VA_ARGS__) / v(1, __VA_ARGS__) / v(2, __VA_ARGS__) / v(3, __VA_ARGS__) / v(4, __VA_ARGS__) / v(5, __VA_ARGS__) / v(6, __VA_ARGS__) / v(7, __VA_ARGS__) / v(8, __VA_ARGS__) / v(9, __VA_ARGS__) / v(10, __VA_ARGS__) / v(11, __VA_ARGS__) / v(12, __VA_ARGS__) / v(13, __VA_ARGS__) / v(14, __VA_ARGS__) / v(15, __VA_ARGS__) // 数值减 1 的常数 #define __cntdec_0 0 #define __cntdec_1 0 #define __cntdec_2 1 #define __cntdec_3 2 #define __cntdec_4 3 #define __cntdec_5 4 #define __cntdec_6 5 #define __cntdec_7 6 #define __cntdec_8 7 #define __cntdec_9 8 #define __cntdec_10 9 #define __cntdec_11 10 #define __cntdec_12 11 #define __cntdec_13 12 #define __cntdec_14 13 #define __cntdec_15 14 #define __cntdec(n) __cntdec_##n // 连接两个符号 #define __connect2(x, y) x##y #define __connect(x, y) __connect2(x, y) // 生成不同个数的顺序符号 #define __repeat_0(m, ...) #define __repeat_1(m, ...) __repeat_0(m, __VA_ARGS__) m(1, __VA_ARGS__) #define __repeat_2(m, ...) __repeat_1(m, __VA_ARGS__) m(2, __VA_ARGS__) #define __repeat_3(m, ...) __repeat_2(m, __VA_ARGS__) m(3, __VA_ARGS__) #define __repeat_4(m, ...) __repeat_3(m, __VA_ARGS__) m(4, __VA_ARGS__) #define __repeat_5(m, ...) __repeat_4(m, __VA_ARGS__) m(5, __VA_ARGS__) #define __repeat_6(m, ...) __repeat_5(m, __VA_ARGS__) m(6, __VA_ARGS__) #define __repeat_7(m, ...) __repeat_6(m, __VA_ARGS__) m(7, __VA_ARGS__) #define __repeat_8(m, ...) __repeat_7(m, __VA_ARGS__) m(8, __VA_ARGS__) #define __repeat_9(m, ...) __repeat_8(m, __VA_ARGS__) m(9, __VA_ARGS__) #define __repeat_10(m, ...) __repeat_9(m, __VA_ARGS__) m(10, __VA_ARGS__) #define __repeat_11(m, ...) __repeat_10(m, __VA_ARGS__) m(11, __VA_ARGS__) #define __repeat_12(m, ...) __repeat_11(m, __VA_ARGS__) m(12, __VA_ARGS__) #define __repeat_13(m, ...) __repeat_12(m, __VA_ARGS__) m(13, __VA_ARGS__) #define __repeat_14(m, ...) __repeat_13(m, __VA_ARGS__) m(14, __VA_ARGS__) #define __repeat_15(m, ...) __repeat_14(m, __VA_ARGS__) m(15, __VA_ARGS__) #define __last_repeat_0(m, ...) #define __last_repeat_1(m, ...) m(1, __VA_ARGS__) #define __last_repeat_2(m, ...) m(2, __VA_ARGS__) #define __last_repeat_3(m, ...) m(3, __VA_ARGS__) #define __last_repeat_4(m, ...) m(4, __VA_ARGS__) #define __last_repeat_5(m, ...) m(5, __VA_ARGS__) #define __last_repeat_6(m, ...) m(6, __VA_ARGS__) #define __last_repeat_7(m, ...) m(7, __VA_ARGS__) #define __last_repeat_8(m, ...) m(8, __VA_ARGS__) #define __last_repeat_9(m, ...) m(9, __VA_ARGS__) #define __last_repeat_10(m, ...) m(10, __VA_ARGS__) #define __last_repeat_11(m, ...) m(11, __VA_ARGS__) #define __last_repeat_12(m, ...) m(12, __VA_ARGS__) #define __last_repeat_13(m, ...) m(13, __VA_ARGS__) #define __last_repeat_14(m, ...) m(14, __VA_ARGS__) #define __last_repeat_15(m, ...) m(15, __VA_ARGS__) #define __repeat(n, m_begin, m_end, ...) __connect(__repeat_, __cntdec(n))(m_begin, __VA_ARGS__) __connect(__last_repeat_, n)(m_end, __VA_ARGS__) // 基础工具宏结束 ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Add IDispatch to class ////////////////////////////////////////////////////////////////////////// // 扩充 CVarTypeInfo 模板类的定义 //template<> //class CVarTypeInfo< void > //{ //public: // static const VARTYPE VT = VT_EMPTY; // //static char VARIANT::* const pmField; //}; template<> class CVarTypeInfo< bool > { public: static const VARTYPE VT = VT_BOOL; static VARIANT_BOOL VARIANT::* const pmField; }; __declspec( selectany ) VARIANT_BOOL VARIANT::* const CVarTypeInfo< bool >::pmField = &VARIANT::boolVal; // 扩充 CComBSTR 类型,用这种类型代替BSTR,能防止内存泄露或者内存释放错误 template<> class CVarTypeInfo< CComBSTR > { public: static const VARTYPE VT = VT_BSTR; static BSTR VARIANT::* const pmField; }; __declspec( selectany ) BSTR VARIANT::* const CVarTypeInfo< CComBSTR >::pmField = &VARIANT::bstrVal; // END of CVarTypeInfo. 使用者可以自行扩充新的类型,例如用 CString 来保存字符串 ////////////////////////////////////////////////////////////////////////// // 定义多参数的模板类 ////////////////////////////////////////////////////////////////////////// // 方法工具模板类和工具宏 #define __tparam(n, ...) typename T##n, #define __tparam_end(n, ...) typename T##n #define __param_type(n, ...) if (FAILED(v[n-1].ChangeType(CVarTypeInfo<T##n>::VT, &dp->rgvarg[dp->cArgs-n]))) return E_INVALIDARG; #define __funcparam(n, ...) v[n-1].*CVarTypeInfo<T##n>::pmField, #define __funcparam_end(n, ...) v[n-1].*CVarTypeInfo<T##n>::pmField #define __funcparam_type(n, ...) T##n, #define __funcparam_type_end(n, ...) T##n #define __method_helper_t(n, ...) / template<class TT, typename rettype, __repeat(n, __tparam, __tparam) rettype (TT::* func)(__repeat(n, __funcparam_type, __funcparam_type_end)) > / class _MethodHelper_##n / { / public: / static HRESULT CallMethod (LPVOID pT, DISPPARAMS* dp, VARIANT* pvarResult) / { / if (pT==NULL) return E_FAIL; / if (dp->cArgs < n) return DISP_E_BADPARAMCOUNT; / CComVariant v[n+1]; /*加1是为了避免 n==0 时的编译错误*/ / __repeat(n, __param_type, __param_type) / CComVariant vRet = (reinterpret_cast<TT*>(pT)->*func)( __repeat(n, __funcparam, __funcparam_end) ); / if (pvarResult && vRet.vt!=VT_EMPTY) vRet.Detach(pvarResult); / return S_OK; / } / }; / /* 返回 VOID 的特化模板类*/ / template<class TT, __repeat(n, __tparam, __tparam) void (TT::* func)(__repeat(n, __funcparam_type, __funcparam_type_end)) > / class _MethodHelper_##n<TT, void, __repeat(n, __funcparam_type, __funcparam_type) func> / { / public: / static HRESULT CallMethod (LPVOID pT, DISPPARAMS* dp, VARIANT* pvarResult) / { / if (pT==NULL) return E_FAIL; / if (dp->cArgs < n) return DISP_E_BADPARAMCOUNT; / CComVariant v[n+1]; / __repeat(n, __param_type, __param_type) / (reinterpret_cast<TT*>(pT)->*func)( __repeat(n, __funcparam, __funcparam_end) ); / return S_OK; / } / }; // 预定义个 16 方法调用工具模板类 __for_each_number(__method_helper_t) #define _method_helper(T, name, type, paramcnt, ...) _MethodHelper_##paramcnt<T,type,__VA_ARGS__,&T::name>::CallMethod ////////////////////////////////////////////////////////////////////////// // 属性GET工具模板类和工具宏 template<class T, typename rettype, rettype T::* member> class _GetHelper { public: static HRESULT CallGet(LPVOID pT, DISPPARAMS* dp, VARIANT* pvarResult) { if (pT==NULL) return E_FAIL; CComVariant vRet = reinterpret_cast<T*>(pT)->*member; if (pvarResult) vRet.Detach(pvarResult); return S_OK; } }; #define _get_helper(T, name, type) _GetHelper<T,type,&T::name>::CallGet ////////////////////////////////////////////////////////////////////////// // 属性PUT工具模板类和工具宏 template<class T, typename rettype, rettype T::* member> class _PutHelper { public: static HRESULT CallPut(LPVOID pT, DISPPARAMS* dp, VARIANT* pvarResult) { if (pT==NULL) return E_FAIL; if (dp->cArgs != 1) return DISP_E_BADPARAMCOUNT; CComVariant v; if (FAILED(v.ChangeType(CVarTypeInfo<rettype>::VT, dp->rgvarg))) return DISP_E_BADVARTYPE; #pragma warning(push) #pragma warning(disable:4800) reinterpret_cast<T*>(pT)->*member = v.*CVarTypeInfo<rettype>::pmField; #pragma warning(pop) return S_OK; } }; #define _put_helper(T, name, type) _PutHelper<T,type,&T::name>::CallPut ////////////////////////////////////////////////////////////////////////// // 映射表工具模板类和映射宏 typedef HRESULT (* fnDispMethod)(LPVOID pT, DISPPARAMS* dp, VARIANT* pVarResult); struct DispMethodData { LPCOLESTR name; // property or method name DISPID dispid; // dispid fnDispMethod pfnGet; fnDispMethod pfnPut; fnDispMethod pfnMethod; }; template<class T> class DispProvider : public IDispatch { private: T* _owner; public: DispProvider() : _owner(NULL) {} void SetOwner(T* owner) { _owner = owner; } /* IDispatch Methods*/ STDMETHOD_(ULONG, AddRef)() { return 2; } STDMETHOD_(ULONG, Release)() { return 1; } STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj) { if (!_owner) return E_UNEXPECTED; if (!ppvObj) return E_POINTER; *ppvObj = NULL; if (IsEqualIID(iid, __uuidof(IUnknown)) || IsEqualIID(iid, __uuidof(IDispatch))) *ppvObj = this; if (*ppvObj) { ((LPUNKNOWN)(*ppvObj))->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) { *pctinfo=0; return E_NOTIMPL; } STDMETHOD(GetTypeInfo)(UINT /*iTInfo*/, LCID /*lcid*/, ITypeInfo **ppTInfo) { *ppTInfo = NULL; return E_NOTIMPL; } STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) { ATLASSERT(cNames == 1); if (cNames != 1) return E_NOTIMPL; if (!_owner) return E_UNEXPECTED; *rgDispId = DISPID_UNKNOWN; const DispMethodData* pMap = T::__GetDispMapEntry(*rgszNames); if (pMap) return *rgDispId = pMap->dispid, S_OK; return DISP_E_MEMBERNOTFOUND; } STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, Word wFlags, DISPPARAMS * pdispparams, VARIANT *pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) { if (!_owner) return E_UNEXPECTED; const DispMethodData* pMap = T::__GetDispMapEntry(NULL, &dispIdMember); if (pMap) { fnDispMethod pfn = (wFlags&DISPATCH_METHOD) ? pMap->pfnMethod : (wFlags==DISPATCH_PROPERTYGET) ? pMap->pfnGet : pMap->pfnPut; if (pfn) return pfn(_owner, pdispparams, pVarResult); } return DISP_E_MEMBERNOTFOUND; } }; #define Begin_Disp_Map(classname) / public: / DispProvider<classname> __disp; / IDispatch* GetDispatch() { return __disp.SetOwner(this), (IDispatch*)&__disp; } / static const DispMethodData* __GetDispMapEntry(LPCOLESTR pszByName=NULL/*find by name*/, DISPID* pByDispid=NULL/*find by dispid*/) / { / typedef classname owner_class; / static const DispMethodData __map_entry[] = { #define Disp_PropertyGet(dispid, name, type) / {OLESTR(#name), dispid, _get_helper(owner_class,name,type), NULL, NULL}, #define Disp_PropertyPut(dispid, name, type) / {OLESTR(#name), dispid, NULL, _put_helper(owner_class,name,type), NULL}, #define Disp_Property(dispid, name, type) / {OLESTR(#name), dispid, _get_helper(owner_class,name,type), _put_helper(owner_class,name,type), NULL}, #define Disp_Method(dispid, name, type, paramcnt, ...) / {OLESTR(#name), dispid, NULL, NULL, _method_helper(owner_class,name,type,paramcnt,__VA_ARGS__)}, #define End_Disp_Map() / {NULL, DISPID_UNKNOWN, NULL, NULL, NULL} / }; / if (pszByName==NULL && pByDispid==NULL) return __map_entry; / for (int i=0; i<sizeof(__map_entry)/sizeof(__map_entry[0]) - 1; i++) / { / if (pByDispid) / { / if (__map_entry[i].dispid == *pByDispid) return &__map_entry[i]; / } / else /*if (pszByName)*/ / { / if (lstrcmpiW(__map_entry[i].name, pszByName) == 0) return &__map_entry[i]; / } / } / return NULL; / } #endif // __MACRO_H__
相关文章推荐
- 让普通C++类轻松支持IDispatch自动化接口
- 转:宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口(二)
- 宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口(二)
- 宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口(二)
- 转载:宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口
- 让普通C++类轻松支持IDispatch自动化接口
- 宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口
- 宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口
- 宏定义的极致发挥---让你的普通C++类轻松支持IDispatch自动化接口
- 8.MFC实现IDispatch自动化接口
- COM学习笔记8_IDispatch (调度接口) 自动化
- 9.ATL实现IDispatch自动化接口
- 让普通C++类中支持连接点(Windows系统)
- 宽字符std::wstring的长度和大小问题?sizeof(std::wstring)是固定的32,说明std::wstring是一个普通的C++类,而且和Delphi不一样,没有负方向,因为那个需要编译器的支持
- 运行类不支持自动化或不支持期望的接口 的解决方法
- COM学习笔记8_IDispatch (调度接口) 自动化
- 430类型不支持自动化或不支持期望的接口错误解决
- VB "类不支持自动化或不支持期望的接口"问题
- COM学习笔记8_IDispatch (调度接口) 自动化
- COM组件里自动化接口与普通接口的转换