gpt4 book ai didi

c++ - 使用 CComVariant 时出现 Outlook 插件内存泄漏

转载 作者:行者123 更新时间:2023-11-28 07:01:52 25 4
gpt4 key购买 nike

我正在分析 Outlook 插件中的句柄泄漏和内存泄漏。

我把我的分析步骤放在这里,因为我几乎没有任何 c++ 知识(我上次使用 c++ 是 7 年前)。

从谷歌搜索后,我使用 WinDbg 的 !htrace -diff 命令找到句柄泄漏。另外,我使用 umdh 来获取内存差异。

通过比较这两个结果,我发现有许多相同的调用堆栈似乎对两者都有贡献:

这是句柄差异信息:

0x77ce55f4: ntdll!NtCreateEvent+0x0000000c
0x75d978a6: KERNELBASE!CreateEventExW+0x0000006e
0x75d97926: KERNELBASE!CreateEventExA+0x00000035
0x75d97965: KERNELBASE!CreateEventA+0x00000027
0x6740738b: olmapi32!HrLogAndRunFnOnThreadEx+0x000002c4
0x67407295: olmapi32!HrLogAndRunFnOnThreadEx+0x000001ce
0x674075cf: olmapi32!HrLogAndRunFnOnThreadEx+0x00000508
0x6740758e: olmapi32!HrLogAndRunFnOnThreadEx+0x000004c7
0x6741cfb2: olmapi32!REFTRACK_WrapObject+0x00000b30
0x6741cf37: olmapi32!REFTRACK_WrapObject+0x00000ab5
0x6741ce55: olmapi32!REFTRACK_WrapObject+0x000009d3
0x67421ed2: olmapi32!HrAsyncWrapObject+0x0000005a

这里是内存差异信息

ntdll!EtwSetMark+23ED
ntdll!RtlInitializeCriticalSectionEx+12B
ntdll!RtlInitializeCriticalSection+12
olmapi32!MSProviderInit+D9
olmapi32!MSProviderInit+1D
olmapi32!REFTRACK_AddRefEx+113
olmapi32!REFTRACK_WrapObject+CDE
olmapi32!REFTRACK_WrapObject+BF1
olmapi32!REFTRACK_WrapObject+AC9
olmapi32!REFTRACK_WrapObject+9D3
olmapi32!HrAsyncWrapObject+5A
olmapi32!FBadEntryList+BF
OUTLOOK!FOutlookIsDeepSyncing+D045
OUTLOOK!FOutlookIsDeepSyncing+CD1D
OUTLOOK!FOutlookIsDeepSyncing+CA93
OUTLOOK!FAllowStoreToSend+1A543
OUTLOOK!GetCurrentDate+9733B
OUTLOOK!HrEnsureIMManager+49BC
OUTLOOK!HrEnsureIMManager+3C75
OUTLOOK!GetMsoInst+2E76
OUTLOOK!GetMsoInst+5BCB
OUTLOOK!GetMsoInst+5C7A
OUTLOOK!HrFindAContact+1893F
DCO!OOM::Items::add+102 (C:\XXX\item.cpp, 67)
....

最后,在item.cpp的第67行,我找到了

Item Items::add( const std::wstring& type ) const
{
IDispatchPtr pIItem = NULL;
HRESULT hr = getImpl()->Add( CComVariant(type.c_str()), &pIItem ); <<< Line 67
if ( FAILED(hr) )
throw Exception( hr, this, L"_Items::Add" );
return Item( pIItem );
}

我在谷歌上搜索了更多,知道 getImpl() 是 SmartPointer 的东西。而 CComVariant 是用来包装对象的东西。我在 CComVariant 上搜索了一些内存泄漏样本,但这似乎不是同一种情况。

是否认为此代码会导致句柄/内存泄漏?或者有什么建议可以让我做更多的研究吗?

提前致谢。

最佳答案

经过 2 周的调查,我想我找到了根本原因,正如 WhozCraig 所怀疑的那样,根本原因是 AddRef() 和 Release() 不匹配。

错误码很简单:

_variant_t vtUnk = dispatch_adapter::get(getImpl(), L"MAPIOBJECT");
CComPtr<IUnknown> pIUnk((IUnknown*)vtUnk);

为了调查这个问题,我查看了一些源代码,第一个是_variant_t

// Extracts a VT_UNKNOWN into an IUnknown*
//
inline _variant_t::operator IUnknown*() const
{
if (V_VT(this) == VT_UNKNOWN) {
if (V_UNKNOWN(this) != NULL) {
V_UNKNOWN(this)->AddRef(); // Here Added Ref
}
return V_UNKNOWN(this);
}

_variant_t varDest;
varDest.ChangeType(VT_UNKNOWN, this);

if (V_UNKNOWN(&varDest) != NULL) {
V_UNKNOWN(&varDest)->AddRef(); // Here Added Ref
}

return V_UNKNOWN(&varDest);
}

然后,如果我们查看 CComPtr 的构造函数,我们会发现:

CComPtr( T* lp) {if((p=lp)!=NULL) p->AddRef();}; // Here Add Ref Again.

两次 AddRef() 和一次 Release(),这就是发生句柄/内存泄漏的原因。

要解决此问题,我们可以使用 CComPtr.Attach() 而不是 CComPtr 的构造函数。

关于c++ - 使用 CComVariant 时出现 Outlook 插件内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22319054/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com