描述:
在不用CComPtr的代码,没有错误.
void InvokeTest2()
{
HRESULT hr = NULL;
ISimpleMath* pSimpleMath = NULL;
hr=CoInitialize(NULL);
if(!SUCCEEDED(hr))
{
return ;
}
hr = CoCreateInstance(CLSID_Math,
NULL,
CLSCTX_INPROC_SERVER,
IID_ISimpleMath,
(void**)&pSimpleMath);
if(SUCCEEDED(hr))
{
int val_1 = 4;
int val_2 = 6;
int ret_val = 0;
int* ret_pval = &ret_val;
pSimpleMath->Add(val_1, val_2,ret_pval);
}
::CoUninitialize();
}
引入CComPtr出现异常.
void InvokeTest1()
{
HRESULT hr = NULL;
CComPtr<IUnknown> spUnknown;
CComPtr<ISimpleMath> spSimpleMath;
hr=CoInitialize(NULL);
if(!SUCCEEDED(hr))
{
return ;
}
hr = spUnknown.CoCreateInstance(CLSID_Math);
hr = spUnknown.QueryInterface( &spSimpleMath );
if(SUCCEEDED(hr))
{
int val_1 = 4;
int val_2 = 6;
int ret_val = 0;
int* ret_pval = &ret_val;
spSimpleMath->Add(val_1, val_2,ret_pval);
}
::CoUninitialize();
}
Call Stack 内容为:
~CComPtrBase() throw()
{
if (p)
p->Release(); //弹出异常的地方,
//Unhandled exception:Access violation reading location
}
MyClient.exe!ATL::CComPtrBase<ISimpleMath>::~CComPtrBase<ISimpleMath>()
MyClient.exe!ATL::CComPtr<ISimpleMath>::~CComPtr<ISimpleMath>()
不知道为什么,怎么解决?
请高手指教?
谢谢!!
解决方案1:
呵呵,想来这个问题就不是三言两语能解决的。
佩服ks_gq兄的耐心,我也再多说点吧。
其实从语义及规范使用上来讲,ks_gq兄的说法是正确的,我们不能只拉屎而不擦屁股(只去初始化而不去释放)。
CoInitialize(Ex)的作用分为两个:载入COM所需的DLL和将调用线程纳入指定的套间并初始化相关数据。相应的,CoUninitialize的作用也可以理解为退出套间和卸载COM DLL。
经过调试查看,由于效率的原因,COM DLL一旦被载入,将会一直映射在内存地址空间中,直到进程退出,所以CoUninitialize的卸载作用是不存在的。也就是说,如果忽略掉第一次CoInitialize(Ex)的调用所起的载入COM DLL的作用,这两个API的作用就限制到了线程的层面:将线程纳入套间和退出套间。
而根据windows的一贯作风(在进程、线程退出时自动执行相关的清理工作),我们应该不用担心如果不调用CoUninitialize,线程将不会退出套间。因此不调用CoUninitialize就正如不关闭打开的文件一样,虽然进程(线程)退出时会保证清理动作的执行,但终究不是良好的行为。
因此在此处,不调用CoUninitialize可以说是最快的解决办法,但不是最好的解决办法。
另外导致最后非法内存访问的原因应该是因为线程退出套间时卸载掉了COM对象所在的DLL,因此对象的内存变为无效。
在CoUninitialize()之前调用spUnknown.Release();spSimpleMath.Release();是不会出错的, 因为智能指针是利用C++对象会自动调用析构的原理实现的, 而析构是发生在函数体外, 也就是'}'之后, 而你在函数的末尾调用了CoUninitialize(),也就是在函数结束之前,你已经卸载了COM库, 所以在函数结束之后智能指针在执行自己的析构函数的时候当然会出错(com库已经被卸载), 这就是你程序出错的原因, 而在CoUninitialize之前我们手动释放, 在智能指针自动调用自己析构函数的时候就不会出错了, 呵呵, 去掉CoUninitialize是不对的, COM库没有被卸载~~~~
最好的解决方法是不要在这个函数的头和尾调用CoInitialize(NULL)和CoUninitialize(), 最好在程序的初始化和程序结束的时候调用, 这样就不会出现你所描述的错误了, 也可以更好的利用智能指针, 不用自己去手动释放. 这是我的原意, 你可能理解错了.
您可能想查找下面的文章:
- 为什么用CComPtr<IObj>创建的对象不能Release,而用IObj创建对象指针,就可以Release?
- CComPtr与_com_ptr_t模板
- CComPtr什么时候需要release?
- 程序中用了CComPtr的话,还需要CoInitialize和CoUninitialize吗?
- CComPtr未定义;atlcomhrequiresatlbasehtobeinclud
- CComQIPtr比CComPtr的好处在于哪里?不再需要显示调用QueryInterface了?
- CComPtr指针的构造函数是不是已经包含了CoCreateInstance了?
- _ApplicationPtr和CComPtr的区别
- CComPtr<I>sp能否作为函数参数传递?
- "focus”:不是“ATL::_NoAddRefReleaseOnCComPtr<T>”的成员的原因?