2009-08-06 7 views
4

On m'a dit être quelques outils que le code suivant est une fuite de mémoire, mais nous ne pouvons pas pour la vie de nous voir où:Où est la fuite de mémoire dans ce C++?

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; 
} 

Nous partons du principe qu'il est quelque chose à voir avec la variante olevar car la taille de la fuite correspond à la taille de la chaîne renvoyée par le jeu d'enregistrements.

J'ai essayé olevar.detach() et olevar.clear(), les deux n'ont eu aucun effet, donc si c'est la cause, comment je libère la mémoire qui est vraisemblablement allouée dans GetItem. Et si ce n'est pas la cause, qu'est-ce que c'est?

EDIT

Je lis l'article proposé par Ray et aussi les commentaires qui y sont associés, puis essayé:

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; 
} 

différences principales étant le OleVariant et bstr sont désormais explicitement créés et détruits.

Cela a réduit de moitié le volume de fuite, mais il y a encore quelque chose qui fuit ici.

Solution?

regardant les conseils de Ray sur l'utilisation Détacher, je suis venu avec ceci:

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; 
} 

Selon l'outil (GlowCode) ce n'est plus une fuite, mais je suis inquiet sur l'utilisation SysFreeString sur fieldValue après avoir été affecté au CString. Il semble fonctionner, mais je sais que ce n'est pas une indication d'être sans corruption de la mémoire!

+0

votre publication m'a fait penser aux valeurs BSTR. Je n'ai pas eu ce problème, mais j'ai un morceau de code qui a entraîné une fuite de mémoire. Il a fallu un peu de temps pour le réduire à cette ligne de code. Désolé pour le problème de formatage. 'void GetValue (COleVariant & oVar) { . . . //oVar.Clear(); - fuite de mémoire à cause de la ligne suivante sans cet appel si bstr a été alloué pour cette variante !! oVar.Vt = VT_I4; oVar.lVal = 100; } ' – Patel

Répondre

6

Vous devez libérer de la mémoire allouée à BSTR.

Voir article

Oh, et vous devez faire Détacher avant d'affecter la valeur bstr de VARIANT à CString

strFieldValue = olevar.detach().bstrVal; 

puis vous assurer que votre objet CString correctement se détruit dans le temps.

+0

Vous auriez bien que le destructeur de COleVariant le fasse pour vous ... Je ne peux pas vraiment vérifier ce qu'il fait maintenant. – Goz

+0

En fait, en regardant à travers la source MFC. ~ COleVariant appelle "VariantClear" et selon docs sur VariantClear

"Si le champ vtfield est VT_BSTR, la chaîne est libérée" – Goz

+0

Est-ce kasher dans C++ managé quand même? Vous affectez un hashting qui sera détruit quand olevar sort de la portée d'une référence (strFieldValue), en C++ non géré cela signifie généralement que strFieldValue n'est correct que par coïncidence. –

2

Cet extrait de code peut présenter une fuite de mémoire dans le gestionnaire d'exceptions. En d'autres termes, cette fonction n'est pas une exception sûre.

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

Vous nettoyez jamais jusqu'à olevar ou fieldName dans le cas où une exception est levée après que vous appelez new et avant vous atteignez les delete lignes.

Je vous recommande d'utiliser une sorte de pointeur intelligent (std::auto_ptr, boost::scoped_ptr) pour libérer automatiquement les pointeurs lorsque vous avez fini de les utiliser.

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

Bon point, merci. Je vais regarder dans cela aussi. –