Benutzerdefinierte COM-Implementierung?

8

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.

    
user176168 27.01.2010, 21:26
quelle

6 Antworten

4

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

  • eine dynamische Bibliothek, die mindestens ein Symbol namens "DllGetClassObject" (oder eine Variante davon)
  • exportiert
  • Eine DllGetClassObject-Methode, die die übergebene GUID prüft, um festzustellen, ob und welches Objekt angefordert wird, und führt dann eine "neue CSomeObjectClassFactory"
  • aus
  • Eine CSomeObjectClassFactory-Implementierung, die IClassFactory implementiert (abgeleitet von) und die CreateInstance-Methode in "neue" Instanzen von CSupportedObject implementiert.
  • CSomeSupportedObject, das eine benutzerdefinierte oder COM-definierte Schnittstelle implementiert, die von IUnknown abgeleitet ist. Dies ist wichtig, weil IClassFactory :: CreateInstance ein IID (wiederum eine 16 Byte eindeutige ID, die dieses Mal eine Schnittstelle definiert) übergeben wird, die es für das Objekt für QueryInterface benötigt.

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.

    
Chris Becke 27.01.2010, 22:36
quelle
6

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.

    
Igor Zevaka 27.01.2010 21:46
quelle
3

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.

    
kprobst 27.01.2010 21:40
quelle
3

Das grundlegende Design von COM ist ziemlich einfach.

  1. Alle COM-Objekte stellen ihre Funktionalität über eine oder mehrere Schnittstellen zur Verfügung
  2. Alle Schnittstellen sind von der IUnknown-Schnittstelle abgeleitet, also haben alle Schnittstellen  QueryInterface, AddRef & amp; Geben Sie Methoden als die ersten drei Methoden ihrer virtuellen Methode frei  Funktionstabelle in bekannter Reihenfolge
  3. Alle Objekte implementieren IUnknown
  4. Jede Schnittstelle, die ein Objekt unterstützt, kann von jeder anderen Schnittstelle abgefragt werden.
  5. Schnittstellen werden durch global eindeutige IDs identifiziert, dies sind IIDs GUIDs oder CLSIDs, aber sie sind alle identisch. Ссылка

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.

    
John Knoeller 27.01.2010 21:55
quelle
2

Um wirklich zu verstehen, wie COM funktioniert, empfehle ich "Essential COM" von Don Box zu lesen.

    
Nemanja Trifunovic 27.01.2010 21:30
quelle
2

Sie könnten an der (noch nicht vorhandenen) Boost.Extension -Bibliothek interessiert sein.

    
Éric Malenfant 27.01.2010 21:56
quelle

Tags und Links