您的位置:首页 > 其它

14.说说ATL常用包装类的用法和坑

2017-08-13 09:17 363 查看
ATL提供了很多复杂数据类型的包装类,使用这些包装类可以大大减小开发工作量,但是他们使用起来也有许多坑,需要注意,本文就ATL常用包装类的用法和坑详细说明,力图说明产生这些坑的原因和使用注意事项。本文基于VS2008 ATL,这一版修复了一些之前的bug。

先说通用的注意事项,所有的帮助类都类似智能指针特性(没有引用),如下:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息