简体   繁体   中英

Where is the memory leak in this C++?

I have been told be a couple of tools that the following code is leaking memory, but we can't for the life of us see where:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant olevar;
        olevar = aRecordset->Fields->GetItem(_bstr_t(strFieldName))->Value;
        if (olevar.vt == VT_BSTR && olevar.vt != VT_EMPTY)
        {
            strFieldValue = olevar.bstrVal;
            hr = true;
        }
        else if ((olevar.vt == VT_NULL || olevar.vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

We assume it is something to do with the olevar variant as the size of the leak matches the size of the string being returned from the recordset.

I have tried olevar.detach() and olevar.clear(), both had no effect, so if this is the cause, how do I release the memory that is presumably allocated in GetItem. And if this is not the cause, what is?

EDIT

I read the article suggested by Ray and also the comments associated with it and then tried:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant* olevar = new COleVariant();
        _bstr_t* fieldName = new _bstr_t(strFieldName);
        *olevar = aRecordset->Fields->GetItem(*fieldName)->Value;
        if (olevar->vt == VT_BSTR && olevar->vt != VT_EMPTY)
        {
            strFieldValue = olevar->bstrVal;
            hr = true;
        }
        else if ((olevar->vt == VT_NULL || olevar->vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
        delete olevar;
        delete fieldName;
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

Main differences being the olevariant and bstr are now explicitly created and destroyed.

This has roughly halved the volume of leak, but there is still something in here that is leaking.

Solution?

Looking at the advice from Ray about using Detach, I came up with this:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant olevar;
        _bstr_t fieldName = strFieldName;
        olevar = aRecordset->Fields->GetItem(fieldName)->Value;

        if (olevar.vt == VT_BSTR && olevar.vt != VT_EMPTY)
        {
            BSTR fieldValue = olevar.Detach().bstrVal;
            strFieldValue = fieldValue;
            ::SysFreeString(fieldValue);
            hr = true;
        }
        else if ((olevar.vt == VT_NULL || olevar.vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
        ::SysFreeString(fieldName);
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

According to the tool (GlowCode) this is no longer leaking, but I am worried about using SysFreeString on fieldValue after it has been assigned to the CString. It appears to run, but I know that is no indication of being memory corruption free!

You have to release memory allocated for BSTR.

See article

Oh, and you have to do a detach before assigning bstr value of VARIANT to CString

strFieldValue = olevar.detach().bstrVal;

and then ensure your CString object gets properly destroyed in time.

This code snippet could leak memory in the exception handler. In other words, this function is not exception safe.

catch(Exception^ error)
{
    hr = E_FAIL;
    MLogger::Write(error);
}

You never clean up olevar or fieldName in the event that an exception is thrown after you call new and before you reach the delete lines.

I recommend that you use some sort of smart pointer ( std::auto_ptr , boost::scoped_ptr ) to automatically release the pointers when you finish using them.

std::auto_ptr<COleVariant> olevar(new COleVariant);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM