TN038: Implementierung MFC/OLE IUnknown
Hinweis |
---|
Im Folgenden technischen Hinweis ist nicht aktualisiert wurde, seitdem er erstmals in der Onlinedokumentation enthalten waren.Folglich können mehrere Prozeduren und Themen veraltet oder falsch.Die aktuellsten Informationen wird empfohlen, zum Thema Onlinedokumentations im Index finden. |
Im Wesentlichen aus OLE 2 ist das OLE-Component "oder" Object Model.COM definiert einen Standardwert für das kooperierende Objekte miteinander kommunizieren.Dies schließt, wie Aussieht die Details der ein "Object". Dies schließt Folgendes ein, wie Methoden in einem Objekt weitergeleitet werden.COM definiert auch eine Basisklasse, von der alle kompatiblen Klassen COM abgeleitet sind.Diese Basisklasse ist IUnknown.Obwohl die IUnknown-Schnittstelle gekennzeichnet, als eine C++-Klasse, COM nicht in eine beliebige einer Sprache bestimmt ist, sie kann in C in PASCAL oder in einer anderen Sprache implementiert werden, die das binäre Layout eines COM-Objekts unterstützen kann.
OLE bezieht sich auf alle Klassen, die abgeleitete IUnknown von Schnittstellen als "." Dies ist ein wichtiger Unterschied, da eine "Schnittstelle" How to IUnknown damit keine Implementierung enthält.Es definiert lediglich das Protokoll, mit dem Objekte, nicht die Besonderheiten von Kommunikation, was diese Implementierungen verwenden.Dies ist sinnvoll für ein System, das maximale Flexibilität ermöglicht.Es ist Aufgabe MFC ein Standardverhalten für MFC-/C++programme zu implementieren.
Zur Implementierung von MFC IUnknown zu verstehen, müssen Sie zunächst verstehen, was diese Schnittstelle ist.Eine vereinfachte Version von IUnknown wird unten definiert:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
Hinweis |
---|
Bestimmte notwendige Aufrufkonvention, z. B. __stdcall Details dieser Abbildung für ausgecheckt werden. |
Die AddRefRelease-Memberfunktionen steuern und Speicherverwaltung des Objekts.COM verwendet ein Schema Verweiszählungs, Objekte zu verfolgen.Ein Objekt wird nie direkt verwiesen, wie Sie in C++ wurden.Stattdessen werden COM-Objekte immer über einen Zeiger verwiesen.Um das Objekt freigegeben werden, wenn der Besitzer verwendet wird, wird der Release-Member des Objekts aufgerufen (im Gegensatz zur Anwendung der Operator delete, z. B. für ein herkömmliches C++-Objekt fertig sind.)Der Mechanismus für Verweiszählungs können mehrere Verweise auf einem einzigen verwalteten Objekt zu werden.Eine Implementierung von AddRef und Release verwaltet einen Verweiszähler für das Objekt bei — Das Objekt wird der Verweiszähler erst erreicht (null) gelöscht.
AddRef und Release sind aus einem Implementierung standpunkt recht einfach.Es folgt eine einfache Implementierung:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
Die QueryInterface-Memberfunktion ist ein wenig interessanter.Es ist nicht besonders interessant, ein Objekt verfügen, dessen einzige Memberfunktionen AddRef und Release würde es sind nett sein, das Objekt zu übermitteln, um weitere Schritte ausführen zu IUnknown bereitstellt.Dies ist, wo QueryInterface nützlich ist.Es können Sie erhalten eine andere "Schnittstelle" auf demselben Objekt.Diese Schnittstellen sind in der Regel aus IUnknown abgeleitet, und fügen Sie zusätzliche Funktionalität hinzu, indem sie neue Memberfunktion hinzufügen.COM-Schnittstellen haben niemals die Membervariablen, die in der Schnittstelle deklarierten deklariert werden und allen Memberfunktionen, z rein-virtuell.Beispiel:
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
So IPrintInterface, wenn Sie nur IUnknownIUnknown::QueryInterface Aufruf haben, mithilfe IIDIPrintInterface abrufen.IID ist eine 128-Bit-Zahl, die die Schnittstelle eindeutig identifiziert.Es gibt IID für jede Schnittstelle, die Sie oder OLE definiert.Wenn pUnk einen Zeiger auf ein Objekt IUnknown ist, kann der Code, um IPrintInterface daraus abzurufen:
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface,
(void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface
}
Das angezeigt werden würden, sondern z. B. recht einfach implementieren Sie ein Objekt, das die Schnittstelle IPrintInterface und IUnknown unterstützt?In diesem Fall ist es einfach, da das direkte Verwendung IPrintInterface IUnknown abgeleitete — indem IPrintInterface implementiert, wird automatisch IUnknown unterstützt.Beispiele:
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 würde genau identisch mit denen übereinstimmen, die oben implementiert wurden.CPrintObj::QueryInterface würde etwa folgendermaßen 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, wenn der Schnittstellenbezeichner (IID) erkannt wird, wird ein Zeiger auf das Objekt zurückgegeben; andernfalls tritt ein Fehler auf.Beachten Sie außerdem, dass QueryInterface erfolgreichen impliziten AddRef ergibt.Selbstverständlich würden Sie auch CEditObj::Print implementieren müssen.Das ist einfach, da IPrintInterface direkt von der IUnknown-Schnittstelle abgeleitet wurde.Wenn Sie jedoch zwei verschiedene Schnittstellen unterstützen möchten, sollten sowohl von abgeleiteten IUnknown folgendermaßen:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
Obwohl es mehrere verschiedene Möglichkeiten gibt, eine Klasse zu implementieren, die IEditInterface und IPrintInterface unterstützt, einschließend mit C++-Mehrfachvererbung, liegt der Schwerpunkt dieser Hinweis für die Verwendung von geschachtelten Klassen, diese Funktionen zu implementieren.
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 vollständige Implementierung wird unten aufgeführt:
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 die meisten der IUnknown Implementierung in die CEditPrintObj-Klasse platziert werden soll, statt den Code in CEditPrintObj::CEditObj und CEditPrintObj::CPrintObj duplizierend.Dadurch wird die Menge des Codes und vermeidet Fehler.Der Punkt besteht darin, die von der IUnknown-Schnittstelle ist es möglich, QueryInterface aufrufen, um jede unterstützte Schnittstelle abgerufen und möglicherweise das Objekt aus jeder dieser Schnittstellen kann auf die gleiche Weise durchzuführen.Dies bedeutet, dass alle QueryInterface-Funktionen, die von allen Schnittstellen verfügbar sind, verhalten sich genau die gleiche Weise müssen.Damit diese eingebetteten Objekte die Implementierung im äußeren "Object" verwendet wird, ein Zeiger wieder aufrufen (m_pParent).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.Durchaus wurde ein Bit des Codes hinzugefügt, um eine Funktion hinzuzufügen:Die Fähigkeit, das Objekt zu bearbeiten.Glücklicherweise ist es sehr selten für Schnittstellen, nur eine Memberfunktion aufweisen (obwohl diese möglicherweise der Fall) und in diesem Fall EditObject und PrintObject normalerweise in eine einzelne Schnittstelle zusammengefasst werden.
Der ist wesentlich Erklärung und viel Code für ein solches einfaches Szenario.Die MFC/OLE-Klassen bieten eine einfachere Alternative.Die MFC-Implementierung verwendet eine Technik, die der Methode ähnelt, die Windows-Meldungen mit Meldungszuordnungen umschlossen werden.Diese Funktion wird Interface Maps bezeichnet und wird im nächsten Abschnitt erläutert.
MFC-Schnittstellen-Zuordnungen
MFC/OLE beinhaltet eine Implementierung von "Schnittstelle" Ähnliches mit MFC verknüpft "Meldungszuordnungen" und "in" Konzept und Dispatchzuordnungen bei der Ausführung ein.Die Kernfunktionen von Schnittstellen-Zuordnungen MFC lauten wie folgt:
Eine Standardimplementierung von IUnknown, in CCmdTarget die Klasse erstellt.
Wartung des Verweiszählers, durch AddRef und Release
Gesteuerte Implementierung der Daten aus QueryInterface
Außerdem unterstützen Schnittstellen von Namespacezuordnungen die folgenden erweiterten Features:
Unterstützung für das Erstellen von COM-Objekten aggregierbaren
Unterstützung für die Verwendung von ganzen Objekten in der Implementierung eines COM-Objekts
Die Implementierung ist hookable und erweiterbar
Weitere Informationen zur Aggregation finden Sie in der OLE-Programmierreferenz.
Schnittstellen-Zuordnungs von MFC ist in der CCmdTarget-Klasse Stamm.CCmdTarget "hat-ein" Verweiszähler sowie alle Memberfunktionen, die mit der IUnknown Implementierung zugeordnet sind (der Verweiszähler Beispielsweise ist in CCmdTarget).Um eine Klasse zu erstellen, die OLE COM unterstützt, leiten Sie eine Klasse von CCmdTarget und verwenden unterschiedliche Makros sowie Memberfunktionen von CCmdTarget um die gewünschten Schnittstellen zu implementieren.Implementierung von MFC verwendet geschachtelte Klassen aufgerufen, um jede Schnittstelle ähnlich wie im Beispiel oben zu definieren.Dies wird als einfacher mit einer Standardimplementierung von IUnknown sowie einiger Makros, die ein Teil des Codes wiederkehrenden ausschließen.
So erstellen Sie eine Klasse mit den Schnittstellen implementieren MFC
Leiten Sie eine Klasse von CCmdTarget. entweder direkt oder indirekt
Verwenden Sie die DECLARE_INTERFACE_MAP-Funktion in der Definition der abgeleiteten Klasse.
Für jede Schnittstelle, die Sie unterstützen möchten, die BEGIN_INTERFACE_PART und Makros in der Klassendefinition END_INTERFACE_PART verwenden.
In der Implementierungsdatei verwenden Sie die BEGIN_INTERFACE_MAP und END_INTERFACE_MAP Makros, um die Schnittstellenzuordnung der Klasse zu definieren.
Für jedes unterstützte IID, verwenden Sie das Makro INTERFACE_PART zwischen BEGIN_INTERFACE_MAP und den END_INTERFACE_MAP Makros, um dies zu einem bestimmten IID "Part" der Klasse zugeordnet werden soll.
Implementieren Sie jede der geschachtelten Klassen, die die Schnittstellen darstellen, die Sie unterstützen.
Verwenden Sie das METHOD_PROLOGUE Makro, um das übergeordnete Element, CCmdTarget abgeleitetes Objekt zuzugreifen.
AddRef, Release und QueryInterface können CCmdTarget die Implementierung dieser Funktionen (ExternalAddRef, ExternalRelease und ExternalQueryInterface) delegieren.
Das obige CPrintEditObj-Beispiel kann implementiert werden, wie nachfolgend veranschaulicht:
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 oben dargestellte Deklaration erstellt eine Klasse, die von CCmdTarget abgeleitet ist.Das DECLARE_INTERFACE_MAP Makro teilt dem Framework mit, dass diese Klasse eine benutzerdefinierte Schnittstellenzuordnung verfügt.Außerdem definieren die BEGIN_INTERFACE_PART und END_INTERFACE_PART Makros geschachtelte Klassen, in diesem Fall mit dem Namen CEditObj und CPrintObj (das X verwendet wird, um die geschachtelten Klassen von globalen Klassen, die mit "C" beginnen und Schnittstellenklassen nur zu unterscheiden, die mit "I" beginnen).Zwei geschachtelte Member dieser Klasse werden erstellt: m_CEditObj und m_CPrintObj.Die Makros automatisch AddRef deklarieren, Release und QueryInterface-Funktionen. Deklarieren Sie deshalb nur die Funktionen bestimmt dieser Schnittstelle: EditObject und PrintObject (das OLE Makro- STDMETHOD wird so verwendet, dass Schlüsselwörter und virtuelle _stdcall ggf. für die Zielplattform bereitgestellt werden).
Um die Schnittstellenzuordnung für diese Klasse implementieren:
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()
Dies schließt das IID_IPrintInterface IID mit m_CPrintObj und IID_IEditInterface mit m_CEditObj an.Die CCmdTarget Implementierung von QueryInterface (CCmdTarget::ExternalQueryInterface) verwendet diese Zuordnung, um Zeiger auf den m_CPrintObj m_CEditObj und zurückzugeben, wenn sie angefordert wird.Es ist nicht erforderlich, einen Eintrag für IID_IUnknown einzuschließen. Das Framework verwendet die erste Schnittstelle in der Zuordnung (in diesem Fall m_CPrintObj), wenn IID_IUnknown angefordert wird.
Obwohl das BEGIN_INTERFACE_PART Makro automatisch AddRef, Release und QueryInterface-Funktionen für Sie deklarierte, müssen Sie ihn trotzdem 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, würde zu den oben beschriebenen Definitionen für CEditPrintObj::CEditObj ähnlich sein.Auch wenn es möglich ist, ein Makro erstellen, das verwendet werden kann, um diese Funktionen automatisch generiert werden (die jedoch zuvor in MFC/OLE-Entwicklung dies der Fall war, ist es schwierig, Haltepunkte festzulegen, wenn ein Makro mehr als eine Codezeile generiert.Aus diesem Grund wird dieser Code manuell erweitert.
Indem die Frameworkimplementierung von Meldungszuordnungen verwendet, gibt es mehrere Möglichkeiten, die nicht erforderlich wären, um Aufgaben:
Implementieren der QueryInterface
Implementieren der Version und AddRef
Deklarieren Sie eine dieser Schnittstellen der beiden Methoden für integrierten
Darüber hinaus verwendet das Framework Meldungszuordnungen intern.Dies ermöglicht es Ihnen, aus einer Framework Klasse abzuleiten, sagen, die bereits COleServerDoc unterstützt bestimmte Schnittstellen und Ersetzungen oder Ergänzungen zu Schnittstellen bereitstellt, die vom Framework bereitgestellt werden.Dies wird durch die Tatsache, dass das vollständige Framework ermöglicht das Erben eine Schnittstellenzuordnung von einer Basisklasse unterstützt, die BEGIN_INTERFACE_MAP ist der Grund, weshalb als zweiter Parameter den Namen der Basisklasse hat.
Hinweis |
---|
Es ist im Allgemeinen nicht möglich, die Implementierung integrierter Implementierungen MFC der OLE-Schnittstellen wiederverwenden, indem die eingebettete Spezialisierung dieser Schnittstelle aus der MFC-Version erbt.Dies ist keine da der Verwendung des METHOD_PROLOGUE Makro, um den Zugriff auf CCmdTarget abgeleitetes Objekt enthalten ist, bedeutet fixed offset Abrufen des eingebetteten Objekts aus CCmdTarget abgeleitetes Objekt möglich.Daher können Sie z. B. ein eingebettetes XMyAdviseSink Implementierung von nicht COleClientItem::XAdviseSink ableiten, da MFC in XAdviseSink auf Ihr an einem bestimmten Offset vom Anfang des COleClientItem-Objekts abhängt. |
Hinweis |
---|
Sie können jedoch für alle Funktionen der MFC-Implementierung delegieren, dass Sie Standardverhalten MFC soll.Dies ist in der MFC-Implementierung von IOleInPlaceFrame (XOleInPlaceFrame) in der COleFrameHook-Klasse durchgeführt (es m_xOleInPlaceUIWindow für viele Funktionen zu übertragen.)Dieser Entwurf wurde ausgewählt, um die Größe von Objekten zur Laufzeit zu reduzieren, die mehrere Schnittstellen implementieren. Verwendung entfällt die Notwendigkeit für einen Zeiger zurück (z. B. die m_pParent Methode wurde im vorherigen Abschnitt verwendet). |
Aggregations-und Schnittstellen-Zuordnungen
Zusätzlich zum Unterstützen von COM-Objekten, eigenständigen auch MFC unterstützt Aggregation.Aggregation selbst ist ein komplexes werden hier Thema zu erläutern. Weitere Informationen finden Sie in der OLE-Programmierreferenz Informationen über die Aggregation an.Dieser Hinweis wird einfach die Unterstützung für die Aggregation, die in die Framework und Schnittstellen von Namespacezuordnungen erstellt wird.
Es gibt zwei Möglichkeiten, Aggregation zu verwenden: (1) mithilfe eines COM-Objekts, das Aggregation unterstützt und (2), ein Objekt implementiert, das von anderen aggregiert werden kann.Diese Funktionen können als "Verwenden eines gesamten Objekts" und "vornehmen eines Objekts aggregierbar" bezeichnet.MFC unterstützt beide.
Verwenden eines gesamten Objekts
Um ein gesamtes Objekt zu verwenden, muss jede Methode vorhanden sein, das Aggregat in QueryInterface-Mechanismus zu binden.Das heißt, muss das gesamte Objekt, als ob es ein systemeigener Teil des Objekts ist.Wie ordnet diese Objekte gleichwertiges gleichwertiges Objekt in MFC Schnittstelle Mechanismus?Zusätzlich zum INTERFACE_PART Makro in dem ein geschachteltes Objekt in einen IID zugeordnet ist, können Sie ein ganzes Objekt als Teil der CCmdTarget abgeleiteten Klasse deklarieren.Dazu wird das INTERFACE_AGGREGATE Makro verwendet.Dadurch können Sie eine Membervariable anzugeben (die einen Zeiger auf IUnknown oder abgeleitete Klasse sein muss), der in den Schnittstellen Mechanismus zur zuordnungs integriert werden soll.Wenn der Zeiger nicht NULL ist, wenn CCmdTarget::ExternalQueryInterface aufgerufen wird, ruft das Framework automatisch die QueryInterface-Memberfunktion des Aggregatobjekts an, wenn die angeforderte IID keine der systemeigenen IIDs ist, die vom CCmdTarget-Objekt selbst unterstützt werden.
So verwenden Sie das INTERFACE_AGGREGATE-Makro
Deklarieren Sie eine Membervariable ( IUnknown*), die einen Zeiger auf das gesamte Objekt enthält.
Schließen Sie ein INTERFACE_AGGREGATE Makro in der Schnittstellenzuordnung ein, die auf die Membervariable mit dem Namen verweist.
So fügen Sie einem bestimmten Zeitpunkt (normalerweise während CCmdTarget::OnCreateAggregates), initialisieren Sie die Membervariable auf einen anderen Wert als NULL
Beispiele:
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()
m_lpAggrInner wird im Konstruktor auf NULL initialisiert.Das Framework ignoriert einen NULL-wertigen Membervariable in der Standardimplementierung von QueryInterface.OnCreateAggregates ist ein guter Ausgangspunkt, um die gesamten Objekte tatsächlich zu erstellen.Sie müssen jedoch explizit aufrufen, wenn Sie das Objekt außerhalb der MFC-Implementierung von COleObjectFactory erstellen.Der Grund für das Erstellen von Aggregaten in CCmdTarget::OnCreateAggregates sowie der Verwendung von CCmdTarget::GetControllingUnknown wird, wenn er aggregierbare Objekte erstellt, wird deutlich erläutert.
Diese Methode gibt das Objekt alle Schnittstellen, die das gesamte Objekt sowie seine systemeigene Schnittstellen unterstützt.Wenn Sie nur einen Teil der Schnittstellen, die das Aggregat unterstützt, können Sie CCmdTarget::GetInterfaceHook überschreiben.Dies ermöglicht Ihnen das sehr hookability auf niedriger Ebene, die QueryInterface ähnelt.Normalerweise werden alle Schnittstellen, die das Aggregat unterstützt.
Eine Objekt-Implementierung aggregierbar ausführen
Damit ein Objekt ist aggregierbar, müssen die Implementierung von AddRef, Release und QueryInterface zu einem "Steuern des Unbekannten delegieren." Das heißt, sodass es ist Teil des Objekts, muss sie AddRef, Release und delegiert QueryInterface auch auf ein anderes Objekt abgeleitet von IUnknown.Dieses "Unbekannt" steuert, wird dem Objekt bereitgestellt, wenn es erstellt wird, das heißt es zur Implementierung von COleObjectFactory bereitgestellt wird.Das Implementieren dieses enthält eine kleine Menge an Mehraufwand, und ist in einigen Fällen nicht wünschenswert, dass MFC macht dies optional.Um ein Objekt zu ermöglichen, aggregierbar ist, rufen Sie CCmdTarget::EnableAggregation vom Konstruktor des Objekts an.
Wenn das Objekt auch Aggregate verwendet, müssen Sie zudem sicher sein, dass das richtige führen "Unbekannt" auf den gesamten Objekten steuernd.Normalerweise wird dieser IUnknown Zeiger auf das Objekt übergeben, wenn das Aggregat erstellt wird.Beispielsweise ist der Parameter pUnkOuter "Steuern des Unbekannten" für Objekte, die mit CoCreateInstance erstellt werden.Das richtige "Unknown" Zeiger steuernd, können abgerufen werden, indem CCmdTarget::GetControllingUnknown aufruft.Der Wert, der von dieser Funktion zurückgegeben wird, ist jedoch während des Konstruktors ungültig.Aus diesem Grund wird empfohlen, dass Sie die Aggregate nur in einer Überschreibung der CCmdTarget::OnCreateAggregates erstellen, in dem der Rückgabewert von GetControllingUnknown zuverlässig ist, selbst wenn aus der COleObjectFactory Implementierung.
Es ist auch wichtig, dass das Objekt den richtigen Verweiszähler bearbeiten, sofern künstliche Verweiszähler hinzufügt oder freigegeben wird.Um sicherzustellen, dass dieses Aufrufs immer der Fall ist und ExternalAddRefExternalRelease anstelle InternalRelease und InternalAddRef.Es ist nur selten InternalRelease oder InternalAddRef für eine Klasse aufzurufen, die Aggregation unterstützt.
Referenzmaterial
Erweiterte Verwendung von OLE das Definieren eigener Schnittstellen oder Überschreiben der Implementierung des Frameworks der OLE-Schnittstellen erfordert die Verwendung des zugrunde liegenden Schnittstellen Mechanismus zur zuordnungs.
In diesem Abschnitt wird jedes Makro und APIs, das verwendet wird, um diese erweiterten Features zu implementieren.
CCmdTarget::EnableAggregation — Funktions-Beschreibung
void EnableAggregation();
Hinweise
Rufen Sie diese Funktion im Konstruktor der abgeleiteten Klasse auf, wenn OLE-Aggregation für Objekte dieses Typs unterstützen möchten.Dies bereitet eine spezielle IUnknown-Implementierung vor, die für aggregierbare Objekte erforderlich ist.
CCmdTarget::ExternalQueryInterface — Funktions-Beschreibung
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOID FAR* ppvObj
);
Hinweise
Parameter
lpIID
Ein Zeiger auf einen weiteren (IID des ersten Arguments QueryInterface)ppvObj
Ein Zeiger auf einen IUnknown* (zweites Argument für QueryInterface)
Hinweise
Rufen Sie diese Funktion in der Implementierung von IUnknown für jede Schnittstelle an, die von der Klasse implementiert wird.Diese Funktion stellt die datengesteuerte StandardImplementierung von QueryInterface auf Grundlage der Schnittstellenzuordnung des Objekts bereit.Es ist erforderlich, den Rückgabewert in HRESULT umgewandelt werden.Wenn das Objekt zusammengesetzt ist, ruft diese Funktion im "Steuern von IUnknown" an, anstatt die lokale Schnittstellenzuordnung zu verwenden.
CCmdTarget::ExternalAddRef — Funktions-Beschreibung
DWORD ExternalAddRef();
Hinweise
Rufen Sie diese Funktion in der Implementierung von IUnknown::AddRef für jede Schnittstelle an, die von der Klasse implementiert wird.Der Rückgabewert ist der neue Verweiszähler auf dem CCmdTarget-Objekt.Wenn das Objekt zusammengesetzt ist, ruft diese Funktion im "Steuern von IUnknown" an, anstatt den lokalen Verweiszähler zu bearbeiten.
CCmdTarget::ExternalRelease — Funktions-Beschreibung
DWORD ExternalRelease();
Hinweise
Rufen Sie diese Funktion in der Implementierung von IUnknown::Release für jede Schnittstelle an, die von der Klasse implementiert wird.Der Rückgabewert gibt den neuen Verweiszähler für das Objekt an.Wenn das Objekt zusammengesetzt ist, ruft diese Funktion im "Steuern von IUnknown" an, anstatt den lokalen Verweiszähler zu bearbeiten.
Beschreibung DECLARE INTERFACE MAP - Makro
DECLARE_INTERFACE_MAP
Hinweise
Verwenden Sie dieses Makro in einer Klasse, die von CCmdTarget abgeleitet wird, das eine Schnittstellenzuordnung verfügt.Verwendet nahezu auf die gleiche Weise wie DECLARE_MESSAGE_MAP.Dieser Makroaufruf sollte in die Klassendefinition, normalerweise in einem Header (angeordnet werden. H.)Eine Klasse mit DECLARE_INTERFACE_MAP muss die Schnittstellenzuordnung in der Implementierungsdatei (.CPP) mit den BEGIN_INTERFACE_MAP und END_INTERFACE_MAP Makros definieren.
BEGIN_INTERFACE_PART und Beschreibungen der END_INTERFACE_PART — Makro
BEGIN_INTERFACE_PART(
localClass,
iface
);
END_INTERFACE_PART(
localClass
)
Hinweise
Parameter
localClass
Der Name der Klasse, die die Schnittstelle implementiertiface
Der Name der Klasse, die diese Schnittstelle implementiert.
Hinweise
Für jede Schnittstelle, die die Klasse implementiert, müssen Sie ein BEGIN_INTERFACE_PART und END_INTERFACE_PART Paar vorliegen.Diese Makros definieren eine lokale Klasse, die von der OLE-Schnittstelle abgeleitet ist, die Sie definieren sowie eine eingebettete Membervariable dieser Klasse.AddRef, Release und QueryInterface werden automatisch Member deklariert.Sie müssen die Deklarationen für die anderen Memberfunktionen enthalten, die Teil der Schnittstelle handelt, die implementiert wird (diese Deklarationen werden zwischen END_INTERFACE_PART Makros und die BEGIN_INTERFACE_PART platziert).
Das Argument ist die iface OLE-Schnittstelle, die Sie, wie IAdviseSink oder IPersistStorage (oder eigene benutzerdefinierte Schnittstelle) implementieren möchten.
Das localClass-Argument entspricht dem Namen der lokalen Klasse definiert ist.Ein "x" wird automatisch mit dem Namen vorangestellt.Diese Namenskonvention wird verwendet, um Konflikte mit globalen Klassen mit demselben Namen zu vermeiden.Außerdem wird der Name des eingebetteten Members identisch, es sei denn, die den Namen localClass von "m_x" vorangestellt wird.
Beispiele:
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)
Eine lokale Klasse definieren, die XMyAdviseSink aufgerufen wurden, das von IAdviseSink abgeleitet wird, und ein Member der Klasse, in der er mit dem Namen m_xMyAdviseSink.Note deklariert wurde:
Hinweis |
---|
Die Zeilen, die mit STDMETHOD_ beginnen, sind im Wesentlichen aus OLE2.H kopiert und leicht geändert.Das Kopieren von OLE2.H kann Fehler verringern, die schwer das Problem zu beheben sind. |
BEGIN_INTERFACE_MAP und Beschreibungen der END_INTERFACE_MAP — Makro
BEGIN_INTERFACE_MAP(
theClass,
baseClass
)
END_INTERFACE_MAP
Hinweise
Parameter
theClass
Die Klasse, in der die Schnittstellenzuordnung definiert werden sollbaseClass
Die Klasse, von der theClass von berechnet.
Hinweise
Die BEGIN_INTERFACE_MAP und END_INTERFACE_MAP Makros werden in der Implementierungsdatei verwendet, um die Schnittstellenzuordnung tatsächlich zu definieren.Für jede Schnittstelle, die implementiert wird, gibt es eine oder mehrere INTERFACE_PART Makro aufgerufen wird.Für jedes Aggregat, das die Klasse verwendet, gibt es einen INTERFACE_AGGREGATE Makroaufruf.
Beschreibung INTERFACE PART - Makro
INTERFACE_PART(
theClass,
iid,
localClass
)
Hinweise
Parameter
theClass
Der Name der Klasse, die die Schnittstellenzuordnung enthält.iid
IID, das der eingebetteten Klasse zugeordnet werden soll.localClass
Der Name der lokalen Klasse (weniger dem Namen "X ").
Hinweise
Dieses Makro wird zwischen dem BEGIN_INTERFACE_MAP Makro verwendet und dem END_INTERFACE_MAP Makro für jede Schnittstelle, die das Objekt unterstützt wird.Es ermöglicht es Ihnen, ein IID an einen Member der Klasse zuzuordnen, die durch theClass und localClass angegeben wird.Das "m_x" wird auf localClass automatisch hinzugefügt.Beachten Sie, dass mehr als ein IID möglicherweise einem einzelnen Member zugeordnet ist.Dies ist besonders nützlich, wenn Sie nur die "meisten abgeleitete" - Schnittstelle implementieren und den Wunsch, um zwischen allen Schnittstellen zur Verfügung zu stellen.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 für alle Schnittstellen: IOleUIWindow, IOleWindow oder IUnknown, außer der "meisten abgeleiteten" Schnittstelle IOleInPlaceFrameWindow (das Sie implementieren).Um dies zu behandeln können Sie mehr als ein INTERFACE_PART Makros auf die Basisschnittstelle für jede IOleInPlaceFrameWindow-Schnittstelle zuordnen:
Klassendefinitions in der Datei:
BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)
Klassenimplementierungs in der Datei:
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 auf IUnknown werden, da sie immer erforderlich ist.
Beschreibung INTERFACE PART - Makro
INTERFACE_AGGREGATE(
theClass,
theAggr
)
Hinweise
Parameter
theClass
Der Name der Klasse, die die Schnittstellenzuordnung enthält.theAggr
Der Name der Membervariable, die aggregiert werden soll.
Hinweise
Dieses Makro wird verwendet, um das Framework anzuzeigen, dass die Klasse ein gesamtes Objekt verwendet.Es muss zwischen BEGIN_INTERFACE_PARTEND_INTERFACE_PART und Makros angezeigt werden.Ein gesamtes Objekt ist ein separates Objekt, das über IUnknown abgeleitet ist.Indem Sie ein Aggregat und das INTERFACE_AGGREGATE Makro verwenden, können Sie alle Schnittstellen machen, die das gesamte scheinen durch das Objekt direkt unterstützt werden unterstützt.Das theAggr-Argument ist einfach der Name einer Membervariablen einer Klasse, die von IUnknown abgeleitet wurde (entweder direkt oder indirekt).Alle INTERFACE_AGGREGATE Makros müssen den INTERFACE_PART Makros ausführen, wenn sie in eine Schnittstellenzuordnung eingefügt werden.