14.说说ATL常用包装类的用法和坑
2017-08-13 09:17
363 查看
ATL提供了很多复杂数据类型的包装类,使用这些包装类可以大大减小开发工作量,但是他们使用起来也有许多坑,需要注意,本文就ATL常用包装类的用法和坑详细说明,力图说明产生这些坑的原因和使用注意事项。本文基于VS2008 ATL,这一版修复了一些之前的bug。
先说通用的注意事项,所有的帮助类都类似智能指针特性(没有引用),如下:
1.析构时释放资源
2.赋值时,先释放原来的内容,再分配创建新的内容
这要求使用时必须注意:
1.在CoInitialize和CoUninitialize内使用接口指针
2.变量的生存周期,特别是栈上的变量
1.NULL是合法字符串,BSTR可能只包含NULL也可能中间包含NULL
2.BSTR分配和释放使用对应的Sys...函数,否则很容易出现内存泄漏
常用构造如下:
如下赋值,bstr4内容最终为Test4,因为默认=赋值是按照LPWSTR字符串算的,\0就标记了字符串结束。如果需要赋值包含\0的BSTR字符串需要使用AppendBSTR,这个内部封装了SysAllocStringByteLen.
使用&取CcomBSTR地址时,返回的是BSTR字符串,如果此时直接解引用操作返回的是BSTR内容,=会导致直接覆盖原有的BSTR,导致内存泄露。
唯一需要注意的是,CComVariant不能使用未初始化的VARIANT初始化(使用_ATL_NO_VARIANT_THROW宏检测),没有初始化的CComVariant也不能Detach。
CComSafeArray主要是为了简化SAFEARRAY的操作,常用操作如下:
需要注意的是:
1.CComSafeArray使用模板和宏技术结合,将输入类型int等等映射为实际COM V_I4/V_I8。对于VARIANT/BSTR/IDispatch和IUnknown采用特化技术,这些类型Get出来后会自动转成CComVariant/CComBSTR/CComPtr,Add/SetAt时通过指明bCopy参数来决定是使用拷贝还是托管现有。
2.CComSafeArray 创建后会调用Lock,对应GlobalLock函数,直到销毁调用UnLock,对应GlobalUnlock,这样做是假设用户一直需要操作SafeArray数据。
3.Attach和拷贝构造会校验元素类型,最好不要直接通过GetSafeArrayPtr操作指针。
CComPtr和CComQIPtr大大简化了这一流程。通过内建扩展结合_uuidof,利用模板类是实现了加速编写。后者相对前者,可以在赋值时动态查找接口。
另外使用IDispatch接口时,Invoke时传参非常麻烦,这些这两个类中都做了封装。
演示如下:
可以看到这里spCat赋值时,动态查找了ICat接口。
IDispatch接口可直接调用Invoke1/Invoke2等参数。
需要注意的是:
1.赋值时,会自动AddRef
2.如演示程序,变量周期必须在COM库作用范围内
3.使用->和.调用区别在于,前者调用内嵌指针,后者调用库封装的逻辑。一些函数比如Release需要使用.访问以调用对应的管理逻辑。
详细内容可参考《ATL深入解析》
完整演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
先说通用的注意事项,所有的帮助类都类似智能指针特性(没有引用),如下:
1.析构时释放资源
2.赋值时,先释放原来的内容,再分配创建新的内容
这要求使用时必须注意:
1.在CoInitialize和CoUninitialize内使用接口指针
2.变量的生存周期,特别是栈上的变量
1.CComBSTR
BSTR使用需要注意两点:1.NULL是合法字符串,BSTR可能只包含NULL也可能中间包含NULL
2.BSTR分配和释放使用对应的Sys...函数,否则很容易出现内存泄漏
常用构造如下:
CComBSTR bstr1(OLESTR("Test1")); CComBSTR bstr2(64, (LPCOLESTR)NULL); //支持NULL空字符 CComBSTR bstr3(GUID_TEST); //支持GUID支持长度为64内容为NULL的字符串,支持GUID直接转成字符串。
如下赋值,bstr4内容最终为Test4,因为默认=赋值是按照LPWSTR字符串算的,\0就标记了字符串结束。如果需要赋值包含\0的BSTR字符串需要使用AppendBSTR,这个内部封装了SysAllocStringByteLen.
CComBSTR bstr4; bstr4 = OLESTR("Test 4\0Test 4"); BSTR bt = ::SysAllocStringLen(OLESTR("Test 5\0Test 6"), 13); CComBSTR bstr5; bstr5.AssignBSTR(bt); //包含空字符时的赋值
使用&取CcomBSTR地址时,返回的是BSTR字符串,如果此时直接解引用操作返回的是BSTR内容,=会导致直接覆盖原有的BSTR,导致内存泄露。
void GetBSTR1(BSTR* pBstr) { CComBSTR bstr(OLESTR("Test GetBSTR1")); *pBstr = bstr.Detach(); } void GetBSTR2(BSTR* pBstr) { ::SysReAllocString(pBstr, OLESTR("Test GetBSTR2")); } CComBSTR bstr6 = OLESTR("Test 7"); GetBSTR1(&bstr6); GetBSTR2(&bstr6);可以看到,GetBSTR1中直接对BSTR赋值了,导致了内存泄漏,此时可开启ATL的ATL_CCOMBSTR_ADDRESS_OF_ASSERT宏,该宏判断如果取&时,CComBSTR不为空则报ASSERT警告。也可以参考GetBSTR2中,可以使用函数SysReallocString来处理这种情况。
2.CComVariant
Variant使用时需要VariantInit,然后指定类型和数据,使用完需要VariantClear清除资源。CcomVariant为我们能封装了整个过程,使用时直接赋值就行,模板实例化过程中会自动的找到对应类型,使用完自动释放,如下:CComVariant v1 = 10; CComVariant v2 = 10.2; CComVariant v3 = OLESTR("test"); //默认转成BSTR
唯一需要注意的是,CComVariant不能使用未初始化的VARIANT初始化(使用_ATL_NO_VARIANT_THROW宏检测),没有初始化的CComVariant也不能Detach。
3.CComSafeArray
需要引入头文件atlsafe.h。CComSafeArray主要是为了简化SAFEARRAY的操作,常用操作如下:
void TestSafeArray() { //构造 CComSafeArray<long> sa1(10); //10个long元素,下标0开始 CComSafeArray<double> sa2(5,1); //5个double元素,下标1开始 CComSafeArrayBound bound3(5,1); CComSafeArray<double> sa3(bound3); //5个double元素,下标1开始 CComSafeArrayBound bound40(3); CComSafeArrayBound bound41(4); CComSafeArrayBound bound4[] = {bound40, bound41}; CComSafeArray<double> sa4(bound4, 2); //2行3列double元素 /*****1维*****/ //添加 CComSafeArray<int> sa5; sa5.Add(7); int arr[] = {8, 9}; sa5.Add(2, arr); sa5.Add(sa5); //访问 for (int i=0; i<sa5.GetCount(); i++) { wcout << sa5.GetAt(i) << endl; } //设置 sa5.SetAt(0,77); for (int i=0; i<sa5.GetCount(); i++) { wcout << sa5.GetAt(i) << endl; } /*****多维*****/ LONG index01[] = {1, 0}; LONG index11[] = {1, 1}; LONG index23[] = {3, 2}; sa4.MultiDimSetAt(index01, 10.0); sa4.MultiDimSetAt(index11, 6.8); sa4.MultiDimSetAt(index23, 3.2); wcout << sa4.GetCount(0) << sa4.GetCount(1) << endl; for (int i=0; i<sa4.GetCount(0); i++) { for (int j=0; j<sa4.GetCount(1); j++) { LONG index[2] = {0}; index[0] = j; index[1] = i; double dbVal = 0; if (SUCCEEDED(sa4.MultiDimGetAt(index, dbVal))); { wcout << "(" << i << "," << j << ")=" << dbVal << endl; } } } }
需要注意的是:
1.CComSafeArray使用模板和宏技术结合,将输入类型int等等映射为实际COM V_I4/V_I8。对于VARIANT/BSTR/IDispatch和IUnknown采用特化技术,这些类型Get出来后会自动转成CComVariant/CComBSTR/CComPtr,Add/SetAt时通过指明bCopy参数来决定是使用拷贝还是托管现有。
2.CComSafeArray 创建后会调用Lock,对应GlobalLock函数,直到销毁调用UnLock,对应GlobalUnlock,这样做是假设用户一直需要操作SafeArray数据。
3.Attach和拷贝构造会校验元素类型,最好不要直接通过GetSafeArrayPtr操作指针。
4.CComPtr和CComQIPtr
原生COM创建接口时需要使用CoCreateInstance,传入一大堆参数,调用麻烦。CComPtr和CComQIPtr大大简化了这一流程。通过内建扩展结合_uuidof,利用模板类是实现了加速编写。后者相对前者,可以在赋值时动态查找接口。
另外使用IDispatch接口时,Invoke时传参非常麻烦,这些这两个类中都做了封装。
演示如下:
void TestComPtr() { CoInitialize(NULL); { CComPtr<IDog> spDog; HRESULT hr = spDog.CoCreateInstance(CLSID_CAnimal); if (SUCCEEDED(hr)) { spDog->Wangwang(); } CComQIPtr<ICat> spCat = spDog; if (spCat) { spCat->Miaomiao(); CComVariant strWord(L"抱抱"); CComVariant nAge(10); CComPtr<IDispatch> spCatDisp(spCat); spCatDisp.Invoke2(2, &strWord, &nAge); } } CoUninitialize(); }
可以看到这里spCat赋值时,动态查找了ICat接口。
IDispatch接口可直接调用Invoke1/Invoke2等参数。
需要注意的是:
1.赋值时,会自动AddRef
2.如演示程序,变量周期必须在COM库作用范围内
3.使用->和.调用区别在于,前者调用内嵌指针,后者调用库封装的逻辑。一些函数比如Release需要使用.访问以调用对应的管理逻辑。
详细内容可参考《ATL深入解析》
完整演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
相关文章推荐
- Android 常用Adapter(ArrayAdapter ,SimpleAdapter ,BaseAdapter )的具体用法
- linux查询命令用法的一些常用命令
- Maven介绍,包括作用、核心概念、用法、常用命令、扩展及配置
- linux常用命令用法整理
- Java基础(接口,抽象类,包装类的基础用法)
- 工作总结之REPRO——REPRO常用用法
- Properties类一些常用的用法
- Nesting常用的6种用法以及举例
- Maven介绍,包括作用、核心概念、用法、常用命令、扩展及配置(转)
- tomcat连接常用数据库的用法
- IOS控件学习:UILabel常用属性与用法
- C#中泛型List的定义与用法以及常用函数
- android uri常用用法
- c++中的string常用函数用法总结
- FutureTask的用法及两种常用的使用场景
- 比较常用的三角及border用法
- SQL 常用关键字释义和用法
- JS在用户离开页面时提示信息&常用的弹出窗口用法
- maven用途、核心概念、用法、常用参数和命令、扩展
- MySql与SqlServer的一些常用用法的差别