views:

495

answers:

2

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!

+5  A: 

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.

Ray
You would have though COleVariant's destructor would do that for ya ... Can't really check what it does right now.
Goz
Actually, looking through the MFC source. ~COleVariant calls "VariantClear" and according to the docs on VariantClear<br><br>"If the vtfield is VT_BSTR, the string is freed"
Goz
Is this kosher in Managed C++ anyway ? You are assigning somehting that will be destroyed when olevar goes out of scope to a reference (strFieldValue), in unmanaged C++ this usually means that strFieldValue is only correct by coincidence.
Harald Scheirich
strFieldValue is a CString and I assume that the bstr being assigned to it forces the implicit creation of a CString wrapper. I agree that the bstr is then destroyed (or at least that is what I am trying to get to), but the CString should still be ok?
Colin Desmond
CString will use bstr's 0 terminated buffer of wchars. Thats why you have to do a detach. It ensures that olevar destructor will not call SysFreeString on bstr buffer.
Ray
+2  A: 

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);
Dan
Good point, thank you. I'll look into this too.
Colin Desmond