Eigentlich müsste dieser Artikel eine weitere Überschrift bekommen:
Wie fatal es ist, dass es keine vollständige ATL Dokumentation gibt!
Einige werden CComDispatchDriver kennen. Seit den VS-200x Versionen ist diese Klasse nichts anderes als sie Spezialisierung CComPtr<IDispatch>
Ich hatte vor, eine bestimmte .NET Komponente per late binding in ein C++ Programm zu integrieren.
Kein Problem. CreateInstance, hier ein Invoke0, dort ein Invoke2 und da noch ein InvokeN 😮 … und hier geht auf einmal nichts mehr. Dokumentation in der MSDN: Fehlanzeige!
Bestandsaufnahme: Ich habe einen CComVariant Array aufgebaut und die entsprechenden Daten übergeben. Sieht alles richtig aus. Aber ich erhalte immer nur Fehler (immer E_NOINTERFACE).
- OK, nächster Schritt. Das ganze mal direkt mit dem entsprechenden Interface versuchen. Also CComQIPtr eingebaut und direkter Aufruf über die existierende duale Schnittstelle. Komisch, nun geht es.
- Nun denn. Das ganze nun mal als Klasse mit dem Classview importiert und einen MFC Wrapper gebaut. Der verwendet ja auch IDispatch. Jetzt wird es mysteriös: Es geht auch!
Dann debuggen wir mal den MFC Wrapper. Schrittweise gehe ich durch den Code im COleDispatchDriver::InvokeHelperV bis Zeile 223 und dort lese ich den folgenden Code und Kommentar:
pArg += dispparams.cArgs - 1; // params go in opposite order
❗ Aha! Hier liegt der Hase im Pfeffer. Ich hatte den CComVariant Array natürlich so aufgebaut, dass das erste Argument auch das erste Element im Array war. InvokeN will aber die selbe Reihenfolge wie IDispatch::Invoke, d.h. in umgekehrter Folge. Also beginnt der Array nun mit dem letzten Argument und endet mit dem ersten und siehe da: Es geht ❗
Schade, dass hier die ATL-Doku so lückenhaft ist. Das hätte mir hier, 2 Stunden Arbeit gespart.
Ich hätte es auch schneller haben können, wenn ich mir aufmerksam die Implementierung von CComPtr<IDispatch>::Invoke2 angesehen hätte (Man achte auf den Array varArgs):
inline HRESULT CComPtr<IDispatch>::Invoke2(__in DISPID dispid,
__in VARIANT* pvarParam1,
__in VARIANT* pvarParam2,
__out_opt VARIANT* pvarRet) throw()
{
if(pvarParam1 == NULL || pvarParam2 == NULL)
return E_INVALIDARG;
CComVariant varArgs[2] = { *pvarParam2, *pvarParam1 };
DISPPARAMS dispparams = { &varArgs[0], NULL, 2, 0};
return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
}