Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Anmerkung
Der folgende technische Hinweis wurde seit der ersten Aufnahme in die Onlinedokumentation nicht aktualisiert. Daher sind einige Prozeduren und Themen möglicherweise veraltet oder falsch. Für die neuesten Informationen empfiehlt es sich, nach dem interessanten Thema im Onlinedokumentationsindex zu suchen.
Im Mittelpunkt von OLE 2 steht das "OLE Component Object Model" oder COM. COM definiert einen Standard dafür, wie zusammenarbeitende Objekte untereinander kommunizieren. Dazu gehören die Details dazu, wie ein "Objekt" aussieht, einschließlich der Art, wie Methoden für ein Objekt verteilt werden. COM definiert auch eine Basisklasse, von der alle COM-kompatiblen Klassen abgeleitet werden. Diese Basisklasse ist IUnknown. Obwohl die IUnknown- Schnittstelle als C++-Klasse bezeichnet wird, ist COM nicht spezifisch für eine sprache – sie kann in C, PASCAL oder einer anderen Sprache implementiert werden, die das binäre Layout eines COM-Objekts unterstützen kann.
OLE bezieht sich auf alle Von IUnknown abgeleiteten Klassen als "Schnittstellen". Dies ist ein wichtiger Unterschied, da eine "Schnittstelle" wie IUnknown ohne Implementierung mit sich bringt. Es definiert einfach das Protokoll, nach dem Objekte kommunizieren, nicht die Besonderheiten der Implementierungen. Dies ist für ein System sinnvoll, das maximale Flexibilität ermöglicht. Es ist die Aufgabe von MFC, ein Standardverhalten für MFC/C++-Programme zu implementieren.
Um die Implementierung von IUnknown von MFC zu verstehen, müssen Sie zunächst verstehen, was diese Schnittstelle ist. Nachfolgend wird eine vereinfachte Version von IUnknown- definiert:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
Anmerkung
Bestimmte erforderliche Details zur Anrufkonvention, z. B. __stdcall, werden für diese Abbildung ausgelassen.
Die Mitgliedsfunktionen AddRef und Release steuern die Speicherverwaltung des Objekts. COM verwendet ein Verweiszählschema, um Objekte nachzuverfolgen. Auf ein Objekt wird nie direkt wie in C++ verwiesen. Stattdessen wird immer über einen Zeiger auf COM-Objekte verwiesen. Um das Objekt freizugeben, nachdem der Besitzer es benutzt hat, wird das Release Member des Objekts aufgerufen (im Gegensatz zur Verwendung von operator delete, wie es bei einem herkömmlichen C++-Objekt der Fall wäre). Der Verweiszählmechanismus ermöglicht die Verwaltung mehrerer Verweise auf ein einzelnes Objekt. Eine Implementierung von AddRef und Release verwaltet eine Verweisanzahl für das Objekt – das Objekt wird erst gelöscht, wenn die Referenzanzahl null erreicht.
AddRef und Release sind relativ einfach aus Implementierungssicht. Im Folgenden wird eine einfache Implementierung veranschaulicht:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
Die Mitgliedsfunktion QueryInterface ist ein wenig interessanter. Es ist nicht sehr interessant, ein Objekt zu haben, dessen einzige Memberfunktionen AddRef und Release sind – es wäre schön, dem Objekt zu sagen, mehr zu tun, als IUnknown bereitstellt. Hier ist QueryInterface- nützlich. Damit können Sie eine andere "Schnittstelle" für dasselbe Objekt abrufen. Diese Schnittstellen werden in der Regel von IUnknown abgeleitet und fügen zusätzliche Funktionen hinzu, indem neue Mitgliederfunktionen hinzugefügt werden. COM-Schnittstellen haben nie Membervariablen in der Schnittstelle deklariert, und alle Member-Funktionen werden als rein virtuelle Funktionen deklariert. Zum Beispiel
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
Um ein IPrintInterface zu erhalten, wenn Sie nur ein IUnknownhaben, rufen Sie QueryInterface mit dem IID des IPrintInterfaceauf. Ein IID ist eine 128-Bit-Zahl, die die Schnittstelle eindeutig identifiziert. Es gibt eine IID für jede Schnittstelle, die entweder Sie oder OLE definieren. Wenn pUnk- ein Zeiger auf ein IUnknown-Objekt ist, kann der Code, aus dem ein IPrintInterface abgerufen werden soll, folgendes sein:
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface
}
Das scheint ziemlich einfach, aber wie würden Sie ein Objekt implementieren, das sowohl die IPrintInterface als auch IUnknown Schnittstelle unterstützt. In diesem Fall ist es einfach, da die IPrintInterface direkt von IUnknown abgeleitet wird – durch implementieren von IPrintInterface, IUnknown automatisch unterstützt wird. Zum Beispiel:
class CPrintObj : public CPrintInterface
{
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
virtual void PrintObject();
};
Die Implementierungen von AddRef- und Release- entsprechen genau den oben implementierten Implementierungen.
CPrintObj::QueryInterface würde etwa wie folgt aussehen:
HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
Wie Sie sehen können, wird, wenn der Schnittstellenbezeichner (IID) erkannt wird, ein Zeiger auf Ihr Objekt zurückgegeben. andernfalls tritt ein Fehler auf. Beachten Sie auch, dass eine erfolgreiche QueryInterface zu einer impliziten AddRefführt. Natürlich müssen Sie auch CEditObj::P rint implementieren. Das ist einfach, weil die IPrintInterface direkt von der IUnknown Schnittstelle abgeleitet wurde. Wenn Sie jedoch zwei verschiedene Schnittstellen unterstützen möchten, die beide von IUnknownabgeleitet sind, sollten Sie Folgendes berücksichtigen:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
Obwohl es verschiedene Möglichkeiten gibt, eine Klasse zu implementieren, die sowohl IEditInterface als auch IPrintInterface unterstützt, einschließlich der Verwendung von C++-Mehrfachvererbung, konzentriert sich dieser Hinweis auf die Verwendung geschachtelter Klassen zur Implementierung dieser Funktionalität.
class CEditPrintObj
{
public:
CEditPrintObj();
HRESULT QueryInterface(REFIID iid, void**);
ULONG AddRef();
ULONG Release();
DWORD m_dwRef;
class CPrintObj : public IPrintInterface
{
public:
CEditPrintObj* m_pParent;
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
} m_printObj;
class CEditObj : public IEditInterface
{
public:
CEditPrintObj* m_pParent;
virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
} m_editObj;
};
Die gesamte Implementierung ist unten enthalten:
CEditPrintObj::CEditPrintObj()
{
m_editObj.m_pParent = this;
m_printObj.m_pParent = this;
}
ULONG CEditPrintObj::AddRef()
{
return ++m_dwRef;
}
CEditPrintObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = &m_printObj;
AddRef();
return NOERROR;
}
else if (iid == IID_IEditInterface)
{
*ppvObj = &m_editObj;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG CEditPrintObj::CEditObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CEditPrintObj::CEditObj::Release()
{
return m_pParent->Release();
}
HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
return m_pParent->QueryInterface(iid, ppvObj);
}
ULONG CEditPrintObj::CPrintObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CEditPrintObj::CPrintObj::Release()
{
return m_pParent->Release();
}
HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
return m_pParent->QueryInterface(iid, ppvObj);
}
Beachten Sie, dass der größte Teil der IUnknown -Implementierung in der Klasse CEditPrintObj untergebracht ist, anstatt den Code in CEditPrintObj::CEditObj und CEditPrintObj::CPrintObj zu duplizieren. Dadurch wird die Menge an Code reduziert und Fehler vermieden. Der wichtigste Punkt hier ist, dass es über die IUnknown-Schnittstelle möglich ist, QueryInterface- aufzurufen, um jede Schnittstelle abzurufen, die das Objekt unterstützen kann, und von jeder dieser Schnittstellen ist es möglich, dasselbe zu tun. Dies bedeutet, dass sich alle QueryInterface- funktionen, die von jeder Schnittstelle verfügbar sind, genau auf die gleiche Weise verhalten müssen. Damit diese eingebetteten Objekte die Implementierung im "äußeren Objekt" aufrufen können, wird ein Back-Pointer (m_pParent) verwendet. Der m_pParent Zeiger wird während des CEditPrintObj-Konstruktors initialisiert. Anschließend würden Sie CEditPrintObj::CPrintObj::PrintObject und CEditPrintObj::CEditObj::EditObject ebenfalls implementieren. Es wurde ein wenig Code hinzugefügt, um ein Feature hinzuzufügen – die Möglichkeit, das Objekt zu bearbeiten. Glücklicherweise ist es sehr ungewöhnlich, dass Schnittstellen nur eine einzelne Memberfunktion haben (obwohl dies geschieht), und in diesem Fall würde EditObject und PrintObject in der Regel in eine einzige Schnittstelle kombiniert werden.
Das ist eine Menge Erklärung und viel Code für ein so einfaches Szenario. Die MFC/OLE-Klassen bieten eine einfachere Alternative. Die MFC-Implementierung verwendet eine Technik, die der Art und Weise ähnelt, wie Windows-Nachrichten mit Message Maps umschlossen werden. Diese Einrichtung wird Interface Maps genannt und wird im nächsten Abschnitt erläutert.
MFC-Schnittstellenzuordnungen
MFC/OLE enthält eine Implementierung von "Interface Maps" ähnlich wie die "Message Maps" und "Dispatch Maps" von MFC in Konzept und Ausführung. Die Kernfunktionen der Schnittstellenkarten von MFC sind wie folgt:
Eine Standardimplementierung von IUnknown, die in die
CCmdTargetKlasse integriert ist.Pflege der Referenzanzahl, geändert durch AddRef und Release
Datengesteuerte Implementierung von QueryInterface-
Außerdem unterstützen Schnittstellenzuordnungen die folgenden erweiterten Funktionen:
Unterstützung für das Erstellen von aggregierbaren COM-Objekten
Unterstützung für die Verwendung von Aggregatobjekten in der Implementierung eines COM-Objekts
Die Implementierung ist anschließbar und erweiterbar.
Weitere Informationen zur Aggregation finden Sie im Thema Aggregation.
Die Unterstützung der Schnittstellenzuordnung von MFC haben ihren Stammpfad in der CCmdTarget-Klasse.
CCmdTarget "hat eine"-Referenzanzahl sowie alle Mitgliedsfunktionen, die mit der IUnknown -Implementierung verbunden sind (die Referenzanzahl steht zum Beispiel in CCmdTarget). Um eine Klasse zu erstellen, die OLE COM unterstützt, leiten Sie eine Klasse von CCmdTarget ab und verwenden verschiedene Makros sowie Memberfunktionen von CCmdTarget, um die gewünschten Schnittstellen zu implementieren. Die Implementierung von MFC verwendet geschachtelte Klassen, um jede Schnittstellenimplementierung ähnlich wie im obigen Beispiel zu definieren. Dies wird mit einer Standardimplementierung von IUnknown sowie einer Reihe von Makros erleichtert, die einen Teil des sich wiederholenden Codes beseitigen.
Grundlagen der Schnittstellenkarte
So implementieren Sie eine Klasse mithilfe der Schnittstellenzuordnungen von MFC
Leiten Sie eine Klasse direkt oder indirekt von
CCmdTargetab.Verwenden Sie die funktion
DECLARE_INTERFACE_MAPin der abgeleiteten Klassendefinition.Verwenden Sie für jede Schnittstelle, die Sie unterstützen möchten, die BEGIN_INTERFACE_PART und END_INTERFACE_PART Makros in der Klassendefinition.
Verwenden Sie in der Implementierungsdatei die BEGIN_INTERFACE_MAP und END_INTERFACE_MAP Makros, um die Schnittstellenzuordnung der Klasse zu definieren.
Verwenden Sie für jede unterstützte IID das INTERFACE_PART Makro zwischen dem BEGIN_INTERFACE_MAP und END_INTERFACE_MAP Makros, um diese IID einem bestimmten "Teil" Ihrer Klasse zuzuordnen.
Implementieren Sie jede der geschachtelten Klassen, die die unterstützten Schnittstellen darstellen.
Verwenden Sie das METHOD_PROLOGUE-Makro, um auf das übergeordnete Objekt, das von
CCmdTargetabgeleitete Objekt, zuzugreifen.AddRef, Releaseund QueryInterface können an die
CCmdTargetImplementierung dieser Funktionen delegieren (ExternalAddRef,ExternalReleaseundExternalQueryInterface).
Das obige Beispiel "CPrintEditObj" kann wie folgt implementiert werden:
class CPrintEditObj : public CCmdTarget
{
public:
// member data and member functions for CPrintEditObj go here
// Interface Maps
protected:
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(EditObj, IEditInterface)
STDMETHOD_(void, EditObject)();
END_INTERFACE_PART(EditObj)
BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
STDMETHOD_(void, PrintObject)();
END_INTERFACE_PART(PrintObj)
};
Die obige Deklaration erstellt eine von CCmdTargetabgeleitete Klasse. Das DECLARE_INTERFACE_MAP Makro teilt dem Framework mit, dass diese Klasse über eine benutzerdefinierte Schnittstellenzuordnung verfügt. Darüber hinaus definieren die BEGIN_INTERFACE_PART- und END_INTERFACE_PART Makros geschachtelte Klassen, in diesem Fall mit den Namen CEditObj und CPrintObj (das X wird nur verwendet, um die geschachtelten Klassen von globalen Klassen zu unterscheiden, die mit "C" und Schnittstellenklassen beginnen, die mit "I" beginnen). Zwei geschachtelte Member dieser Klassen werden erstellt: m_CEditObj bzw. m_CPrintObj. Die Makros deklarieren automatisch die funktionen AddRef, Releaseund QueryInterface; Daher deklarieren Sie nur die für diese Schnittstelle spezifischen Funktionen: EditObject und PrintObject (das OLE-Makro STDMETHOD wird verwendet, sodass _stdcall und virtuelle Schlüsselwörter entsprechend der Zielplattform bereitgestellt werden).
So implementieren Sie die Schnittstellenzuordnung für diese Klasse:
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()
Dies verbindet die IID_IPrintInterface IID mit m_CPrintObj bzw. IID_IEditInterface mit m_CEditObj. Die CCmdTarget -Implementierung von QueryInterface (CCmdTarget::ExternalQueryInterface) verwendet diese Map, um auf Anfrage Zeiger auf m_CPrintObj und m_CEditObj zurückzugeben. Es ist nicht erforderlich, einen Eintrag für IID_IUnknowneinzuschließen; das Framework verwendet die erste Schnittstelle in der Karte (in diesem Fall m_CPrintObj), wenn IID_IUnknown angefordert wird.
Obwohl das BEGIN_INTERFACE_PART Makro automatisch die AddRef-, Release und QueryInterface-Funktionen für Sie deklariert hat, müssen Sie sie dennoch implementieren:
ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
REFIID iid,
void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
// code to "Edit" the object, whatever that means...
}
Die Implementierung für CEditPrintObj::CPrintObj ähnelt den oben genannten Definitionen für CEditPrintObj::CEditObj. Obwohl es möglich wäre, ein Makro zu erstellen, das verwendet werden könnte, um diese Funktionen automatisch zu generieren (aber früher in der MFC/OLE-Entwicklung war dies der Fall), ist es schwierig, Haltepunkte festzulegen, wenn ein Makro mehr als eine Codezeile generiert. Aus diesem Grund wird dieser Code manuell erweitert.
Durch Verwendung der Frameworkimplementierung von Meldungszuordnungen mussten einige Vorgänge nicht ausgeführt werden:
Implementieren von QueryInterface
Implementieren von AddRef und Release
Deklarieren einer dieser integrierten Methoden für beide Schnittstellen
Darüber hinaus verwendet das Framework Meldungszuordnungen intern. Auf diese Weise können Sie von einer Frameworkklasse ableiten, zum Beispiel von COleServerDoc, die bereits bestimmte Schnittstellen unterstützt und entweder Ersatz für oder Ergänzungen zu den vom Framework bereitgestellten Schnittstellen bietet. Dies ist möglich, da das Framework das Erben einer Schnittstellenzuordnung von einer Basisklasse vollständig unterstützt. Aus diesem Grund verwendet BEGIN_INTERFACE_MAP als zweiten Parameter den Namen der Basisklasse.
Anmerkung
Im Allgemeinen ist es nicht möglich, die integrierten Implementierungen der OLE-Schnittstellen von MFC wiederzuverwenden, indem man nur die eingebettete Spezialisierung dieser Schnittstelle aus der MFC-Version übernimmt. Dies ist nicht möglich, da die Verwendung des Makros METHOD_PROLOGUE, um Zugriff auf das enthaltende CCmdTarget-abgeleitete Objekt zu erhalten, einen festen Offset des eingebetteten Objekts gegenüber dem CCmdTarget-abgeleiteten Objekt impliziert. Dies bedeutet beispielsweise, dass Sie einen eingebetteten XMyAdviseSink nicht von der Implementierung von MFC in COleClientItem::XAdviseSinkableiten können, da XAdviseSink auf einem bestimmten Offset vom oberen Rand des COleClientItem-Objekts basiert.
Anmerkung
Sie können jedoch für alle Funktionen, für die Sie das Standardverhalten von MFC wünschen, an die MFC-Implementierung delegieren. Dies geschieht in der MFC-Implementierung von IOleInPlaceFrame (XOleInPlaceFrame) in der COleFrameHook Klasse (sie delegiert für viele Funktionen an m_xOleInPlaceUIWindow). Dieses Design wurde ausgewählt, um die Laufzeitgröße von Objekten zu reduzieren, die viele Schnittstellen implementieren; es beseitigt die Notwendigkeit eines Back-Pointers (z. B. die Art und Weise, wie m_pParent im vorherigen Abschnitt verwendet wurde).
Aggregations- und Schnittstellenkarten
Zusätzlich zur Unterstützung eigenständiger COM-Objekte unterstützt MFC auch Aggregation. Aggregation selbst ist zu komplex, um hier zu diskutieren; weitere Informationen zur Aggregation finden Sie im Thema Aggregation. Dieser Hinweis beschreibt einfach die Unterstützung für aggregation, die in das Framework und die Schnittstellenzuordnungen integriert ist.
Es gibt zwei Möglichkeiten zum Verwenden der Aggregation: (1) mithilfe eines COM-Objekts, das Aggregation unterstützt, und (2) implementieren ein Objekt, das von einem anderen aggregiert werden kann. Diese Fähigkeiten können als "Verwendung eines Aggregatobjekts" und "ein Objekt aggregierbar machen" bezeichnet werden. MFC unterstützt beide.
Verwenden eines Aggregatobjekts
Um ein Aggregatobjekt zu verwenden, muss es eine Möglichkeit geben, das Aggregat mit dem QueryInterface-Mechanismus zu verknüpfen. Mit anderen Worten: Das Aggregatobjekt muss sich so verhalten, als ob es sich um einen systemeigenen Teil Ihres Objekts handelt. Wie kann dies also mit dem Schnittstellenzuordnungsmechanismus von MFC verknüpft werden. Zusätzlich zum INTERFACE_PART Makro, bei dem ein geschachteltes Objekt einem IID zugeordnet ist, können Sie auch ein Aggregatobjekt als Teil ihrer CCmdTarget abgeleiteten Klasse deklarieren. Dazu wird das INTERFACE_AGGREGATE Makro verwendet. Damit können Sie eine Mitgliedsvariable (die ein Zeiger auf eine IUnknown oder eine abgeleitete Klasse sein muss) angeben, die in den Interface-Map-Mechanismus integriert werden soll. Wenn der Zeiger beim Aufruf von CCmdTarget::ExternalQueryInterface nicht NULL ist, ruft das Framework automatisch die Mitgliedsfunktion QueryInterface des Aggregatobjekts auf, wenn das angeforderte IID nicht zu den nativen IIDs gehört, die vom CCmdTarget -Objekt selbst unterstützt werden.
So verwenden Sie das INTERFACE_AGGREGATE-Makro
Deklarieren Sie eine Membervariable (eine
IUnknown*), die einen Zeiger auf das Aggregatobjekt enthält.Fügen Sie ein INTERFACE_AGGREGATE-Makro in Ihre Interface-Map ein, das sich namentlich auf die Member-Variable bezieht.
Initialisieren Sie irgendwann (in der Regel während
CCmdTarget::OnCreateAggregates) die Membervariable auf einen anderen Wert als NULL.
Zum Beispiel:
class CAggrExample : public CCmdTarget
{
public:
CAggrExample();
protected:
LPUNKNOWN m_lpAggrInner;
virtual BOOL OnCreateAggregates();
DECLARE_INTERFACE_MAP()
// "native" interface part macros may be used here
};
CAggrExample::CAggrExample()
{
m_lpAggrInner = NULL;
}
BOOL CAggrExample::OnCreateAggregates()
{
// wire up aggregate with correct controlling unknown
m_lpAggrInner = CoCreateInstance(CLSID_Example,
GetControllingUnknown(), CLSCTX_INPROC_SERVER,
IID_IUnknown, (LPVOID*)&m_lpAggrInner);
if (m_lpAggrInner == NULL)
return FALSE;
// optionally, create other aggregate objects here
return TRUE;
}
BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
// native "INTERFACE_PART" entries go here
INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()
Die m_lpAggrInner Variable wird im Konstruktor auf NULL initialisiert. Das Framework ignoriert eine NULL-Membervariable in der Standardimplementierung von QueryInterface.
OnCreateAggregates ist ein guter Ort, um ihre Aggregatobjekte tatsächlich zu erstellen. Sie müssen es explizit aufrufen, wenn Sie das Objekt außerhalb der MFC-Implementierung von COleObjectFactoryerstellen. Der Grund für das Erstellen von Aggregaten in CCmdTarget::OnCreateAggregates sowie die Verwendung von CCmdTarget::GetControllingUnknown wird beim Erstellen von aggregatfähigen Objekten deutlich.
Mit dieser Technik erhalten Sie alle Schnittstellen, die vom Aggregatobjekt unterstützt werden, sowie die systemeigenen Schnittstellen. Wenn Sie nur eine Teilmenge der vom Aggregat unterstützten Schnittstellen benötigen, können Sie CCmdTarget::GetInterfaceHooküberschreiben. Dies ermöglicht Ihnen eine Hookability auf sehr niedriger Ebene, ähnlich wie bei QueryInterface. In der Regel möchten Sie alle Schnittstellen, die vom Aggregat unterstützt werden.
Eine Objektimplementierung aggregierbar machen
Damit ein Objekt aggregierbar ist, muss die Implementierung von AddRef-, Release-und QueryInterface- an ein "Steuerung unbekannt" delegieren. Anders ausgedrückt: Damit es Teil des Objekts ist, muss es AddRef, Releaseund QueryInterface an ein anderes Objekt delegieren, das auch von IUnknownabgeleitet wird. Dieses "controlling unknown"-Objekt wird dem Objekt beim Erstellen zur Verfügung gestellt, d. h., es wird der Implementierung von COleObjectFactory bereitgestellt. Die Implementierung führt zu einem geringen Aufwand und ist in einigen Fällen nicht wünschenswert, daher macht MFC dies optional. Um das Aggregieren eines Objekts zu ermöglichen, rufen Sie CCmdTarget::EnableAggregation aus dem Konstruktor des Objekts auf.
Wenn das Objekt ebenfalls Aggregate verwendet, müssen Sie sicherstellen, dass Sie die richtige "steuernde Unbekannte" an die Aggregatobjekte übergeben. In der Regel wird dieser IUnknown Zeiger an das Objekt übergeben, wenn das Aggregat erstellt wird. Der Parameter "pUnkOuter" ist z. B. der "kontrollierende Unbekannte" für Objekte, die mit CoCreateInstanceerstellt wurden. Der richtige "controlling unknown"-Zeiger kann durch Aufrufen von CCmdTarget::GetControllingUnknown abgerufen werden. Der von dieser Funktion zurückgegebene Wert ist jedoch während des Konstruktors ungültig. Aus diesem Grund wird empfohlen, dass Sie Ihre Aggregate nur in einer Überschreibung von CCmdTarget::OnCreateAggregateserstellen, wobei der Rückgabewert aus GetControllingUnknown zuverlässig ist, auch wenn er aus der Implementierung von COleObjectFactory erstellt wurde.
Es ist auch wichtig, dass das Objekt beim Hinzufügen oder Freigeben künstlicher Referenzzählungen die korrekte Referenzanzahl manipuliert. Um sicherzustellen, dass dies der Fall ist, rufen Sie immer ExternalAddRef und ExternalRelease anstelle von InternalRelease und InternalAddRefauf. Es ist selten, InternalRelease oder InternalAddRef für eine Klasse aufzurufen, die Aggregation unterstützt.
Referenzmaterial
Die erweiterte Verwendung von OLE, wie Definieren eigener Schnittstellen oder Überschreiben der Implementierung des Frameworks der OLE-Schnittstellen, erfordert die Nutzung des zugrunde liegenden Schnittstellenzuordnungsmechanismus.
In diesem Abschnitt werden jedes Makro und die APIs erläutert, die zum Implementieren dieser erweiterten Features verwendet werden.
CCmdTarget::EnableAggregation — Funktionsbeschreibung
void EnableAggregation();
Bemerkungen
Rufen Sie diese Funktion im Konstruktor der abgeleiteten Klasse auf, wenn Sie die OLE-Aggregation für Objekte dieses Typs unterstützen möchten. Dadurch wird eine spezielle IUnknown-Implementierung vorbereitet, die für aggregierte Objekte erforderlich ist.
CCmdTarget::ExternalQueryInterface — Funktionsbeschreibung
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOIDFAR* ppvObj
);
Parameter
lpIID
Ein ferner Zeiger auf eine IID (das erste Argument für QueryInterface)
ppvObj
Ein Zeiger auf ein IUnknown*-Objekt (das zweite Argument für QueryInterface)
Bemerkungen
Rufen Sie diese Funktion in Ihrer Implementierung von IUnknown für jede Von Der Klasse implementierte Schnittstelle auf. Diese Funktion stellt die standardmäßige datengesteuerte Implementierung von QueryInterface basierend auf der Schnittstellenzuordnung Ihres Objekts bereit. Es ist erforderlich, den Rückgabewert in ein HRESULT umzuwandeln. Wenn das Objekt in eine Aggregation eingebunden ist, ruft diese Funktion das "controlling IUnknown" auf, anstatt die lokale Schnittstellenzuordnung zu verwenden.
CCmdTarget::ExternalAddRef — Funktionsbeschreibung
DWORD ExternalAddRef();
Bemerkungen
Rufen Sie diese Funktion in Ihrer Implementierung von IUnknown::AddRef für jede von Der Klasse implementierte Schnittstelle auf. Der Rückgabewert ist die neue Verweisanzahl für das CCmdTarget-Objekt. Wenn das Objekt aggregiert wird, ruft diese Funktion das "controlling IUnknown"-Objekt auf, anstatt den lokalen Verweiszähler zu bearbeiten.
CCmdTarget::ExternalRelease — Funktionsbeschreibung
DWORD ExternalRelease();
Bemerkungen
Rufen Sie diese Funktion in Ihrer Implementierung von IUnknown::Release für jede Schnittstelle auf, die Ihre Klasse implementiert. Der Rückgabewert gibt die neue Bezugsanzahl für das Objekt an. Wenn das Objekt aggregiert wird, ruft diese Funktion das "controlling IUnknown"-Objekt auf, anstatt den lokalen Verweiszähler zu bearbeiten.
DECLARE_INTERFACE_MAP – Makrobeschreibung
DECLARE_INTERFACE_MAP
Bemerkungen
Verwenden Sie dieses Makro in jeder Von CCmdTarget abgeleiteten Klasse, die über eine Schnittstellenzuordnung verfügt. Wird auf die gleiche Weise wie DECLARE_MESSAGE_MAP verwendet. Dieser Makroaufruf sollte in der Klassendefinition platziert werden, in der Regel in einer Kopfzeile (. H) Datei. Eine Klasse mit DECLARE_INTERFACE_MAP muss die Schnittstellenzuordnung in der Implementierungsdatei (.CPP) mit den Makros BEGIN_INTERFACE_MAP und END_INTERFACE_MAP definieren.
BEGIN_INTERFACE_PART und END_INTERFACE_PART – Makrobeschreibungen
BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)
Parameter
localClass
Der Name der Klasse, die die Schnittstelle implementiert
iface
Der Name der Schnittstelle, die diese Klasse implementiert
Bemerkungen
Für jede Schnittstelle, die Ihre Klasse implementiert, müssen Sie über ein BEGIN_INTERFACE_PART- und END_INTERFACE_PART-Paar verfügen. Diese Makros definieren eine lokale Klasse, die von der OLE-Schnittstelle abgeleitet wird, die Sie definieren, sowie eine eingebettete Membervariable dieser Klasse. Die AddRef, Releaseund QueryInterface Mitglieder werden automatisch deklariert. Sie müssen die Deklarationen für die anderen Memberfunktionen einschließen, die Teil der implementierten Schnittstelle sind (diese Deklarationen werden zwischen dem Makro "BEGIN_INTERFACE_PART" und "END_INTERFACE_PART" platziert).
Das iface-Argument ist die OLE-Schnittstelle, die Sie implementieren möchten, wie zum Beispiel IAdviseSinkoder IPersistStorage (oder Ihre eigene benutzerdefinierte Schnittstelle).
Das argument localClass ist der Name der lokalen Klasse, die definiert wird. Der Buchstabe "x" wird dem Namen automatisch vorangestellt. Diese Benennungskonvention wird verwendet, um Kollisionen mit globalen Klassen mit demselben Namen zu vermeiden. Darüber hinaus entspricht der Name des eingebetteten Elements dem Namen der localClass, außer dass 'm_x' vorangestellt ist.
Zum Beispiel:
BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
STDMETHOD_(void, OnViewChange)(DWORD, LONG);
STDMETHOD_(void, OnRename)(LPMONIKER);
STDMETHOD_(void, OnSave)();
STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)
würde eine lokale Klasse namens "XMyAdviseSink" definieren, die von IAdviseSink abgeleitet ist, und ein Element der Klasse, in der sie deklariert wird, namens m_xMyAdviseSink. Hinweis:
Anmerkung
Die Zeilen, die mit STDMETHOD_ beginnen, sind im Wesentlichen aus OLE2.H kopiert und leicht verändert. Das Kopieren von ihnen aus OLE2.H kann Fehler verringern, die schwer zu beheben sind.
BEGIN_INTERFACE_MAP und END_INTERFACE_MAP – Makrobeschreibungen
BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP
Parameter
theClass
Die Klasse, in der die Schnittstellenzuordnung definiert werden soll
baseClass
Die Klasse, von der theClass abstammt.
Bemerkungen
Die BEGIN_INTERFACE_MAP- und END_INTERFACE_MAP-Makros werden in der Implementierungsdatei verwendet, um die Schnittstellenzuordnung tatsächlich zu definieren. Für jede implementierte Schnittstelle gibt es einen oder mehrere INTERFACE_PART Makroaufrufe. Für jedes Aggregat, das die Klasse verwendet, gibt es einen INTERFACE_AGGREGATE Makroaufruf.
INTERFACE_PART — Makrobeschreibung
INTERFACE_PART(theClass, iid, localClass)
Parameter
theClass
Der Name der Klasse, die die Schnittstellenzuordnung enthält.
iid
Die IID, die der eingebetteten Klasse zugeordnet werden soll.
localClass
Der Name der lokalen Klasse (weniger das 'X').
Bemerkungen
Dieses Makro wird zwischen dem BEGIN_INTERFACE_MAP-Makro und dem END_INTERFACE_MAP Makro für jede Schnittstelle verwendet, die ihr Objekt unterstützt. Sie können eine IID einem Element der Klasse zuordnen, die durch theClass und localClassangegeben ist. Die m_x wird automatisch zur localClass hinzugefügt. Beachten Sie, dass mehrere IID einem einzelnen Mitglied zugeordnet sein können. Dies ist sehr nützlich, wenn Sie nur eine "abgeleitete" Schnittstelle implementieren und alle Zwischenschnittstellen ebenfalls bereitstellen möchten. Ein gutes Beispiel hierfür ist die IOleInPlaceFrameWindow Schnittstelle. Die Hierarchie sieht wie folgt aus:
IUnknown
IOleWindow
IOleUIWindow
IOleInPlaceFrameWindow
Wenn ein Objekt IOleInPlaceFrameWindow implementiert, kann ein Client QueryInterface auf einer dieser Schnittstellen: IOleUIWindow, IOleWindow, or IUnknown, eben der „am meisten abgeleiteten“ Schnittstelle IOleInPlaceFrameWindow (derjenigen, die Sie tatsächlich implementieren). Um dies zu behandeln, können Sie mehrere INTERFACE_PART Makros verwenden, um jede basisbasierte Schnittstelle der IOleInPlaceFrameWindow-Schnittstelle zuzuordnen:
in der Klassendefinitionsdatei:
BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)
in der Klassenimplementierungsdatei:
BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP
Das Framework kümmert sich um IUnknown, da es immer erforderlich ist.
INTERFACE_PART — Makrobeschreibung
INTERFACE_AGGREGATE(theClass, theAggr)
Parameter
theClass
Der Name der Klasse, die die Schnittstellenzuordnung enthält,
theAggr
Der Name der Membervariable, die aggregiert werden soll.
Bemerkungen
Dieses Makro wird verwendet, um dem Framework mitzuteilen, dass die Klasse ein Aggregatobjekt verwendet. Sie muss zwischen den makros BEGIN_INTERFACE_PART und END_INTERFACE_PART angezeigt werden. Ein Aggregatobjekt ist ein separates Objekt, das von IUnknownabgeleitet wird. Mithilfe eines Aggregats und des INTERFACE_AGGREGATE-Makros können Sie festlegen, dass alle Schnittstellen, die vom Aggregat unterstützt werden, direkt vom Objekt unterstützt werden. Das Argument theAggr ist einfach der Name einer Mitgliedsvariable Ihrer Klasse, die von IUnknown abgeleitet ist (entweder direkt oder indirekt). Alle INTERFACE_AGGREGATE Makros müssen den INTERFACE_PART Makros folgen, wenn sie in einer Schnittstellenzuordnung platziert werden.
Siehe auch
Technische Hinweise nach Nummer
Technische Hinweise nach Kategorie