Ich möchte eine benutzerdefinierte Implementierung von COM in C ++ auf einer UNIX-Plattform implementieren, die es mir ermöglicht, objektorientierten Code dynamisch zu laden und zu verknüpfen. Ich denke, das würde auf einer ähnlichen Funktionalität basieren, die POSIX zum Laden und Aufrufen von DLLs, dh dlopen, dlsym und dlclose, bereitstellt.
Ich verstehe, dass die allgemeine Idee von COM ist, dass Sie mit einigen Funktionen verknüpfen, dh QueryInterface, AddRef und Release in einer gemeinsamen dll (Kernel32.dll), die dann Zugriff auf Schnittstellen ermöglicht, die nur eine Tabelle von Funktionszeigern verkapselt sind mit einem Zeiger auf das Objekt, für das die Funktionszeiger aufgerufen werden sollen. Diese Funktionen werden über IUnknown verfügbar gemacht, von dem Sie erben müssen.
Wie funktioniert das alles? Gibt es eine bessere Möglichkeit, objektorientierten Code dynamisch zu verknüpfen und zu laden? Wie funktioniert die Vererbung von einer DLL? Muss jeder Aufruf der Basisklasse für eine exposed Member-Funktion sein, d. H. Private / protected / public wird einfach ignoriert?
Ich bin ziemlich versiert in C ++ und Template-Metaprogrammierung und habe bereits ein vollständig reflektierendes C ++ - System, d. h. Member-Eigenschaften, Member-Funktionen und globale / statische Funktionen, die boost verwenden.
Ich möchte eine benutzerdefinierte Implementierung von COM in C ++ auf einer UNIX-Plattform implementieren, die es mir ermöglicht, objektorientierten Code dynamisch zu laden und zu verknüpfen. Ich denke, das würde auf einer ähnlichen Funktionalität basieren, die POSIX zum Laden und Aufrufen von DLLs, dh dlopen, dlsym und dlclose, bereitstellt.
Auf der einfachsten Ebene wird COM mit Schnittstellen implementiert. Wenn Sie in c ++ mit der Idee der reinen virtuellen oder abstrakten Basisklassen vertraut sind, dann wissen Sie bereits, wie man eine Schnittstelle in C ++ definiert.
%Vor%Die COM-Runtime bietet eine Menge zusätzlicher Dienste, die für die Windows-Umgebung gelten, aber nicht wirklich benötigt werden, wenn "mini" COM in einer einzigen Anwendung implementiert wird, um dynamisch mit einer OO-Schnittstelle zu verlinken, wie es dlopen normalerweise erlaubt , dlsym usw.
COM-Objekte werden abhängig von Ihrer Plattform in .dll-, .so- oder .dylib-Dateien implementiert. Diese Dateien müssen mindestens eine Funktion exportieren, die standardisiert ist: DllGetClassObject
In Ihrer eigenen Umgebung können Sie einen Prototyp erstellen, wie Sie wollen, aber um mit der COM-Laufzeit unter Windows zu interagieren, müssen der Name und die Parameter natürlich dem com-Standard entsprechen.
Die Grundidee ist, dass ein Zeiger auf eine GUID übergeben wird - 16 Bytes, die eindeutig einem bestimmten Objekt zugewiesen sind, und es erstellt (basierend auf der GUID) und gibt die IClassFactory * eines Factory-Objekts zurück.
Das Factory-Objekt wird dann von der COM-Laufzeit verwendet, um Instanzen des Objekts zu erstellen, wenn die IClassFactory :: CreateInstance-Methode aufgerufen wird.
Also, bis jetzt haben Sie
Ich verstehe, dass die allgemeine Idee von COM darin besteht, dass Sie mit einigen Funktionen wie QueryInterface, AddRef und Release in einer gemeinsamen DLL (Kernel32.dll) verknüpfen, die Ihnen dann Zugriff auf Schnittstellen ermöglicht, die nur eine Tabelle sind Funktionszeiger eingekapselt mit einem Zeiger auf das Objekt, für das die Funktionszeiger aufgerufen werden sollen. Diese Funktionen werden über IUnknown verfügbar gemacht, von dem Sie erben müssen.
Tatsächlich wird COM von OLE32.dll implementiert, das eine "c" API namens CoCreateInstance verfügbar macht. Die App hat CoCreateInstance eine GUID übergeben, die sie in der Windows-Registry nachschlägt - die einen DB der GUID hat - & gt; "Pfad zu DLL" -Zuordnungen. OLE / COM lädt dann die DLL (dlopen), ruft die DllGetClassObject (dlsym) -Methode auf und übergibt die GUID erneut. Vorausgesetzt, dies ist erfolgreich, ruft OLE / COM CreateInstance auf und gibt die resultierende Schnittstelle an app zurück.
Wie funktioniert das alles? Gibt es eine bessere Möglichkeit, objektorientierten Code dynamisch zu verknüpfen und zu laden? Wie funktioniert die Vererbung von einer DLL? Muss jeder Aufruf an die Basisklasse für eine exposed Member-Funktion sein, d. H. Private / protected / public wird einfach ignoriert?
Die implizite Vererbung von C ++ - Code aus einer dll / so / dylib funktioniert, indem jede Methode in der Klasse als "dekoriertes" Symbol exportiert wird. Der Methodenname wird mit der Klasse und dem Typ jedes Parameters versehen. Dies ist die gleiche Art, wie die Symbole aus statischen Bibliotheken (.a oder .lib files iirc) exportiert werden. Statische oder dynamische Bibliotheken, "privat, geschützt usw." werden immer vom Compiler erzwungen, Parsen der Header-Dateien, niemals der Linker.
Ich bin ziemlich versiert in C ++ und Template-Metaprogrammierung und habe bereits ein vollständig reflektierendes C ++ - System, d. h. Member-Eigenschaften, Member-Funktionen und globale / statische Funktionen, die boost verwenden.
c ++ - Klassen können normalerweise nur aus DLLs mit statischer Verknüpfung exportiert werden - dlls, die beim Laden geladen werden, nicht über dlopen zur Laufzeit. COM ermöglicht das dynamische Laden von C ++ - Schnittstellen, indem sichergestellt wird, dass alle in COM verwendeten Datentypen entweder Pod-Typen oder reine virtuelle Schnittstellen sind. Wenn Sie diese Regel brechen, indem Sie eine Schnittstelle definieren, die versucht, einen Boost oder irgendeinen anderen Objekttyp zu übergeben, werden Sie schnell in eine Situation kommen, in der der Compiler / Linker mehr als nur die Header-Datei benötigt, um herauszufinden, was los ist sorgfältig vorbereitete "com" dll muss statisch oder implizit verknüpft werden, um zu funktionieren.
Die andere Regel von COM lautet: Übergeben Sie niemals den Besitz eines Objekts über eine dynamische Bibliotheksgrenze. d.h.Geben Sie niemals eine Schnittstelle oder Daten von einer DLL zurück, und verlangen Sie, dass die App sie löscht. Schnittstellen müssen alle IUnknown oder zumindest eine Release () -Methode implementieren, die es dem Objekt ermöglicht, dies zu löschen. Alle zurückgegebenen Datentypen müssen ebenfalls einen bekannten De-Allocator haben - wenn Sie eine Schnittstelle mit einer Methode namens "CreateBlob" haben, sollte es wahrscheinlich eine Buddy-Methode namens "DeleteBlob" geben.
Ein paar Dinge zu beachten:
Die Macht von COM kommt hauptsächlich von der IDL und dem Midl-Compiler. Es ermöglicht eine sehr genaue Definition der Objekte und Schnittstellen zu allen für Sie generierten C / C ++ - Voreinstellungen.
COM-Registrierung. Unter Windows werden die Klassen-IDs (CLSID) in der Registrierung aufgezeichnet, wo sie mit der ausführbaren Datei verknüpft sind. Sie müssen ähnliche Funktionen in der UNIX-Umgebung bereitstellen.
Die gesamte IUnknown-Implementierung ist ziemlich trivial, außer für QueryInterface
, das funktioniert, wenn es in C implementiert wird (d. h. keine RTTI).
Ein ganz anderer Aspekt von COM ist IDispatch - also Aufruf und Entdeckung der späten gebundenen Methode (Read-only-Reflection).
Sehen Sie sich XPCOM an, da es sich um eine COM-ähnliche Umgebung mit mehreren Plattformen handelt. Das ist wirklich eines dieser Dinge, die Sie besser nutzen, um andere Technologien zu nutzen. Es kann viel Zeit in Anspruch nehmen, die besser woanders verbracht wird.
Sehen Sie sich die CORBA-Dokumentation an, unter System.ComponentModel
im sscli, die XPCOM-Teile der Mozilla-Codebasis. Miguel de Icaza hat etwas wie OLE in GNOME implementiert, das Bonobo genannt wird, was ebenfalls nützlich sein könnte.
Je nachdem, was Sie mit C ++ machen, sollten Sie sich Plugin-Frameworks für C ++ wie Yehia ansehen. Ich glaube Boost hat auch etwas ähnliches.
Bearbeiten: pugg scheint im Moment besser gepflegt zu sein als Yehia. Ich habe es jedoch nicht versucht.
Das grundlegende Design von COM ist ziemlich einfach.
Wo COM komplex wird, hängt davon ab, wie Schnittstellen von außerhalb des Prozesses, in dem sich das Objekt befindet, aufgerufen werden können. COM-Marshalling ist ein böses, haariges Biest. Noch mehr durch die Tatsache, dass COM sowohl Single-Thread- als auch Multi-Thread-Programmiermodelle unterstützt.
Die Windows-Implementierung von COM ermöglicht die Registrierung von Objekten (die ursprüngliche Verwendung der Windows-Registrierung war für COM). Die COM-Registrierung enthält mindestens die Zuordnung zwischen der eindeutigen GUID für ein COM-Objekt und der Bibliothek (DLL), die den Code enthält.
Damit dies funktioniert. DLLs, die COM-Objekte implementieren, müssen über eine ClassFactory verfügen - einen Einstiegspunkt in der DLL mit einem Standardnamen, der zum Erstellen eines der von der DLL implementierten COM-Objekte aufgerufen werden kann. (In der Praxis ruft Windows COM ein IClassFactory-Objekt von diesem Einstiegspunkt ab und verwendet dieses zum Erstellen anderer COM-Objekte.)
Das ist die 10-Cent-Tour, aber um das wirklich zu verstehen, müssen Sie Essential COM von Don Box.
Um wirklich zu verstehen, wie COM funktioniert, empfehle ich "Essential COM" von Don Box zu lesen.
Sie könnten an der (noch nicht vorhandenen) Boost.Extension -Bibliothek interessiert sein.