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!