描述:
*概要
ATL的封装函数IConnectionPointImplMT<T, piid, CDV>::GetInterfaceAt(int nConnectionIndex)在其内部调用IGlobalInterfaceTable::GetInterfaceFromGlobal(DWORD dwCookie, REFIID riid,void **ppv)时发生内存泄漏,原ATL封装函数如下:
template <class T, const IID* piid, class CDV>
LPUNKNOWN IConnectionPointImplMT<T, piid, CDV>::GetInterfaceAt(
int nConnectionIndex)
{
m_CPMTCritSec.Lock();
LPUNKNOWN pUnk = NULL;
// IConnectionPointImplMT Vector stores DWORDs instead of IUnknown pointers,
// explicit cast required:
DWORD dwGITCookie = (DWORD)(m_vec.GetAt(nConnectionIndex));
if (dwGITCookie != NULL)
{
IID iid;
GetConnectionInterface(&iid);
//HRESULT hr = S_OK;
/*!*/ HRESULT hr = m_pGIT->GetInterfaceFromGlobal( // <- 就是这条!!
dwGITCookie, iid, reinterpret_cast<void **>(&pUnk));
ATLASSERT(hr == S_OK);
}
m_CPMTCritSec.Unlock();
return pUnk;
}
*我实在找不到解决方法
我想当然:既然GetInterfaceFromGlobal()申请了资源,必然有一个对应的函数可以释放掉,但我仔细阅读过MSDN,也都仔细试过,三个函数没有一个能实现:IGlobalInterfaceTable::GetInterfaceFromGlobal()
IGlobalInterfaceTable::RegisterInterfaceInGlobal()
IGlobalInterfaceTable::RevokeInterfaceFromGlobal()
*我也全都按照微软网站上的方法做了
把
class CProxy_IEvntFirerEvents : public IConnectionPointImpl<T, &DIID__IEvntFirerEvents, CComDynamicUnkArray>
换成
class CProxy_IEvntFirerEvents : public IConnectionPointImplMT<T, &DIID__IEvntFirerEvents, CComDynamicUnkArray>
把
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
换成
CComPtr<IUnknown> sp;
sp.Attach (GetInterfaceAt(nConnectionIndex));
我也试了其它的释放方法,要么行不通,要么无效。
请各位高手帮我,谢谢!
解决方案1:
作为多进程的GlobalTable如果不对每个线程调用::CoUnInitialize(),就会导致列集信息无法释放,建议不要实用::CoInitializeEx(NULL,COINIT_MULTITHREADED)
解决方案2:m_pGIT->Release()应该和m_pGIT->GetInterfaceFromGlobal匹配,你在什么地方Release的?次数是否一样?
解决方案3: 我估计这个问题是线程调度问题。
不知道你用的是什么管理方式Free,Both?
你可以试一试自己调度
CoMarshalInterThreadInterfaceInStream
CoGetInterfaceAndReleaseStream
那段代码是VC向导产生的,没有问题,不必费心去修改他。关键是这个函数不能从COM对象创建线程外引用。即不能在工作线程中调用这个函数,否则会引起客户端崩溃。MS中有关于COM对象内部多线程引发客户端事件的方法,记不清位置了。如有需要可以联系。
解决方案5: m_pGIT->GetInterfaceFromGlobal(dwGITCookie, iid, reinterpret_cast<void **>&pUnk))没有引起内存泄漏。
你是不是用了这样
[CComQIPtr<T>] = [IConnectionPointImplMT<T, piid, CDV>::GetInterfaceAt(int nConnectionIndex)]
这样的方式赋值?这才是内存泄漏的根源。从函数的实现来说,GetInterfaceAt(int nConnectionIndex)返回的指针就应该存在一个引用,因为调用者必须保证这个对象在函数外围的一切调用都在它的生存周期内,所以最终得由调用者释放这个指针。
但是这里,只能指针CComQIPtr<>或者类似的智能指针再给它加了一个引用,这才是问题的根本所在。你改用CComPtr<IYouinterface> sp; sp.p = ...GetInterfaceAt()就可以解决这个问题。
另外,这样的函数不是很好的设计,一般用作自己内部用,如果作为公共模块暴露给其它调用者,一般不会这样,除了上面说的原因外,还要防止可以调用这个函数而没有使用返回值,这样跟上面的理由一般,也是一个内存泄漏。因此,如果作为公共模块的接口,一般会用参数IYouinterface** pp代替。API,CRT函数也是类似的,例如,除了HeapAlloc(),GlobalAlloc(),LocalAlloc(),malloc(), operator new()等少数几个分配内存的函数外,一般都不会在函数体内把一部分内存所有权放弃,然后作为返回值交给客户。
CString::operator LPCTSTR()等函数虽然也返回指针,但是,它对字符串的内存并不会把所有权交给它的调用者,而是始终由CString类保持着引用,并总是由CString释放。也跟上面类似的道理。
ATL里哪里来的IConnectionPointImplMT?我怎么从来没看到过?是你自己定义的吧?
不过你的问题很简单,主要是这个函数定义的有问题:
LPUNKNOWN IConnectionPointImplMT<T, piid, CDV>::GetInterfaceAt( int nConnectionIndex);
只要你直接调用GetInterfaceAt(nConnectionIndex);这样就内存泄露了,因为返回的临时对象不会被自动析构。
除非像这样写,手工删除返回的对象:
LPUNKNOWN p = GetInterfaceAt(nConnectionIndex);
p->Release();
所以返回COM对象一定要用智能指针,否则很容易造成内存泄露。