2009-11-22 9 views
11

Ich habe eine COM-Funktion, die ein SafeArray über einen LPSAFEARRAY* Out-Parameter zurückgeben sollte. Die Funktion erstellt SafeArray mit der ATL-Vorlagenklasse CComSafeArray. Meine naive Implementierung verwendet CComSafeArray<T>::Detach(), um das Eigentum von den lokalen Variablen auf den Ausgabeparameter zu bewegen:Wie gibt man ein lokales CComSafeArray an einen LPSAFEARRAY-Ausgabeparameter zurück?

void foo(LPSAFEARRAY* psa) 
{ 
    CComSafeArray<VARIANT> ret; 
    ret.Add(CComVariant(42)); 
    *psa = ret.Detach(); 
} 

int main() 
{ 
    CComSafeArray<VARIANT> sa; 
    foo(sa.GetSafeArrayPtr()); 

    std::cout << sa[0].lVal << std::endl; 
} 

Das Problem ist, dass CComSafeArray::Detach() eine Unlock Operation durchführt, so dass, wenn der neue Besitzer des Safearray (Haupt der sa in diesem Fall) ist zerstört das Schloss ist nicht Null und Destroy schlägt das SafeArray mit E_UNEXPECTED nicht frei (dies führt zu einem Speicherverlust, da das SafeArray nicht freigegeben ist).

Wie kann die Eigentumsübertragung zwischen CComSafeArrays über eine COM-Methodengrenze korrekt übertragen werden?


Edit: Von der einzigen Antwort so weit scheint es, dass der Fehler auf der Client-Seite ist (main) und nicht von der Serverseite (foo), aber ich finde es schwer zu glauben, dass CComSafeArray wasn Für diesen trivialen Anwendungsfall muss es einen eleganten Weg geben, ein SafeArray aus einer COM-Methode in eine CComSafeArray zu bekommen.

+0

Welche Version von Visual Studio verwenden Sie? –

+0

Dies passiert sowohl für VS8 (2005) als auch für VS9 (2008). – Motti

+2

Basierend auf meiner Erfahrung glaube ich, wer auch immer CComSafeArray entworfen hat, hat es nie wirklich benutzt. Sie können Ihre eigene Wrapperklasse verwenden, wenn Sie möchten. – Amnon

Antwort

10

Das Problem ist, dass Sie den internen Zeiger des Empfängers CComSafeArray direkt einstellen. Verwenden Sie die Attach() Methode eine bestehende SAFEARRAY zu einem CComSafeArray anbringt:

LPSAFEARRAY ar; 
foo(&ar); 
CComSafeArray<VARIANT> sa; 
sa.Attach(ar); 
+0

Sicherlich ist das nicht die Art, wie 'CComSafeArray' verwendet werden soll, es geht gegen den Kern von' CComVariant' und 'CComBSTR'. – Motti

+0

Wie Sie im Code gesehen haben, erwartet der CComSafeArray, dass der SAFEARRAY gesperrt wird. Sie müssen es irgendwie sperren. – Amnon

+0

Und es gibt keine Attach-ähnliche Funktionalität, die sperrt und auch keine Detach-ähnliche Funktion, die nicht entsperrt - so muss die Arbeit entweder auf der Anrufer-oder der Callees-Seite getan werden. –

1

ich, dass erraten würde, wo es keine Absicht, eine solche Anwendungsfall zu ermöglichen. Wahrscheinlich war es nicht der gleiche Entwickler, die CComVariant & CComPtr :)

schrieb ich glaube, dass CComSafeArray ‚s Autor betrachten Wert Semantik als Hauptziel; Attach/Detach könnte einfach eine "Bonus" -Funktion sein.

+1

Und selbst mit diesem Grund habe ich immer noch das Gefühl, dass 'CComSafeArray's Standard-ctor und' GetSafeArrayPtr' Designfehler/Workarounds sind ... – Andrey

5

Nur um zu bestätigen, dass die markierte Antwort die richtige ist. RAII-Wrapper können nicht über COM-Grenzen hinweg arbeiten.

Die Methode der geposteten Methode ist nicht korrekt, Sie können nicht davon ausgehen, dass der Aufrufer einen gültigen SAFEARRAY liefert. Nur [out] ist kein gültiges Attribut in der Automatisierung, es muss entweder [out, retval] oder [in, out] sein. Wenn es [out, retval] ist, wie es aussieht, muss die Methode ein neues Array von Grund auf erstellen. Wenn es [in, out] ist, muss die Methode das übergebene Array zerstören, wenn es nicht mit dem erwarteten Array-Typ übereinstimmt, und einen neuen erstellen.