综合技术

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

微信扫一扫,分享到朋友圈

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

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字符串算的,就标记了字符串结束。如果 需要赋值包含的BSTR字符串需要使用AppendBSTR
,这个内部封装了SysAllocStringByteLen.

CComBSTR bstr4;
bstr4 = OLESTR("Test 4Test 4");

BSTR bt = ::SysAllocStringLen(OLESTR("Test 5Test 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		sa1(10);		//10个long元素,下标0开始
	CComSafeArray	sa2(5,1);		//5个double元素,下标1开始

	CComSafeArrayBound bound3(5,1);
	CComSafeArray	sa3(bound3);	//5个double元素,下标1开始

	CComSafeArrayBound bound40(3);
	CComSafeArrayBound bound41(4);
	CComSafeArrayBound bound4[] = {bound40, bound41};
	CComSafeArray	sa4(bound4, 2);	//2行3列double元素

	/*****1维*****/
	//添加
	CComSafeArray 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 spDog;
		HRESULT hr = spDog.CoCreateInstance(CLSID_CAnimal);
		if (SUCCEEDED(hr))
		{
			spDog->Wangwang();
		}

		CComQIPtr spCat = spDog;
		if (spCat)
		{
			spCat->Miaomiao();

			CComVariant	strWord(L"抱抱");
			CComVariant nAge(10);

			CComPtr spCatDisp(spCat);
			spCatDisp.Invoke2(2, &strWord, &nAge);
		}
	}

	CoUninitialize();
}

可以看到这里spCat赋值时,动态查找了ICat接口。

IDispatch接口可直接调用Invoke1/Invoke2等参数。

需要注意的是:

1.赋值时,会自动AddRef

2.如演示程序,变量周期必须在COM库作用范围内

3.使用->和.调用区别在于,
前者调用内嵌指针,后者调用库封装的逻辑

。一些函数比如Release需要使用.访问以调用对应的管理逻辑。

完整演示代码下载链接

原创,转载请注明来自 http://blog.csdn.net/wenzhou1219

阅读原文...


微信扫一扫,分享到朋友圈

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

CSDN博客

13.浅析COM多线程

上一篇

这是一段关于钛星人信念与夜宵的故事 | 生活方式

下一篇

评论已经被关闭。

插入图片

热门分类

往期推荐

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

长按储存图像,分享给朋友