2013-03-14 13 views
8

Wenn ich COM verwende, verwende ich normalerweise ATL-Smartpointer wie ATL::CComPtr und ATL::CComBSTR für die Ressourcenverwaltung. Aber einige der Methoden, die ich anrufe, verwenden Ausgabeparameter, um Zeiger auf zugewiesenen Speicher zurückzugeben, den ich freigeben muss. Zum Beispiel:Ausnahmesicheres Speicherhandling mit COM

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    DoSomething(pszName); 
    CoTaskMemFree(pszName); 
} 

anzumerken, dass GetDisplayName Speicher für die Zeichenfolge zuordnet und gibt einen Zeiger auf ihn durch den Ausgangsparameter. Es liegt in der Verantwortung des Anrufers, diesen Speicher mit CoTaskMemFree freizugeben.

Wenn DoSomething eine Ausnahme auslöst, wird der obige Code undicht. Ich würde gerne eine Art von intelligenten Zeiger für pszName verwenden, um solche Lecks zu vermeiden, aber die API braucht WCHAR**, so dass ich nicht sehe, wie ich alles außer der Adresse eines dummen Zeigers übergeben kann. Da ich nicht die Zuteilung bin, kann ich RAII nicht verwenden.

I kann Verwendung RRID wenn ich ein deleter wie diese machen:

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    std::unique_ptr<WCHAR, CoTaskMemDeleter> guard(pszName); 
    DoSomething(pszName); 
} 

das funktioniert:

struct CoTaskMemDeleter { 
    void operator()(void *p) { ::CoTaskMemFree(p); } 
}; 

Und dann sofort den zurückgegebenen Zeiger auf einen Standard-Smart-Pointer wie folgt zuweisen , aber es scheint fehleranfällig zu sein, eine zusätzliche Schutzvariable einzuführen. Zum Beispiel lässt dieser Ansatz pszName auf den freigegebenen Speicher zeigen, so dass es leicht wäre, ihn versehentlich erneut zu verwenden.

Gibt es eine sauberere Möglichkeit, einen Smart-Pointer oder einen RAII-Wrapper für den vom COM-Server zugewiesenen Speicher zu verwenden, der vom Ausgabeparameter zurückgegeben wird? Fehle ich etwas, das ATL bietet?

+0

Was ist so schlimm daran, die Erinnerung selbst zu befreien? Für mich scheint es sich nicht zu lohnen, etwas anderes zu tun. – evanmcdonnal

+0

@evanmcdonnal: Ausnahmesicherheit. –

Antwort

11

ATL bereits hat diese eine aus der Box:

CComHeapPtr<WCHAR> pszName; 
const HRESULT nResult = pShellItem->GetDisplayName(..., &pszName); 
// Hooray, pszName will be CoTaskMemFree'd for you on scope leave 
// via ~CComHeapPtr 

CHeapPtr kann abgeleitet werden, um andere Ressource Releaser in ähnlicher Weise zu implementieren. CComHeapPtr ist ein MSDN documented class.

+1

Ich sah CComHeapPtr in der ATL-Dokumentation, aber ich erkannte nicht, dass 'CoTaskMemAlloc' /' Free' 'CComAllocator' verwendet. Meine nächste Reaktion war: "Wie kompiliert sich das?", Da "& pszName" eher "CComHeapPtr *" als "WCHAR **" zu sein scheint. Dann entdeckte ich, dass die ATL-Pointer-Klassen den unären "Operator &" überladen, was ich für verboten hielt, aber eigentlich nur dringend davon abriet. Diese Überladung verhindert auch, dass Sie diese in einem STL-Container aufbewahren. –

+0

Die Smart-Template-Klasse ähnelt dem bekannten "CComPtr", indem Zeiger umschlossen und ein Verweis '*' aus dem Template-Argument genommen wird: 'IUnkown *' wird von 'CComPtr ' umschlossen, und 'WCHAR *' wird von 'CHeapPtr umschlossen '. Ja beide überladen '&' insbesondere, um Assertionsfehler auszulösen, sobald Sie versuchen, Nicht-NULL-Zeiger als Platzhalter für die Annahme eines neuen Rohwerts verfügbar zu machen. –

+1

Nur aus Neugier: Wer entmutigt Schreibmaschine &? – StuartRedmann