COM Klasse (attributed C++), Events, injected Code
-
Ich hab' hier nen COM Server der mit MSVC8 (SP1) und attributed C++ implementiert ist. Da drinnen gibts dann Klassen die eben Events schicken können, so mit __event - geht ja alles erstmal schön. Sämtliche Klassen sind mit threading("free") markiert.
Und nu sind wir beim Fehler Suchen über was drübergestolpert... nämlich die Implementierung der injizierten Advise/Unadvise Funktionen sowie der "schicke Event X raus" Funktionen. Und diese sehen für meine Augen ziemlich fischig aus.Schematisch sieht das so aus:
HRESULT MyClass::EventBlubb() { // ... Lock(); IUnknown** pp = this->m_vec.begin(); Unlock(); for(; pp != this->m_vec.end(); pp++) { __WeissNimmerWasGenau(*pp, ...); } // ... }Und ich denke dieser Code ist oberfaul.
In der Funktion wird weder eine lokale Kopie der Daten in m_vec gemacht, noch wird der Lock lange genug aufrecht erhalten (was dann aus anderen Gründen wieder problematisch wäre) -- ruft jmd. in der Zeit wo EventBlubb() läuft also Advise oder Unadvise auf, dann krachts u.U.
(Advise bzw. Unadvise können zum Beispiel dazu führen dass der von m_vec.begin() bzw. m_vec.end() zurückgelieferte Pointer sich ändert, bzw. auch einfach der Inhalt des Vectors).
Und BTW: m_vec ist KEIN std::vector sondern irgendsoein ATL Zeugs, ist aber in dem Fall ziemlich egal, m_vec kümmert sich auch NICHT um irgendwelche Synchronisierungsgeschichten. Könnte es auch garnicht, nicht wenn der Code aussen so aussieht wie er nunmal aussieht.Ist jmd. von euch schonmal über dieses Problem drübergestolpert? Falls es jmd. interessiert der es noch nicht weiss: den injizierten Code kann man sich z.B. ansehen indem man in den Project-Settings unter "Output Files" die Option /Fx einstellt ("Expand Attributed Source: yes"). Oder indem man mit dem Debugger einfach reinsteppt...
Und wichtiger: weiss jmd. eine Lösung?
Speziell auch für folgenden Fall:- Client ruft Advise() auf
- COM Server fängt an einen Event an alle registrierten Clients zu schicken, holt sich grad den Zeiger auf unseren Client und ...
- Client ruft Unadvise auf (und Unadvise gibt die Sink-Referenz frei!)
- Client zerstört seine Sink ("der Sink"/"die Sink" ... ? egal.)
- COM Server läuft weiter wo er bei 2) stehengeblieben ist, und schickt den Event an den alten Interface Zeiger aus -- die Referenz wurde aber bereits hergegeben, und das Objekt ist schon tot. Pöse

Klar könnte ich die Implementierung von Advise(), Unadvise() und EventXYZ() selbst schreiben so dass das passt, aber irgendwo hatte ich ja doch gehofft dass dieses "attributed C++ COM Zeugs" mir genau sowas abnehmen würde...

p.S.: MSVC7.1 (SP1) generiert bis auf ein kleines (und in dem Fall unwichtiges) Detail genau den selben Code.
-
Bist Du sicher das dieser Code injeziert wird:
IUnknown** pp = this->m_vec.begin();Und kein CComPtr? Würde mich wundern.
Hast Du ein sample? Muss nicht Multithreaded sen?
-
OT: Ist Attributed ATL nicht "deprecated"?
-
Jupp! Ist es!
-
Ich war zu schnell. Also für VS2005 gilt das nicht. IMHO wird es keine Fortführung in Orcas geben. Aber wie weit die das zuammenstreichen.
Aber offizielles habe ich gerade nicht gefunden.Atributed ATL war zum großen Teil zwischen VS2003 und VS2005 nicht komaptibel und es gibt mit dem Zeug Scherereien ohne Ende.
Ich ärgere mich heute noch, dass ich einen Teil der DB Features verwendet habe.
-
Was meint ihr attributed ATL ist tot/deprecated/blah *fürcht*? Heisst das ich kann mir meinen ganzen attributed-COM-Zeugs Code in die Haare schmieren wenn ich z.B. auf's neue Studio umsteigen will? Hmpf. Was gibts denn für Alternativen? Ich meine ich würde es schon hinbekommen das alles "zu Fuss" mit ATL auszuprogrammieren, aber es ist halt schon etwas lästig, gelinde gesagt. Der ganze doofe Glue-Code...
Oder gibt's vielleicht eine einfachere Möglichkeit als ATL? Irgendwas was mir hilft out-of-process COM Server (bzw. evtl. auch mal in-process) mit schönen dualen Interfaces und Events etc. in C++ zu programmieren...
Managed C++ möchte ich eigentlich nicht verwenden, und C++ sollte es auf jeden Fall bleiben (aus verschiedenen Gründen).
----
Hier das gewünschte Beispiel, das ist der Code den MSVC8 (SP1) erzeugt damit ich meine Events verschicken kann:
// in MyClass.h (bzw. MyClass.mrg.h) innerhalb von MyClass: HRESULT MyEvent(::LONG i1,::LONG i2,::LONG i3,::LONG i4) { HRESULT hrRet = S_OK; HRESULT hr = S_OK; IConnectionPointImpl<MyClass, &__uuidof(MyNamespace::DMyDualInterface), CComDynamicUnkArray>* p = this; VARIANT rgvars[4]; Lock(); IUnknown** pp = NULL; __try { pp = p->m_vec.begin(); } __finally { Unlock(); } while (pp < p->m_vec.end()) { if (*pp != NULL) { IDispatch* pDispatch = (IDispatch*) *pp; ::VariantInit(&rgvars[3]); rgvars[3].vt = VT_I4; V_I4(&rgvars[3])= i1; ::VariantInit(&rgvars[2]); rgvars[2].vt = VT_I4; V_I4(&rgvars[2])= i2; ::VariantInit(&rgvars[1]); rgvars[1].vt = VT_I4; V_I4(&rgvars[1])= i3; ::VariantInit(&rgvars[0]); rgvars[0].vt = VT_I4; V_I4(&rgvars[0])= i4; DISPPARAMS disp = { rgvars, NULL, 4, 0 }; VARIANT ret_val; hr = __ComInvokeEventHandler(pDispatch, 151, 1, &disp, &ret_val); if (FAILED(hr)) { hrRet = hr; } } pp++; } return hrRet; }m_vec ist dabei vom Typ CComDynamicUnkArray, und die Advise und Unadvise Funktionen können wie gesagt dazu führen dass die Pointer die .begin() bzw. .end() zurückliefern sich ändern. Der Zugriff auf m_vec innerhalb der Schleife (ohne Lock) ist IMHO also komplett falsch und ziemlich riskant.
MSVC 7.1 lässt das __try und __finally weg, ansonsten ist der Code gleich was ich gesehen habe. Ich hab' zwar kein diff drüber gemacht, aber die Teile um die es mir geht sind gleich.
-
Hülfe

Wäre froh wenn mir jmd. nen Tip geben könnte bezüglich "wie macht man am einfachsten COM Server mit C++"...
EDIT: also anders gesagt: ist die ATL da wirklich das beste was man bekommen kann, oder gibts da noch andere gute Optionen? Ich kenne halt sonst nix...