Eine Spezifikation. COM liefert ein Standardmodell zum Integrieren von
Komponenten. Es folgt dem Trend eine Anwendung in mehrere wieder verwendbare
Komponenten aufzuteilen.
Ziel von COM :
Sprachunabhängigkeit - z.B. kann ein VB-Client eine
C++ Komponente aufrufen. Ein weiteres Ziel ist die Ortsunabhängigkeit: die
Ansteuerung der Komponente ist für den Client immer gleich, egal wo sich die
Komponente befindet.
Sie kann sich innerhalb des Client-Prozesses befinden als Dll, innerhalb
eines anderen Prozesses auf dem Client Rechner, als getrenntes Exe-File.
Oder sie befindet sich auf einem anderen Rechner als Remote Komponente.
Eine Komponente kann als Dll oder als EXE File implementiert werden. Hierin
enthalten sind ein oder mehrere COM-Objekte.
Das COM Objekt enthält die Methoden, die der Benutzer über einen Zeiger auf ein
Interface (Schnittstelle) aufruft.
Das Com-Objekt exportiert seine Funktionalität in Form eines Interfaces. In C++
wird einInterface über virtuelle abstrakte Methoden realisiert - Polymorphismus
lässt grüßen.
Was ist Polymorphismus ?
Zum einen virtuelle Funktionen, die im COM-Objekt
implementiert werden müssen. Zum anderen auch der Zugriff vom Client stets über
einen Basisklassen Zeiger.Die Basisklasse ist hier das Interface. Das ist auch
der Grund, warum in COM
grundsätzlich der Zugriff auf eine Funktionalität im COM-Objekt nur über einen
Interface Pointer erfolgt.
Wie wird das alles in C++ implementiert ?
In welchen Sprachen können COM-Objekte entwickelt und verwendet werden ? In den Sprachen, die es erlauben Funktionen über Funktionszeiger anzusprechen d.h. in allen in Sprachen, außer in Scriptsprachen, dafür gibt es aber auch einen Weg.
Was ist ein Interface ?
Ganz allgemein eine Gruppierung von Funktionen für
eine bestimmte Aufgabe. So ermöglicht das Standard Interface IStream das
Schreiben in einen Stream und das Lesen daraus. Diese Funktionalität kann in
einem eigenen COM-Objekt
implementiert werden. Clients können dadurch in einer standardisierten
Weise auf das COM-Objekt zugreifen.
Ein
einmal festgelegtes Interface darf nicht mehr geändert werden, die VTable darf
sich nicht ändern.
Was sind GUID‘s : 128 Bit breite Zahlen, sie werden
verwendet um Interfaces, Klassen und Type - Libraries eindeutig zu
identifizieren.
Sie werden in C++ als Struktur verwendet ( Es gibt keinen
128 Bit Datentyp).
Jedes COM-Objekt braucht eine IUnknown Interface Implementierung.
Über die
enthaltenen Methoden AddRef() und Release() wird die Lebensdauer des
COM-Objekts gesteuert, über QueryInterface(..) wird nach weiteren Interfaces
gefragt.
Was ist ein COM-Server ?
Eine Komponente, eine DLL oder ein EXE File, die eine
oder mehrere COM Objekte enthält .
Der Ort des Servers sollte für den Client keine Rolle spielen, für den Client
ist der Aufruf immer "InProcess". Dadurch ist der Aufruf immer gleich. Bei
einem EXE-Server ist kein direkter Zeiger möglich, dann erhält der Client einen
Zeiger auf einen Proxy.
Wie instantiiert der Client die COM-Objekte innerhalb des COM-Servers ?
Über
eine ClassFactory. Diese ist selbst ein COM-Objekt und muss im COM-Server
implementiert werden. Da Sprachabhängigkeit erwünscht ist, wrappt die
ClassFactory den "new" Operator. Wie ist der genaue Vorgang ?
Die CLSID‘s aller COM-Objekte eines COM-Servers müssen in die Registry eingetragen werden. Als Minimum ist der Ort des Servers einzutragen: Die CLSID des COM-Servers wird unter HKEY_CLASSES_ROOT\CLSID eintragen.
Wie findet COM den gewünschten Server ? Wie ist der genaue Vorgang der Aktivierung ?
Wir steuern einen InProcess COM-API Server mit einem Console C++ Client an.
Es gibt 2 Verfahren zur Implementierung von COM-Objekten mit mehreren
Interfaces. Multiple-Inheritance (Mehrfachvererbung), diese wird auch von der
ATL verwendetdazu wird das COM-Objekt von allen Interfaces abgeleitet.
Alle Methoden aller Interfaces müssen dann im COM-Objekt implementiert
werden. Vorteil :Code ist intuitiver, auch performanter.
Nachteil:
Namenskonflikte bei gleichen Methoden in unterschiedlichen Interfaces, sie sind
eher selten aber lösbar.
Embedding mit Nestet Klassen, so werden
Interfaces in der MFC implementiert.
Vorteil: keine Namenskonflikte
Nachteil: Code ist gewöhnungsbedürftig. Für jedes Interface wird eine eigene
Klasse erstellt.
Wie unterstützt uns die ATL bei der Programmierung von COM-Objekten und Servern
?
Welche Teile werden dabei von der ATL übernommen ? Die Projekt-Struktur.
Was können die Wizards ? Was können sie nicht ?
Die beiden Wizards:
Der
ATL COM AppWizard erstellt das Housing, d.h. die Dll oder das Exe File.
Der ATL Objekt Wizard erstellt COM-Objekte.
Was liefert uns die ATL ?
Support für Custom Interfaces , eine
Implementierung für duale Interfaces. Eine drastische Vereinfachung zur
Implementierung von Connection Points für das Eventhandling in COM.
Eine
Implementierung von IUnknown, IClassFactory und IDispatch. Support für wieder
verwendbare Objekte in COM über Aggregation.
Error Support mit der ATL. Alle
Thread Modelle werden unterstützt. Code ist optimiert.Ein Script für die
Registrierung wird erzeugt.
Was sind die wichtigsten ATL-Klassen ?
Wie schaut die Klassenableitung aus ?
Die ClassFactory der ATL erstellt ein
CComObject oder ein CComAggObject.
Hier ist IUnknown für unsere Klasse
implementiert, dieses Objekt stellt auch die VTBL zur Verfügung.
Die
IUnknown Implementierung muss sich in der am weitesten abgeleiteten Klasse
befinden, das bestimmt die Mehrfachvererbung, daher wird unsere Klasse noch
einmal abgeleitet.
Das erste Interface bekommen wir mit dem ATL Objekt Wizard beim Erstellen des
COM-Objekts. Weitere Interfaces fügt man am besten in der IDL von Hand hinzu .
Wie ist die Vorgehensweise bei Custom Interfaces, wie bei den Standard
Interfaces beispielsweise bei einem IStream oder IPersist Interface ?
Wie werden GUID's erzeugt ?
Außerdem gibt es noch einen weiteren Wizard,
den Implement Interface Wizard. Implementiert der denn schon unsere Interfaces ?
Dem ist leider nicht so.
SmartPointer:rtPoinDas
"Buchführen" der Interface Zeiger mit AddRef() und Release() ist gerade in
tief verschachteltem COM-Code nicht mehr trivial. SmartPointer können unseren
Code stark vereinfachen. Sie sind Klassen, die einen Interface Zeiger kapseln.
Durch geschickte Operator Überladung kann man sie verwenden wie normale
Interface Zeiger.
Verstehen wir die Anwendung des IUnknown Interfaces an sich und wissen wir,
wann ein
QueryInterface(..) erfolgen muss, dann macht uns das Verwenden der
SmartPointer auch keine Probleme, denn auch sie haben ihr Eigenheiten. Auch die
ATL verwendet SmartPointer, sie hat dafür eigene Klassen.
Was bekommen wir noch ? Wrapper Klassen. KlaWrapper Klassen werten den HRESULT der Methoden der COM-Komponente aus und werfen beim Client eine Exception. Es gibt in COM kein durchgängiges Exception Konzept vom Server wie z.B. bei CORBA.
Wrapper für Propertys: s:
Ursprung hat das in Visual Basic, wo
man Propertys schon lange kennt.
Propertys sind public
Zugriffsfunktionen für private Variable im COM-Objekt.
Sie werden im Client
wie ein Variablen Zugriff verwendet z.B. ohne Klammern.
Es gibt jede Menge Makros, die man nicht alle im Detail kennen muss, eine
Zuordnung
sollte aber möglich sein.
Ausgelöst wird der Native COM Support durch eine Import Anweisung,
wir erhalten 2 Files im Debug Verzeichnis der Client Anwendung.
Die ATL unterstützt Fehler Meldungen im Server. Das erfolgt für jedes Interface
getrennt.
Die Fehlermeldung wird einem Error Objekt übergeben.
Die
Implementierung erfolgt in CComCoClass. Sie wrappt das Interface
ICreateErrorInfo
mit 6 überladenen Funktionen.
Das erfordert, daß
der Server ISupportErrorInfo implementiert. COM wertet das aus und
wirft beim Client eine Exception.
Die Fehler Unterstützung erfolgt für jedes Interface getrennt.
Es gibt Sprachen, die keinen Zugriff auf virtuelle Methoden Tabellen (VMTs)
haben.
Wie ist es aber möglich, dass Anwendungen wie der Internet Explorer
AktiveX Controls ansteuern können, diese sind ja auch COM-Objekte und sehr
komplexe dazu. In COM wird aber immer nur mit Interfaces gearbeitet !
Die Lösung: Der Internet Explorer hat dazu ein ganz spezielles Interface
implementiert, das Interface IDispatch. Über dieses Interface kann jedes
COM-Objekt
angesteuert werden, sofern es auch dieses IDispatch Interface
implementiert hat.
Über dieses Interface können wir somit ein COM-Objekt nach einer bestimmten
Methode fragen. Ist die Methode vorhanden, erhalten wir eine Dispatch ID für
einen
anschließenden Aufruf.
Dispatch Interfaces sind von natur aus erheblich langsamer als Custom Interfaces.
Die Lösung:
Das duale Interface ist eine Kombination eines Custom Interfaces
mit einem
IDispatch Interface. Die ATL stellt dafür eine Implementierung zur
Verfügung.
Somit können "VTBL-Clients" auf das schnelle Custom Interface
zugreifen,
"IDispatch-Clients" auf das langsamere IDispatch Interface.
Eigentlich ideal, aber wir sind in den Datentypen eingeschränkt: Es stehen uns
nur die
Automations-Datentypen zur Verfügung, also keine Strukturen etc.
Es
gibt noch einen weiteren Nachteil. IDispatch Clients können keinen
QueryInterface
machen, dafür gibt es aber einen Work Arround.
Objektorientierte Sprachen ermöglichen die Vererbung auf Sourcecode -
Ebene.
COM erlaubt die Wiederverwendung von binären Objekten.
Dafür
gibt es 2 Arten:
COM-Containment und COM-Aggregation
Wie sieht
die Implementierung dazu aus ?
Wie funktionieren Events in COM, aus der Sicht von Client und Server ?
Wie erzeugt ein COM-Objekt Events mit Custom- und Dispatch-Interfaces ?
In COM werden dazu Connection Points verwendet.
Einführung in die Remoting Infrastruktur bei COM.
Marshaling mit der Proxy /
Stub Dll
BSTR, der Datentyp für Strings in COM
Speicherhandling bei der Übergabe von Daten zwischen Client und Server
Wer legt an ? Wer gibt frei ?
Übergabe von Strukturen
Übergabe von Arrays
Übergabe von Zeiger
COM und mehrere Threads.
Die Thread-Modelle MTA und STA
OutOfProcess Server
Szenarios:
STA Client und STA Server
Mixed Modelle
MTA
Client und MTA Server
Worker Threads verwenden COM
Der FTM