Übergeben von Schnittstellenzeigern

Veröffentlicht: 12. Jan 2000 | Aktualisiert: 08. Nov 2004

Von Michael Höhne

Bei der Betrachtung diverser Objektmodelle stellt man fest, dass häufig eine Struktur ähnlich der folgenden gewählt wird:
ClassA: public: STDMETHOD(GetB)(IClassB**); ClassB: public: STDMETHOD(get_Parent)(IClassA**pVal);

Das heißt, die Methode GetB von Objekt A liefert ein Objekt vom Typ B. Dieses Objekt (B) hat eine Property Parent, die seinen Erzeuger (A) zurückliefert. Fragt sich nur, wie B bei der Erzeugung die Information über A erhalten soll, schließlich akzeptiert der Konstruktur keine Parameter. Natürlich können wir dem Interface B eine Methode oder Property SetParent hinzufügen, doch wäre diese Methode überall sichtbar und würde bei unsachgemäßer Anwendung zu schweren Fehlern führen.

Die Lösung besteht in der Übergabe des Interfaces von A beim Aufruf der CoCreateInstance Methode. Normalerweise würde man Folgendes schreiben, um eine Instanz von B zu erhalten:

HRESULT hr = CoCreateInstance( 
CLSID_ClassB, NULL, CLSCTX_INPROC_SERVER, IID_IClassB, 
(void**)&pB 
);

Im zweiten Parameter (NULL) kann jedoch der Parent von B übergeben werden, wobei dies nicht unbedingt mit dem wirklichen Erzeuger identisch sein muss:

HRESULT hr = CoCreateInstance( 
CLSID_ClassB, GetUnknown(), CLSCTX_INPROC_SERVER, IID_IClassB, (void**) &pB 
);

Diese Syntax ist schön, hat allerdings einen großen Nachteil: sie schlägt fehl. Das liegt an der Tatsache, dass CoCreateInstance zwingend vorschreibt, dass das IUnknown Interface angefordert werden muss, wenn der Outer Unknown angegeben wird. Damit der Aufruf funktioniert, muss der Code entsprechend umgeschrieben werden:

STDMETHODIMP CClassA::GetB(IClassB** pRetVal) 
{ 
IUnknown* pUnkB; 
HRESULT hr = CoCreateInstance( 
CLSID_ClassB, GetUnknown(), CLSCTX_INPROC_SERVER, 
IID_IUnknown, (void**) &pUnkB 
); 
if (SUCCEEDED(hr)) 
hr = pUnkB->QueryInterface(IID_IClassB, (void**)pRetVal); 
return hr; 
}

Bleibt noch zu klären, wo der übergebene Interface Pointer von A bleibt. Freundlicherweise erledigt die CComObjectRootEx Klasse diese Aufgabe für uns. Sie enthält die Membervariable m_pOuterUnknown und speichert dort den übergebenen Pointer. Die Parent Property ist demnach sehr einfach:

STDMETHODIMP CClassB::get_Parent(IClassA **pVal) 
{ 
return m_pOuterUnknown->QueryInterface( 
IID_IClassA, (void**)pVal 
); 
}

Viel Spaß beim Ausprobieren!