Seminar Komponentenorientierte Softwareentwicklung Für : Herr Prof. Dr. Thiesing COM (DCOM, COM+) Von: Nurhan Duman MatrNr: 7037779 1
Inhaltsangabe Historie von COM/DCOM/COM+...S.3 Was ist COM (allgemeine Formulierung)... S.4 Warum COM / Vorteile von COM... S.4 Kleiner Abstecher: Was sind Zeiger?... S.5 Programmiersprachenunabhängigkeit...S.7 Wie funktioniert COM genau?... S.8 Noch einiges zu den Begriffen... S.11 COM-Komponente als DLL bzw. EXE...S.12 COM-InprocServer... S.13 Entwicklung einer COM Komponente mit Visual C++ und Assistenten... S.15 Zusammenfassung.S.24 Literaturverweise... S.25 2
Historie von COM/DCOM/COM+ Früher als es nur die MS-Dos Umgebung gab, war es nicht vorstellbar daß zwei Anwendungen gleichzeitig auf einem Rechner liefen. Später wurden die Rechnerleistungen immer mehr erweitert, und Windows wurde entwickelt. Nun war es möglich, mit Multitasking zu arbeiten. Der Wunsch zwischen den unterschiedlichen Anwendungen wie Word und Excel Daten austauschen zu lassen, wurde immer größer. Man entwickelte verschiedene Wege, um dieses Problem zu lösen. Zwei dieser Lösungen heißen Zwischenablage und DDE (Dynamic Data Exchange), wobei letztere heute nicht mehr bekannt ist, da sie für Softwareentwickler viel zu schwer hantierbar war und nicht mehr eingesetzt wurde. Das Copy-and-Paste-Prinzip, das die Zwischenablage für uns ermöglicht, ist bis heute gängiger Standard und kann nicht mehr weggedacht werden. Um zwei Anwendungen miteinander zu verknüpfen bsp. in ein Word-Dokument eine Excel-Tabelle zu verknüpfen, war mit der Zwischenablage nicht möglich. Wollte man in der eingebetteten Tabelle etwas ändern, musste man zuerst in der eigentlichen Excel- Umgebung dies tun, dann kopieren und die engebettete Tabelle durch die neue ersetzen. Mit der Einführung von OLE1 (Object Linking and Embedding) war es möglich, in der Excel-Tabelle Änderungen zu machen, und diese wurden in jedem Dokument, wo jene Excel-Tabelle eingebettet war, übernommen. Das eingefügte Dokument konnte sogar durch Doppelklick geöffnet werden. Diese Technik, die Kommunikation zwischen verschiedenen Prozessen zu gewährleistet, basierte wiederum auf DDE, welches als ein Protokoll dafür verstanden werden konnte und kompliziert zu programmieren war. Man erkannte später, daß die eingebetteten Dokumente (Objekte) selbst kleine Software-Komponenten waren, die in verschiedene Anwendungen eingebaut werden konnten und die Anwendungen dadurch erweiterten. OLE1 wurde durch OLE2 ersetzt. Unter OLE2, das eigentlich auf COM (Component Object Model) basierte, konnte man durch Doppelklick das eingebettete Objekt im selben Fenster bearbeiten (Vorort- Aktivierung). Beide Anwendungen wurden zusammen entwickelt und aufeinander abgestimmt, doch COM bot vielmehr allgemeine Interaktionsmechanismen, die es Softwarekomponenten gestattete, gemäß den von ihnen veröffentlichten Diensten miteinander zu kommunizieren. Mit der Einführung von Windows NT Betriebssystem wurde DCOM (Distributed COM) eingeführt, der es ermöglichte, daß auch zwischen entfernten Rechnern Anwendungen miteinander kommunizieren. COM+ ist die Weiterentwicklung von COM, die mit Windows2000 eingeführt wurde. COM+ umfasst COM und den Microsoft Transaction Server sowie weitere Dienste. 3
Zwischenablage (1987) Verteilte Computer (1980er) OLE1 (1992) OLE2 / COM (1995) Open Software Foundation Distributed Computing Environment Remote Procedure Calls (OSF DCE RPC) Ende 80er Distributed COM (1996) COM+ (1997) Was ist COM (allgemeine Formulierung) COM steht für Component Object Model, ein von Microsoft definierter Standard für die Entwicklung von Anwendungs-Komponenten. Dazu definiert der Standard so genannte Schnittstellen, über die eine Anwendung entsprechende COM-Objekte der Komponenten anfordern kann. Warum COM / Vorteile von COM Um große, komplexe Aufgaben zu realisieren, bedarf es ausgeklügelter organisatorischer Strukturen. Probleme ab einer gewissen Größe bedürfen einer strukturellen Aufteilung. Diese besteht darin, Probleme zu zerlegen und dafür Teillösungen zu suchen, welche dann zu einer Gesamtlösung zusammengefügt werden. Die Idee, Probleme zu zerlegen, ist gleichermaßen für die Softwareentwicklung anzuwenden. 4
COM ist ein Hilfsmittel, um komplexe Aufgabenstellungen in der Softwareentwicklung in mehrere Ebenen zu zerlegen und anschließend eine Gesamtlösung zu erstellen. COM standardisiert den Zugriff auf das Dienstangebot einer Softwarekomponente und erreicht somit Interoperabilität. D.h. es können mehrere Komponenten (Teillösungen einer Software) erstellt werden und durch COM bzw. durch die definierten Schnittstellen können diese Teillösungen als Ganzes wieder zusammengesetzt werden. Es können Objekte erzeugt werden, Methoden aufgerufen werden usw. Ein weiterer Vorteil oder Nutzen von COM ist es, daß die Komponente, die genutzt werden soll, in einer beliebigen Programmiersprache geschrieben werden kann solange sie ein spezielles binäres Format aufweist, doch dazu später. Kleiner Abstecher: Was sind Zeiger? Für eingefleischte Java-Entwickler wird das wohl ein Fremdwort sein. Zeiger sind nichts anderes als (wie die Bezeichnung schon sagt) Variablen, die nicht den Wert einer Variable selbst sondern die Speicheradresse enthalten, wo sich der Wert der Variable befindet. Zeiger werde in C definiert, indem vor der Variable ein Stern-Symbol angegeben wird. Die Adresse einer gewöhnlichen Variable kann man mit dem kaufmännischen Und bestimmen. In dem oben angegebenen Beispiel wird die Variable a definiert, welche dann den Wert 10 erhält. Die Variable b stellt einen Zeiger dar, welcher dann die Adresse der Variable a zugewiesen bekommt. Auf den Inhalt der Variable a kann man wie gewohnt zugreifen. Um auf den Inhalt der Variable b zugreifen zu können, muss man das Stern-Symbol davor schreiben, sonst greift man auf die Adresse zu. 5
Der Vorteil von Zeigern ist, dass die Adresse während des Programmablaufs geändert werden kann, somit sind dynamische Listen etc. möglich. Bsp: class Figure { public: void paint() { printf( error: paint of class Figure called!\n ); class Rectangle : public Figure {public: void paint() { printf( painting a rectangle \n ); class Circle : public Figure {public: void paint() { printf( painting a circle \n ); //Objekte Erzeugen Figure * somefigures[3]; somefigure[0] new Rectangle(); somefigure[1] new Circle(); somefigure[2] new Rectangle(); for (int i=0; i<3; i++) somefigures[i] ->paint(); //Ausgabe error: paint of class Figure called! error: paint of class Figure called! error: paint of class Figure called! Wir stoßen hier auf die Grenzen der Leistungsfähigkeit von Methodenaufrufen in der von uns programmierten Art und Weise! Der ablaufende Code hält sich exakt an die Vereinbarungen, die im Quelltext stehen, sprich die Definition von somefigures als Variable des Typs Figure sowie der Aufruf somefigures[i] -> paint veranlassen den Übersetzer, die zur Übersetzungszeit vorliegende Information für die Code-Generierung zugrunde zu legen. Abgeleitete Klassen von paint haben so niemals eine Chance, zur Laufzeit ausgeführt zu werden (statische Bindung oder auch early-binding), der compile-time-datentyp des Zeigers ist für den Aufruf der Methode zur Laufzeit entscheidend. Möglich wäre es aber mit virtual bei der Definition der Methoden. 6
class Figure { public: virtual void paint() = 0; class Rectangle : public Figure {public: virtual void paint() { printf( painting a rectangle \n ); class Circle : public Figure {public: virtual void paint() { printf( painting a circle \n ); Mit diesen Methodenaufrufen erhalten wir als Ergebnis: Painting a rectangle... Painting a circle... Painting a rectangle Zu Klassen, die eine oder mehrere als virtual deklarierte Methoden enthalten, erzeugt der Compiler eine so genannte virtuelle Funktionszeigertabelle vtbl (virtual function table). Dieses eindimensionale Feld liefert für jede virtuelle Methode einen Funktionszeiger, also einen Pointer, über den die Methode zur Laufzeit indirekt aufgerufen wird. Jede Instanz einer solchen Klasse wiederum besitzt in ihren Instanzdaten einen Zeiger auf diese Tabelle, den sogenanngten virtual function table pointer vptr. Programmiersprachenunabhängigkeit Nahezu alle verfügbaren C++-Compiler erzeugen Binärcode, der virtuelle Methoden über die Indirektionsstufe einer vtbl aufruft. Man entschloss sich, diesen Mechanismus auch für COM-Schnittstellen zu verwenden Ein Zeiger auf eine COM-Schnittstelle ist ein Zeiger auf eine vtbl, die der zugehörigen abstrakten Basisklasse in C++ gehört. In welcher Sprache die Methoden des COM-Objektes geschrieben worden sind, die der COM-Client aufrufen möchte spielt hier keine Rolle. Der Zugriff auf eine COM- Schnittstelle wie auch das Speicherlayout einer COM-Schnittstelle sind durch ein binäres Standardformat fest vorgegeben. Wenn nun ein Client die Parameter (Datentyp) und den Rückgabewert einer bestimmten Methode kennt und den Funktionszeiger aus der vtbl verwendet, kann er 7
auf das COM-Objekt zugreifen, ohne die konkrete Klasse zu kennen, mit deren Hilfe das Objekt programmiertechnisch erzeugt worden ist. COM-Schnittstellenzeiger Zeiger auf vtbl virtuelle Fkt.zeigerTabelle Methoden Implementierung Client der Schnittstelle vptr Fkt.1 Fkt.2 Fkt.3 vtbl HRESULT Funktion1() {... HRESULT Funktion2() {... Implementierung der Schnittstelle HRESULT Funktion3() {... Wie funktioniert COM genau? COM basiert auf dem Client- / Server-Prinzip. Der Server ist die COM-Komponente und der Client eine Anwendung, die diese Komponente nutzt. Da COM unterschiedliche Programmiersprachen unterstützt, kann man gewünschte Komponenten nicht einfach durch den Aufruf der Komponenten-Bezeichnung laden, da jede Programmiersprache ihre eigenen Aufruf-Konventionen hat. Um dieses Problem umgehen zu können, definiert der COM-Standard, dass jede Komponente einfach eine eindeutige Zahl als so genannte GUID (Globally Unique Identifier) erhält. Zahlen werden grundsätzlich von allen Programmiersprachen unterstützt. Dieser GUID in Form einer Zahl wird in der Registry des Windows-Betriebssystems abgelegt. Laut Standard sollte diese Zahl weltweit eindeutig sein, was durch entsprechende Generatoren realisiert wird. 8
GUIDGEN ist z.b. ein solcher Generator. (Unter:Start/Ausführen, Pfad von guidgen.exe reinschreiben c:/programme/c++/tools/guidgen.exe,toolt erscheint, Registry Format auswählen und copy) Der Generator generiert die GUID anhand der MAC-Adresse einer vorhandenen Netzwerkkarte, der Uhrzeit und des Datums. Falls keine Netzwerkkarte vorhanden ist im Rechner, lassen sich mit Hilfe von Systemparametern, die im BIOS abgelegt sind, Zahlen generieren. Man kann auch ohne den Generator GUIDs erstellen, die COM-API Funktion CoCreateGuid wird in das Betriebssystem eingebettet. //Zeiger auf die erzeugte Guid HRESULT CoCreateGuid( GUID * pguid); Somit ist sichergestellt, dass die Wahrscheinlichkeit für eine doppelt vergebene GUID gering ist (hängt natürlich auch vom Programmierer ab). Die GUID für Komponenten wird auch CLSID (ClassID) genannt. Es wird natürlich nicht nur die CLSID in der Registry abgelegt, sondern auch der Path zur entsprechenden Komponente in Form einer DLL oder EXE. Einer Anwendung, die diese Komponente nutzen möchte, muss die CLSID oder die so genannte ProgID der Komponente bekannt sein. Die ProgID ist eine Namensvergabe (eine besser lesbare Form für den Menschen) für die Komponente, die folgendermaßen aufgebaut ist: <Hersteller>.<Klasse>.<Version> Durch entsprechende API - Funktionen (API: Application Programming Interface), die von COM zur Verfügung gestellt werden, kann eine Anwendung nun eine Komponente 9
über die Registry und die bekannte CLSID laden oder über die bekannte ProgID die CLSID herausfinden. Damit ist es aber nicht getan, nicht nur die Komponente hat eine GUID (CLSID), sondern auch jede Schnittstelle, die man auch IID (InterfaceID) nennt. Somit müssen diese IIDs auch der Anwendung bekannt sein. Über die CLSID und IIDs kann die Anwendung die Schnittstellen der Komponente anfordern, welche dann als ein COM-Objekt zurückgeliefert werden. COM ist nicht an grafische Oberflächen gebunden, somit kann auch eine Konsolenanwendung auf das COM-System zugreifen. Windows-Registratur (Registry) Registry= eine Zentrale Informationsdatenbank innerhalb von Windows. Sie beinhaltet nahezu alle Informationen über die Konfiguration des Betriebssystems, wie etwa Einstellungen zur Anpassung der Oberfläche (Farben,Hintergrund,Cursorsymbole,etc.), Systemeinstellungen (Liste der Programme, die beim Systemstart auszuführen sind) und eben auch bestimmte Informationen über COM. Die Struktur der Windows-Registratur ist baumartig angeordnet, die Informationen liegen in so genannten Schlüsseln ( keys ), denen ein oder mehrere Attribute oder wiederum Unterschlüssel ( subkeys ) zugeordnet sein können. Blick in die Windows Registratur: Hauptschlüssel, Unterschlüssel und ihre Attribute: 10
Bsp. Abbildung von der CLSID von Word für Windows: Noch einiges zu den Begriffen COM-Server ist eine Binärdatei, die die Realisierung von COM-Klassen enthält und die Bereitstellung der Objekte für den Client (Erzeugen der Objekte, Zugriff über Schnittstellenzeiger bereitstellen) durchführt. Binärdatei nimmt die Rolle eines COM-Servers ein. Ist eine Windows-DLL oder ein unter Windows ausführbares Programm (EXE). Com-Komponente Es ist mit COM-Klassen nicht möglich, Objekte zu erzeugen. Zusätzlich zur COM- Klasse ist vom Entwickler Quelltext bereitzustellen, der erst zusammen mit der COM- Klasse und der COM-Laufzeitumgebung den kompletten Zyklus eines COM-Objektes abdeckt (Erzeugen und Löschen eines Objektes, Schnittstelle IUnknown implementieren, Zugriff auf die Methoden herstellen). Unter COM-Komponenten versteht man in Ergänzung einer COM-Klasse die komplette Programmumgebung (Binärdatei), die dem Betriebssystem (speziell der COM- Laufzeitumgebung) die Erzeugung von Objekten dieser Klasse ermöglicht. Die COM- Komponente kann entweder in Form einer DLL- oder als EXE-Datei realisiert werden. 11
COM-Client Windows-Programm veranlasst Erzeugung von COM-Objekten und ruft seine Methoden auf. COM-Komponente als DLL bzw. EXE Es gibt grundsätzlich zwei unterschiedliche COM-Komponentenarten. Den so genannten InProc-Server und OutProc-Server. Der InProc-Server wird in der Regel als DLL (Dynamic Link Library) realisiert. Ein InProc-Server arbeitet im gleichen Adressraum wie die Anwendung, die diesen als DLL einbindet. Trotz all dem ist der InProc-Server eine eigenständige Komponente der Anwendung. Der OutProc-Server ist ein selbstständig laufendes Programm, welches in Form einer ausführbaren EXE-Datei existiert. Da dieser Server ein eigenständiges Programm darstellt, besitzt dieser einen eigenen und somit von der Client-Anwendung abweichenden Adressraum. Damit der Server und die Client-Anwendung trotzdem miteinander kommunizieren können, stellt die COM-Laufzeitumgebung eine Transportdienstleistung der Parameter zur Verfügung. Dabei werden die Parameter automatisch in ein übertragbares Format konvertiert, dieses Verfahren nennt man Marshaling. COM ist so aufgebaut, dass die Client-Anwendung nicht genau wissen muss, ob es sich jetzt um einen InProc-Server oder einen OutProc-Server handelt. Das COM-Laufzeitsystem übernimmt die benötigten Konvertierungen und die Kommunikation. 12
COM-InprocServer 8. Zeiger 5.1 erzeuge Klassen-Obj. Fabrikmethode- Muster Client COM-Umgebung 5.2 Zeiger 6. erzeuge Objekt 1. anmelden 2. Obj. erzeugen 7. Zeiger Server 3. CLSID nachfragen 4. Pfadangabe DLL Registry 9. Methode aufrufen - DLL ist in der Registry registriert. - Client meldet sich bei COM an - COM-Objekt soll erzeugt werden - COM durchsucht Registratur nach entsprechender CLSID - COM Fkt. lädt die DLL in den Adressraum des Clienten - Server wird gestartet - Objekt der Klassenobjekt der Fabrik wird erzeugt - Schnittstellenzeiger auf die Klasse CFactoryImpl werden geliefert 13
IUnknown QueryInterface() AddRef() Release() implements IMathematik QueryInterface() AddRef() Release() implements Mathematik muli() Klassenfabrik CFactoryImpl extends IClassFactory CreateInstace() LockServer() implements QueryInterface() AddRef() Release() CreateInstance() LockServer() 1. COM- Umgebung Schnittstelle IUknown ist von jeder COM-Klasse zu implementieren und von jeder Schnittstelle zu erben. Methode QueryInterface() erwartet als Eingangsparameter die IID einer COM- Schnittstelle, als Ausgabeparameter liefert sie einen Zeiger auf diese Schnittstelle zurück, wenn das Objekt diese Schnittstelle implementiert. Funktionen, die einen Schnittstellenzeiger erzeugt haben und diesen über den Rückgabewert der Funktion an den Aufrufer zurückgeben, müssen vor der Rückgabe einmal AddRef() aufrufen. D.h. der Referenzzähler des Objektes wird um eins hochgezählt. Funktionen, die einen Schnittstellenzeiger mit Hilfe des Aufrufs einer zweiten Funktion entgegennehmen müssen Release() aufrufen, wenn sie den Schnittstellenzeiger nicht mehr benötigen. D.h. der Referenzzähler wird um eins erniedrigt. Methode der Schnittstelle IClassFactory LockServer() beschäftigt sich damit, ob ein COM-Server aus dem Speicher wieder entfernt werden soll. Wann soll COM die durch die CoLoadLibrary geladene InprocDLL wieder entladen? Zu diesem Zweck verwaltet COM pro InProcDLL einen Zähler, der besagt ob die DLL noch im Adressraum der Applikation geladen bleiben soll oder wieder entfernt werden kann. 1. COM erzeugt ein neues Objekt der Klassenfabrik CFactoryImpl. Diese liefert einen Schnittstellenzeiger der COM-Schnittstelle. Es ist uns jetzt möglich, Methoden der CFactoryImpl aufzurufen. Die Klasse CFactoryImpl kann Objekte von Mathematik erzeugen mit CreateInstance(). Nun können wir COM-Objekte der Schnittstelle IMathematik erzeugen. 14
Code Beispiel aus dem Client //Ein COM-Objekt der Klassenfabrik erzeugen. CoGetClassObject(CLSID_Mathematik, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&factory); //Durch die Klassenfabrik ein COM-Objekt der Schnittstelle IMathematik erzeugen. factory->createinstance(null, IID_IMathematik, (void**)&mathematik); Entwicklung einer COM-Komponente mit Visual C++ und Assistenten Im folgenden Beispiel wird ein InProc-Server mit Hilfe des ATL-COM-Anwendungs-Assistenten erstellt. Dazu muss zunächst ein neues Projekt erstellt werden, welches hier MyCOMServer heißt. Nachdem auf OK geklickt wurde, erscheint ein weiterer Dialog, in dem man den Server-Typ auswählen kann. Da wir einen InProc-Server erstellen möchten, muss hier Dynamic Link Library (DLL) ausgewählt werden. Anschließend kann man auf Fertigstellen klicken. 15
Am Ende erscheint nur noch ein Dialog welches eine Liste von Dateien anzeigt, die erstellt werden. Hier muss man nur mit OK bestätigen. Somit hat man das Grundgerüst des InProc-Servers erstellt, aber noch keine eigenen Schnittstellen, was folgen wird. Um eine eigene Schnittstelle (COM-Objekt) zu definieren, muss man in der Menüleiste von Visual C++ unter Einfügen im Kontextmenü den Punkt Neues ATL-Objekt... auswählen. Anschließend wird ein Dialog eingeblendet, in dem man die Art der Schnittstelle definieren kann. Da wir ein einfaches Beispiel erstellen möchten, sollte unter der Liste Kategorie der Punkt Objekte und unter der Auswahlbox Objekte der Punkt Einfaches Objekt ausgewählt sein. Danach einfach auf Weiter klicken. 16
Nun erscheint ein Dialog, in dem man die Eigenschaften des COM-Objekts definiert. Wenn man jetzt in das Eingabefeld Kurzbez. eine allgemeine Bezeichnung für die Schnittstelle angibt, werden alle anderen Felder entsprechend der Namenskonvention automatisch gefüllt. Wir haben uns entschieden, eine Schnittstelle mit der Bezeichnung Mathematik zu erstellen. Danach wieder auf OK klicken. I steht dabei für Interface also Schnittstelle. Wenn man jetzt links im Arbeitsbereich die Klassen-Ansicht wählt, kann man sich den grafischen Aufbau anschauen. Es wurden eine Klasse CMathematik und eine Schnittstelle IMathematik für die Klasse erstellt. 17
Nun wollen wir eine Operation erstellen, auf die wir durch unsere Schnittstelle zugreifen können. Dazu klicke man mit der rechten Maustaste auf IMathematik innerhalb des Knotens CMathematik. Im Kontextmenü muss man dann Methode hinzufügen... auswählen. Würde man stattdessen auf IMathematik innerhalb des Knotens MyCOMServer Klassen klicken und dort eine Operation erstellen, dann würde nur die Schnittstellen- Definition erstellt werden und nicht die Implementierung. Nun kann man der neuen Operation einen Namen geben und deren Parameter definieren. Wie man sieht, kann man keinen eigenen Rückgabeparameter angeben, was auch laut Definition von COM nicht erlaubt ist. Es muss immer ein HRESULT Rückgabeparameter angegeben sein, welcher anzeigt, ob die Operation erfolgreich war oder nicht bzw. welcher Fehler vorliegt. Dieser Rückgabeparameter wird durch den Assistenten automatisch angegeben. Wir wollen hier eine einfache Multi-Operation erstellen, welche die folgenden Parameter hat: double *erg: Hier wird das Ergebnis zurückgeliefert. 18
Da man keinen eigenen Rückgabe-Parameter definieren kann, muss die Rückgabe über Zeiger erfolgen. double zahl1: Erste Zahl, die wir multiplizieren werden double zahl1: Zweite Zahl, die wir multiplizieren werden Sobald man auf OK klickt, wird diese Operation in der Schnittstelle und als Implementierung definiert. In der Klassen-Ansicht des Arbeitsbereiches kann man das Ganze nachvollziehen. Die Multi-Operation mit dem pinken Quadrat davor ist die Implementierung und die Multi-Operation mit dem grünen Quadrat davor ist die Definition der Operation in der Schnittstelle (COM-Objekt). Wenn man jetzt auf die Implementierung der Multi-Operation (pinkes Quadrat) einen Doppelklick ausführt, dann wird der Quelltext angezeigt, den man entsprechend ergänzen kann, um die Funktionalität der Operation zu programmieren. Wie man sieht, wurde automatisch die Rückgabe S_OK definiert. Diese Rückgabe meldet, dass die Operation erfolgreich war. Mit E_FAIL kann man melden, dass die Ausführung fehlgeschlagen ist. 19
Es gibt noch viele andere vordefinierte Rückmeldungen, aber dies ist für das Verständnis erst mal nicht relevant. Der Quelltext unserer einfachen Multi - Operation kann nur Potenzen mit positiven Exponenten verarbeiten und sieht folgendermaßen aus. Nachdem man das Projekt nun kompiliert, wird die DLL des InProc-Servers erstellt und dieser automatisch in der Registry registriert. Somit ist der InProc-Server fertig. Im folgenden Bild ist die grobe Struktur unseres InProc-Servers in der Registry zu sehen. Dies sind die wichtigsten Eintragungen, jedoch macht Visual C++ noch zusätzliche Angaben. 20
Client zur Nutzung des InProc-Servers Auf einen Server greift ein Client zu, welchen wir jetzt erstellen. Dazu muss sich der Client zuerst beim COM-Laufzeitsystem anmelden, um den Zugriff auf die Komponenten zu erhalten, was mit der Funktion CoInitialize erfolgt. CoInitialize(NULL); Nach der erfolgreichen Anmeldung fordert der Client eine so genannte Klassenfabrik des angegebenen Servers an, was mit der Funktion CoGetClassObject realisiert wird. CoGetClassObject(CLSID_Mathematik, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&factory); Diese Funktion erhält als Parameter die GUID des Servers CLSID_Mathematik und die vordefinierte GUID der Klassenfabrik IID_IClassFactory. Der Parameter CLSCTX_INPROC_SERVER gibt an, dass es sich um einen InProc- Server handelt. Zurückgeliefert wird ein Zeiger auf das erstellte COM-Objekt der Klassenfabrik. 21
Mit der Klassenfabrik kann man nun weitere gewünschte COM-Objekte erstellen, dazu stellt diese eine Operation mit der Bezeichnung CreateInstance zur Verfügung. factory->createinstance(null, IID_IMathematik, (void**)&mathematik); In unserem Beispiel fordern wir die in unserem InProc-Server zuvor definierte Schnittstelle IMathematik an. Bei Erfolg wird ein COM-Objekt dieser Schnittstelle geliefert. Nun kann man mit diesem COM-Objekt wie mit einem Objekt einer gewöhnlichen Klasse arbeiten, also z.b. Operationen ausführen. Wenn die COM-Objekte nicht mehr benötigt werden, müssen diese mit der Operation Release freigegeben werden. In unserem Beispiel ist es das Objekt mathematik. Wenn keine Objekte mehr erstellt werden sollen, muss auch die Klassenfabrik factory durch ein Release freigegeben werden. mathematik->release(); factory->release(); Am Ende jeder Client-Anwendung muss noch eine Abmeldung vom COM- Laufzeitsystem erfolgen, was mit der Funktion CoUninitialize erfolgt. CoUninitialize(); Hier nochmal die Client-Anwendung im Ganzen. //Das Importieren der Header-Datei des Servers ist nötig, //da diese die GUIDs der Komponente und deren Schnittstellen enthält. #include "..\MyCOMServer\MyCOMServer.h" #include <iostream.h> #include "..\MyCOMServer\MyCOMServer.h" #include <iostream.h> void main() { IClassFactory IMathematik HRESULT double *factory; *mathematik; hr; ergebnis, zahl1, zahl2; //Anmeldung bei COM für den Zugriff auf das COM-Laufzeitsystem. CoInitialize(NULL); //const CLSID CLSID_Mathematik = {0x0BB7A368,0x80AF,0x4374,{0xB1,0x34,0xC4,0x9C,0x62,0xC9,0x53,0xA5; //const IID IID_IMathematik = {0xB7DF317F,0x9C28,0x4480,{0x83,0x3B,0xC7,0x26,0xE3,0xB6,0xDC,0xDD; 22
// IMathematik *mathematik = NULL; // hr = CoCreateInstance(CLSID_Mathematik, NULL, CLSCTX_INPROC_SERVER, IID_IMathematik, (LPVOID *) & mathematik); //Ein COM-Objekt der Klassenfabrik erzeugen. CoGetClassObject(CLSID_Mathematik, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void**)&factory); //Durch die Klassenfabrik ein COM-Objekt der Schnittstelle IMathematik erzeugen. factory->createinstance(null, IID_IMathematik, (void**)&mathematik); //Benutzereingabe. cout <<"Zwei Zahlen miteinander multiplizieren.\n\n"; cout <<"Bitte geben Sie eine Zahl ein: "; cin >>zahl1; cout <<"Bitte geben Sie noch eine Zahl ein: "; cin >>zahl2; //Multiplikation. hr = mathematik->multi(&ergebnis, zahl1, zahl2); //Ausgabe des Ergebnises. if(succeeded(hr)) { cout <<"\ndas Ergebnis lautet: " <<ergebnis <<"\n\n"; else { cout <<"\ndie Zahlen konnten nicht multipliziert werden.\n\n"; //COM-Objekte wieder freigeben. mathematik->release(); factory->release(); //Abmeldung bei COM. CoUninitialize(); 23
Zusammenfassung COM ist also ein Verfahren für den Datenaustausch zwischen verteilten Objekten. Die proprietäre Spezifikation von der Firma Microsoft beschreibt Software- Komponenten für neu zu schreibende oder bereits bestehende Programme. COM- Komponenten sind für den Einsatz unter den Windows-Betriebssystemen von Microsoft konzipiert. Sie werden meist in der Programmiersprache C++ geschrieben, können aber auch in einigen anderen Programmiersprachen entwickelt werden. COM definiert eine Software-Schnittstelle, durch die COM-Komponenten Daten miteinander austauschen. Sie ermöglicht es beliebigen Komponenten, miteinander zu kommunizieren, egal in welcher Programmiersprache die Komponenten geschrieben wurden. Dank der Durchschaubarkeit des Speicherortes spielt es für den Programmierer, der eine Komponente schreibt, keine Rolle, ob sich die übrigen Komponenten zum Beispiel in einer DLL, in einer lokalen EXE-Datei oder auf einem anderen Netzwerk-Rechner befinden. So braucht der Programmierer auch keine Veränderung an seiner Komponente vorzunehmen, wenn andere Komponenten "umziehen", also an anderem Ort im Computer-Netz gespeichert werden. 24
Literaturverweise Buch von Peter Loos; GoTo COM InternetSeiten: http://www.informatik.uni-bonn.de/~ur/lectures/ss2002/folien/1 http://www.codeproject.com/com/hellocom.asp http://www.fim.uni-linz.ac.at/diplomarbeiten/diplomarbeit_emsenhuber/thesis.pdf 25