为什么我在使用 CComSafeArray<BSTR> 时会泄漏内存?

Why do I leak memory when using CComSafeArray<BSTR>?

我有一个内存泄漏,我已将其缩小到以下代码。怎么回事?

CComSafeArray<BSTR> sa(5);
sa[0] = SysAllocString("constant");

CComSafeArray::~CComSafeArray calls SafeArrayDestroy (via CComSafeArray::Destroy(), see below), which should then call SysFreeString 所有成员。因此,这里不应该有泄漏。怎么回事?

HRESULT Destroy()
{
    HRESULT hRes = S_OK;
    if (m_psa != NULL)
    {
        hRes = Unlock();
        if (SUCCEEDED(hRes))
        {
            hRes = SafeArrayDestroy(m_psa);
            if (SUCCEEDED(hRes))
                m_psa = NULL;
        }
    }
    return hRes;
}

简答:

不是在 CComSafeArray<BSTR>CComSafeArray<BSTR>::operator[]CComSafeArray<BSTR>::GetAt(T) return 中保存指针的副本,而是一个 CComBSTR 对象,它采用BSTR。这导致 BSTR 泄漏。

长答案:

经过几个小时的无果而终,在尝试编译一个更简单的版本以试图缩小漏洞时出现编译错误,暴露了它:

CComSafeArray<BSTR> sa(5);
sa[0] = nullptr;

这不会编译,因为在幕后 CComSafeArray<BSTR>::operator[] return 是一个 CComBSTR 对象,并且 nullptr 可以匹配 CComBSTR::operator=(LPCSTR)CComBSTR::operator=(LPCOLESTR).砰,编译错误。

一旦我发现 CComBSTR 在幕后参与,它就明白了。 CComBSTR::operator= takes a copy of the BSTR instead of saving the pointer(我如何阅读代码,给定正常行为)或取得所有权,导致未释放的临时文件泄漏 BSTR

typename _ATL_AutomationType<T>::_typewrapper& GetAt(_In_ LONG lIndex) { ... }

...

// specialization for BSTR so GetT doesn't return &BSTR
template <>
struct _ATL_AutomationType<BSTR>
{
    typedef CComBSTR _typewrapper ;
    enum { type = VT_BSTR };
    static void* GetT(_In_ const BSTR& t) throw()
    {
        return t;
    }
};