Die Community zu .NET und Classic VB.
Menü

COM als Middleware - Seite 7

 von 

2.2 Instanzierung von Objekten, IClassFactory
Nächste Seite >>
2.0 COM Server
<< Vorherige Seite

2.1 Interfaces, schnittstellenbasierte Programmierung  

Das tragende Paradigma unter COM ist das Konzept der schnittstellenorientierten Programmierung. Eine Schnittstelle [i.F. auch Interface] ist eine leere, virtuell definierte Klasse, also eine Hülle zu semantisch zusammengehörigen Funktionsprototypen ohne jede Codeimplementierung. Eine solche Klasse trägt die Bezeichnung abstrakte Klasse. Deren weitere Reduzierung, bedingt durch den Verzicht auf Datenelemente, ist als rein abstrakte Klasse benannt [s. Kapitel 2.1] Die einzige Möglichkeit einen solchen Klassentypen zu verwenden bietet die Vererbung.

Im COM-Sinne hat ein Interface die Definition der rein abstrakten Klasse zu erfüllen. Syntaktisch bieten Visual Basic und C++ keine unmittelbaren Konstrukte, ein solches Interface zu erstellen. Beide Entwicklungsumgebungen kennen entgegen der Programmiersprache Java, bei der die rein abstrakte Klasse zum elementaren Entwurfselement gehört, nur das gemeine Klassenmodul. C++ adaptiert Interfaces durch virtuelle Standard-Klassen ohne Code- und Datenimplementierung. VB hingegen ermöglicht deren Erstellung mit Hilfe eines konfigurierbaren Klassenattributs, das für eine solche Schnittstelle die Eigenschaft PublicNonCreateable vorsieht und somit eine Instanzbildung durch den New-Operator verhindert. Wie bei C++ entfallen definitionsgemäß auch hier sämtliche Implementierungen. Die Einbindung einer abstrakten Klasse erfolgt unter VB durch das Schlüsselwort Implements, ihre Nutzung beschränkt sich auf den Aspekt der Polymorphie.

Die Grundidee des COM-Interfacekonzeptes dient dazu, Schnittstellen zwecks eines systemweiten Kommunikationsvertrages zu manifestieren und die Implementierung des zugehörigen Codes an anderer Stelle kontextbezogen vorzunehmen, ohne dass die Schnittstelle selbst nochmals zu übersetzen ist. Das Format einer Schnittstelle liegt stets in binärer Form vor und ist damit unveränderlich.

Insgesamt definiert Microsoft rund 60 sogenannter Standardinterfaces, welche die COM Architektur verkörpern, respektive unterstützen. Weitere ins System eingebrachte Schnittstellen von Drittherstellern [Third Party] definiert Microsoft als Custominterfaces, auch als Userdefined Interface bezeichnet.

2.1.1 Die Standardschnittstelle IUnknown

In einem Komponenten-Framework lassen sich die Instanzen öffentlicher Klassen eines Servers beliebig erstellen sowie weitergeben. Es bedarf daher eines Mechanismus, der die Referenzen eines Objektes zählt und so verwaltet, dass es sich bei Erreichen des Wertes Null entsprechend aus seinem allokierten Speicherbereich entfernt bzw. selber terminiert.

COM-Objekte dürfen mehrere Schnittstellen besitzen, müssen aber mindestens den geschützten, also nicht überladbaren COM-Grundtyp IUnknown implementieren. Jedes COM-Interface ist aus IUnknown abgeleitet. Jede von IUnknown verschiedene Schnittstelle lässt sich nur über IUnknown selbst referenzieren. Die Grundvoraussetzung für die Referenzierung eines Objekts ist ein Zeiger auf diese Schnittstelle. IUnknown verwaltet über drei Methoden die Lebensdauer eines Objektes sowie den Verzeichnisdienst zur Abfrage weiterer beinhalteter Schnittstellen:

AddRef Jedes COM-Objekt enthält einen Referenzzähler, der die Instanzen des betroffenen Objekts summiert. Der Zähler inkrementiert sich bei jeder neuen Referenz.
Release Bildet das Gegenstück zu AddRef und dekrementiert bei jeder freigegebenen Instanz den Referenzzähler. Erreicht der Zähler nach einer Dekrementierung den Wert Null, so wird das letzte Objekt zerstört und aus dem Speicher entfernt. Hieraus resultiert die notwendige Freigabe von Instanzen. Erfolgt diese nicht, so belegt das Objekt überflüssigerweise Speicher. Dies kann bei häufiger Instanzierung von großen Objekten rasch zu Speicherproblemen führen.
QueryInterface Diese Methode erwartet als ersten Parameter allgemein eine GUID [s. Kapitel 2.2.1], im speziellen den Interface Identifier [IID] einer Schnittstelle und speichert im zweiten einen Zeiger auf diese. Der Rückgabewert der Funktion gibt mit einem Wert ungleich failed Auskunft über den Erfolg des Aufrufs. Mittels dieser Methode kann der elementare Dienst angeboten werden, weitere in einem Objekt enthaltene Schnittstellen zu ermitteln, deren Anzahl statisch ist. Das Ergebnis eines identischen Aufrufs ergibt stets den selben Zeiger [Reflexivität]. Ermittelt der erste Aufruf eine untergeordnete Schnittstelle, so ist durch diese gleichermaßen die übergeordnete zu erfahren [Symmetrie]. Wenn der Aufrufer aus einer ersten eine zweite Schnittstelle erfährt und aus der zweiten eine dritte, dann ist auch die Möglichkeit gegeben aus der ersten die dritte direkt zu erhalten. [Transitivität]. Bei jeder erfolgreichen Operation auf QueryInterface erhöht sich automatisch auch der Referenzzähler über den internen Aufruf der Methode AddRef.

IUnknown beschreibt lediglich eine Schnittstelle. Dies bedeutet, dass die eigentliche Codeimplementierung vom Entwickler vorzunehmen ist. Das folgende Listing zeigt die sinngemäße C++ Umsetzung:

STDMETHODIMP_(UOLONG)
CMyClass::AddRef ()
{
        m_dwRefCount++;
    return m_dwRefCount;
}
// ------------------------------------------------------
STDMETHODIMP_(UOLONG)
CMyClass::Release ()
{
    m_dwRefCount--;
        If (m_dwRefCount == 0)    { delete this; }
    return m_dwRefCount;
}
// ------------------------------------------------------
STDMETHODIMP
CMyClass::QueryInterface (REFIID iid, void **ppv)
{
    If (ppv == (void **) 0) { return E_INVALIDARG; }
        If (IsEqualIID (iid, IID_IUnknown))
        { *ppc = static_cast <IUnknown*> (this); }
    Else If (IsEqualIID (iid, IID_Interface1))
        { *ppc = static_cast <IID_Interface1*> (this); }
        reinterpret_cast <IUnknown*> (*ppv)) -> AddRef;
    return S_OK;
}

Der Visual Basic Compiler automatisiert während des Übersetzungsvorganges diese Implementierung, so dass sie für den Entwickler transparent bleibt.

Trotz der verfügbaren Mechanismen zur Referenzzählung und Terminierung über die IUnknown-Methoden, ist eine umfassende Verwaltung der Lebensdauer eines Objekts nicht zwangläufig gewährleistet. Verweist eine Instanz auf sich selbst oder übergibt Server A einen Verweis seiner Instanz an Server B und umgekehrt, so erreicht der Referenzzähler der betroffenen Komponente ohne explizites Einwirken niemals den Wert Null. Das Objekt bleibt damit unzerstört. Ein solcher Zustand nennt sich zirkuläre Referenz. Im ungünstigsten Fall zerstört das Betriebssystem ein solches InProc-Server-Konstrukt spätestens bei Beendigung dessen Clientprozesses. Von Tragweite sind zirkuläre Referenzen hingegen bei OutProc-Servern, da sie als eigenständige Anwendungen ohne äußeres Einwirken nicht terminierbar sind. Grundsätzlich sollte der Entwickler also stets die ordnungsgemäße Löschung aller erstellten Referenzen explizit berücksichtigen.

2.1.2 VTables

Eine VTable, gelegentlich auch als ViTable bezeichnet, [engl.: Virtual Function Table] beschreibt ein eindimensionales Array aus Pointern, dessen Elemente je eine Methode eines Objekts referenzieren. Ein VTable ist eine statische Ressource einer Klasse. Dies bedingt, dass alle Instanzen der Klasse die gleiche Tabellenstruktur nutzen.

Das virtuelle Attribut einer Methode deutet an, dass die Prozedur in einer neuen, abgeleiteten Klasse überschreibbar ist. Zur Gewährleistung eines solchen Konzeptes, darf mit den Instanzdaten einer Klasse nicht direkt auf die implementierte Funktionalität derselben verwiesen werden; vielmehr muss die Referenzierung dann zwingend dynamisch über die VTable erfolgen. Erst das Vorhandensein dieser Tabelle ermöglicht überhaupt den Zugang auf eine neue überschriebene Methode. Ohne einen solchen Verteiler würde ein Aufruf direkt auf die ursprüngliche Basisklasse zielen.

Die Reihenfolge der in einer VTable enthaltenen Funktionspointer ist konstant. Bei Ableitung zu einer neuen Klasse durch einfache Vererbung, entsteht auch eine neue VTable. Ergänzte Methoden fügen sich der VTable der ursprünglichen Basisklasse an. Einträge überschriebener Methoden hingegen werden in der neuen Tabelle gegen Zeiger auf die ersetzten Funktionen der abgeleiteten Klasse ausgetauscht.


Abbildung 5: VTable einer instanzierten, aus A abgeleiteten Klasse B

Das COM-Konzept der VTables entspricht der gleichnamigen in C++ verwendeten Struktur. Der Zusammenschluss aus der abgeleiteten Standardschnittstelle IUnknown und der VTable bilden als Einheit ein COM-Interface [ebenfalls unter der Bezeichnung Custominterface oder Userdefined Interface geläufig]. IUnknown ermöglicht das Ansprechen enthaltener Schnittstellen; die nachfolgenden Zeiger erlauben die Referenzierung der Methoden der COM-Komponente. Erfolgt die Bindung einer Komponente über ihr Custominterface, so enthält ein nativer Client harte Referenzen auf die VTable. Diese Bindungsart heißt Early Binding und bedingt die zwingende Einhaltung der Methodenreihenfolge [Interface Signature oder auch Binary Contract genannt]. Die Änderungen des Schnittstellenaufbaus eines öffentlichen Interfaces führt unmittelbar zur Inkompatibilität aller auf dieses Interface verweisenden nativen Anwendungen.


Abbildung 6: Aufbau eines COM-Interfaces

Da COM-Schnittstellen die Sprachenunabhängigkeit garantieren, existiert neben einer strikten Einhaltung der Methodenreihenfolgen auch eine Konvention zur Übergabe von Parametern. Diese Festlegung ist notwendig, um einen gemeingültigen Standard für den Auf- und Abbau des Stacks [Stack Maintenance Responsibility] zu gewährleisten. Die Übergabesequenz [Argument Passing Order] der Parameter verläuft vergleichbar mit der der Windows API, von rechts nach links. Rückgabetyp einer Schnittstellenmethode ist zwingend ein Wert vom Typ HRESULT. Die Namenskonvention für Schnittstellen schreibt ein führendes, großes I vor.

Visual Basic unterstützt neben dem Custominterface einen weiteren Interface-Typen, die Dispatchschnittstelle [s. Kapitel 2.1.6].

2.1.3 Interface Definition Language [IDL]

IDL definiert eine eigenständige an C angelehnte Standardsprache zur Schnittstellenbeschreibung. Konzeptionell ist die ursprünglich von der OSF entwickelte IDL zu CORBA mit der von COM identisch. Syntaktisch sind beide jedoch zueinander inkompatibel. Erst ein IDL-Standard erreicht eine sprachenunabhängige Interoperabilität zwischen verschiedenen Schnittstellen. Jede COM-Schnittstelle sollte diese Vorgabe zum Ziele einer uneingeschränkten Schnittstellen-Kommunikation einhalten. IDL legt den Schnittstellentyp, die Funktionsprototypen der Methoden sowie Größe und Art einer Auswahl elementarer Datentypen einheitlich fest und garantiert somit eine konsistente Schnittstellenarchitektur. IDL lässt folgende Schnittstellentypen zu:

CoClass Beschreibt eine Komponente, auch COM-Klasse genannt [bezeichnet durch ihre CLSID] mit allen implementierten Schnittstellen und dem zugehörigen Code.
Interface Beschreibt ein durch eine IID bezeichnetes Custominterface [VTable] einschließlich seiner Methoden und Parameter.
Dispinterface Beschreibt die durch eine IID bezeichnetes Dispatchschnittstelle einschließlich seiner Methoden und Parameter.
Module Beschreibt ein DLL-Modul [durch seinen Dateinamen bezeichnet] einschließlich der Namen und Ordinalzahlen seiner Funktionen und öffentlichen Variablen.
Typedef Beschreibt eine benutzerdefinierte Struktur, Enumeration oder Union, bezeichnet durch einen eindeutigen Namen oder optional durch eine GUID.

IDL-Code, respektive ihr Visual Basic Pendant ODL, wird als Textdatei erstellt und abschließend kompiliert. Sie prägt damit einem Binärstandard. Hieraus folgt, dass jede COM-fähige Programmiersprache über die Fähigkeit verfügen muss, IDL mindestens zu verstehen und optional auch zu erstellen. Damit eine Komponente die für sie typische systemweite Verfügbarkeit erfährt, ist die durch IDL definierte Schnittstelle in die Registry einzutragen [s. Kapitel 2.6.3].

[
  odl,
  uuid(31B09710-EADC-11CD-B9F7-00AA004753B5),
  hidden,
  dual,
  nonextensible,
  oleautomation
]
interface _ItemsSelected : IDispatch {
    [id(00000000), propget, helpcontext(0x0001a919)]
    HRESULT Item(
                    [in] VARIANT Index,
                    [out, retval] long* pRet);
};

Trotz des COM-Paradigmas sind nicht alle komponentenfähigen Programmiersprachen bedingungslos zueinander kompatibel. So unterstützt nicht jede Sprache sämtliche durch IDL angebotenen Datentypen. Dies hat zufolge, dass Schnittstellen die bekannte Datentypen der einen Sprache enthalten, sich für eine andere als ungültig darstellen. Das komplette Interface gilt dann fehlerintolerant als bedeutungslos und ist dadurch nicht implementierbar. C++ unterstützt hierbei die meisten der definierten IDL-Typen.


Abbildung 7: Auszug unterstützter IDL-Datentypen in C++ und Visual Basic

Besondere Bedeutung ist Typen wie String, Array und Boolean beizumessen. Sie erfahren eine entsprechende COM-spezifische Variant-Umsetzung in Form von BSTR, SAFE-ARRAY sowie BOOL und werden als Automationstypen bezeichnet. In IDL zu includierende, weitere Schnittstellen lassen sich über eine Importdirektive einbinden. Das Interface selbst ist dabei durch einen GUID Untertypen, der IID, eindeutig gekennzeichnet. Es muss stets von der Schnittstellenbasis IUnknown oder einer bereits IUnknown implementierenden Schnittstelle abgeleitet sein. Der in HRESULT abgelegte Rückgabewert einer Methode enthält nach Aufruf den Fehlerstatus [s. Kapitel 2.6.1].

MIDL [Microsoft Interface Definition Language] ist ein Compiler, der die Syntax eines in IDL verfassten Quelltextes prüft und ihn abschließend in eine binäre Ressource übersetzt. Die direktivenreiche MIDL versteht bei 50 Aufrufparametern rund 140 Schlüsselwörter und vereint die Semantik der Microsoft-ODL mit der OSF-IDL. Neben dem neueren, überarbeiteten MIDL-Compiler findet sein älteres ODL-Pendant MKTYPLIB ebenfalls noch Verwendung. Beide sind im Lieferumfang des Visual-Studios enthalten. Über die reine Schnittstellenbeschreibung hinaus stehen dem Entwickler mit MIDL weitere wichtige Funktionalitäten zur Verfügung:

  • COM-gerechte Kapselung, beispielsweise die Verpackung prozeduraler APIs
  • Individuelles Prototyping von Methoden
  • Definition spezifischer Funktionsparameter [z. B. dynamische Speicherverwaltung]
  • Kommentierungsmöglichkeit sämtlicher Definitionen für z. B. Objektbrowser
  • Erstellung von COM-Klassen [Komponenten]
  • Erstellung von Typenbibliotheken

Der Aufruf von MIDL generiert aus dem entsprechenden IDL-Quelltext eine Typenbibliothek und eine Make-Datei sowie sämtliche für eine Komponente benötigten C- und Headerfiles. Hierzu gehören neben den Standarddefinitionen der Schnittstellen auch C-Quelldateien zur konformen Errichtung von Proxy- und Stub-Servern für den prozessübergreifenden Zugriff [s. Kapitel 2.5]. Das Tool NMAKE erstellt letztendlich aus der Make-Datei des MIDL-Compilers die eigentliche Komponente, die es in Folge über den Registrierungsserver REGSVR32 im System anzumelden gilt. Unter den beiliegenden Beispielen befindet sich ein kurzer Code der die Manipulation eines bestehenden InProc-Servers anhand einer neuen Schnittstellendefinition aufzeigt [Samples\IDL].

2.1.4 TypeLibraries, Typenbibliotheken

Komponenten, die IDispatch implementieren, können optional ihre Schnittstellen in Bibliotheken, den sogenannten TypeLibraries exportieren. Optional deswegen, da das zwingende Vorhandensein einer solchen Bibliothek kontextabhängig ist. Sie ist nur dann eine unumgängliche Bedingung, falls ein Client die OLE-Automation erwartet, bzw. nur für diese Form des Zugriffs ausgelegt ist. Da dies aber in den meisten Fällen nicht auszuschließen ist, empfiehlt sich stets das Anlegen dieser Bibliothek.

Erlaubte Extensionen für die Extraktion sind hierbei OCX, DLL, OLB, TLB oder EXE. Eine TypeLibrary ist die binäre, mit MIDL kompilierte Darstellung einer Schnittstellenbeschreibung in IDL bzw. ODL. Die Bibliotheken bilden eine Runtime-Repository und beinhalten die statische Kapselung eines COM-Objekts. Sie sind vergleichbar mit einer einfachen Datenbank und entweder im Betriebssystem als eigenständige Datei anzumelden [Registry, "HKEY_CLASSES_ROOT\TypeLib"] oder in die Komponente direkt binär einzubinden und damit in beiden Fällen öffentlich verfügbar.

Das Vorhandensein einer TypeLibrary in Verbindung mit einer Dispatchschnittstelle ermöglicht das dynamische Binden von Komponenten zur Laufzeit. Ein Client kann einen Server via seitens des Betriebssystems speziell für diesen Fall zur Verfügung gestellter Bibliotheksschnittstellen auf sämtliche Typeninformationen hin abfragen. Dem Client, in diesem Zusammenhang auch Automation-Controller genannt, darf demnach der Server und dessen Methoden zur Kompilierzeit vollständig unbekannt sein. Operationen auf den Server werden über die TypeLibrary an die Dispatchschnittstelle weitergereicht, welche dann die bezeichnete Prozedur mittels ihrer Invoke-Methode referenziert. Der Aufruf einer Methode darf während der Ausführungszeit des Clients durch den Namen erfolgen. Dieses Verfahren ist durch CallByName bezeichnet; zulässige Datentypen bei der Parameterübergabe sind zu dieser Bindungsart lediglich Variants. Eine mit MIDL erstellte Bibliothek muss nicht zwangsläufig mit der in IDispatch abgelegten Information übereinstimmen. Methodennamen dürfen beispielsweise beliebig manipuliert werden, wenn hierbei im Ergebnis die eigentliche Schnittstellenstruktur unverändert bleibt.

Mit im Lieferumfang der Visual-Studio Umgebung enthalten ist der Objektbrowser OLEVIEW, mit dem sich die Bibliotheken, sofern vorhanden, jeder Komponente betrachten und in IDL zurück exportieren lassen. Unter den beiliegenden Beispielen befindet sich der Quelltext eines eigenen Komponentenbrowsers [Samples\TypeLibs].


Abbildung 8: Ansicht einer Typenbibliothek mit dem Tool OLEVIEW

COM unterstützt mit den Schnittstellen ITypeLib, ITypeInfo, ITypeComp einen spezialisierten Satz von Methoden für den laufzeitbezogenen Zugriff auf die Typenbibliothek einer Komponente:

ITypeLib ermöglicht den öffnenden Zugriff auf die Informationen einer Typenbibliothek. Sie stellt das übergeordnete Behältnis für die Typeninformation einer Komponente dar und gestattet die allgemeine Iteration durch vorhandene Typenbeschreibungen. Die speziellen Beschreibungen eines Typs sind erst durch die implementierte Schnittstelle ITypeInfo abfragbar.
ITypeInfo bietet den direkten Zugriff auf alle in der Bibliothek vorhandenen Informationen zu je einem Typ. Unterstützt wird im wesentlichen die Beschreibung von Funktionen, Strukturen, Module und Schnittstellen, sowie deren Attribute, Prototyp und Parameter. Compiler nutzen ITypeInfo um direkte Referenzen auf eine Interface oder eine Methode zu setzen.
ITypeComp Comp steht für Compiler und unterstützt solche durch einen performten Zugriffs-Mechanismus. Das iterierende Navigieren entfällt.

2.1.5 Die Standardschnittstelle IDispatch, Automation

Das Dispatchinterface, auch Automation-Interface genannt, ist der Schlüssel zu einer der COM-Kernkonzepte, der Automation [Object Scripting]. Klassische Automationsserver, wie Microsoft Word oder Excel, legen ihre gesamte Objekthierarchie über dieses Verfahren offen. Automation beschreibt die Eigenschaft eines Servers mittels seiner Dispatchschnittstelle durch einen Clients dynamisch programmierbar bzw. "fernzusteuerbar" zu sein. Prinzipiell ist dies zwar auch über rein VTablebasierte COM-Schnittstellen zu realisieren, der Zugriff über Interpretersprachen oder Programmiersprachen, wie Java oder Delphi, die keine statischen Custominterfaces unterstützen, wäre allerdings dadurch ausgeschlossen. Automation ermöglicht das späte Binden eines Servers zur Laufzeit an einen Client und die zeigerlose Operation auf dessen Methoden in textueller Form. Ihre Umsetzung findet mit durch die Standardschnittstelle IDispatch und ihrer vier Methoden statt:

GetTypeInfoCount Der Rückgabewert dieser Methode zeigt an, ob eine Typeninformation für ein Objekt enthalten ist [0 oder 1].
GetTypeInfo Falls Typeninformationen vorliegen, lässt sich mit GetTypeInfo ein Zeiger auf ITypeInfo [s. Kapitel 2.1.4] erfragen.
GetTypeInfo Übergeben wird ein Stringarray mit Methodennamen. Ergebnis ist ein Array gleicher Größe, dessen Elemente die zu je einer Methode gehörende DISPID sowie deren Parameterbeschreibungen enthalten.
GetIDsOfNames Ermöglicht die fehlertolerante Operation auf die Methoden eines Objekts anhand ihrer zuvor ermittelten DISPID. Benötigte Parameter sind in der Struktur DISPPARAMS beschrieben. Mit übergeben wird eine LCID [Länderkennung], die den lokalen Kontext bestimmt.

Die Implementierung der ursprünglich ergänzend zu VB entworfenen IDispatch-Schnittstelle ist für einen Server nicht zwingend vorgegeben, aber empfohlen, da erst sie die Verwendung von Scripting erlaubt [z. B. Javascript, VB-Script, VBA und ab VB4 abwärts besitzen keine Möglichkeiten zur VTable-Verwendung]. Sprachen wie Delphi verfügen über keine Custominterfaces, sondern legen ihre Komponenten nur über Dispatchschnittstellen offen. Tendenziös werden in Zukunft die meisten Komponenten IDispatch unterstützen. Ihre verbreitetste Anwendung findet die Automation in der Programmiersprache Visual Basic. Der Umgang mit Dispatchschnittstellen erweitert die Flexibilität von Visual Basic wesentlich. Die Sprache verfügt zwar über die Fähigkeit der Nutzung von Custominterfaces, besitzt aber weder für die dort verwendeten VTables, noch für das Staffeln von Parametern manipulative Werkzeuge.

IDispatch bietet über seinen integrierten Mechanismus Zugriff auf die Methoden und Parameter eines Objekts sowie die Mittel diese Methoden syntaktisch korrekt aufzurufen, ohne das Objekt selbst binär referenzieren zu müssen. IDispatch definiert somit eine dynamische Schnittstelle zu einem Server. Die Auswahl des zu verwendenden Servers als auch die Selektion seiner anzusprechenden Prozeduren können entgegen der VTable-Methode, zur Laufzeit des Clients einerseits festgelegt und andererseits manipuliert werden [Late Binding].

Das Konzept des von CORBA entlehnten laufzeitbezogenen Informationsdienstes via IDispatch beinhaltet das Potential der Komponentendynamik. Ist in einer Komponente IDispatch implementiert, dann darf sie über diese Schnittstelle zur Laufzeit ihr offengelegtes Erscheinungsbild ändern, ohne dadurch clientseitige Inkompatibilitäten zu produzieren. Diese Technik findet praktisch allerdings eher selten Anwendung.

Der typische Verlauf einer Automation startet mit der Instanzierung eines Servers durch seine CLSID. Das Ergebnis dieses Vorgangs liefert einen Zeiger auf die zugehörige Dispatchschnittstelle. Der Client kann jetzt der Funktion GetIDsOfNames die Zeichenkette eines bekannten Methodennamens übergeben und erhält als Antwort die zugehörige DISPID sowie die notwendige Aufrufkonvention der nun bestimmten Methode. Bevor im Anschluss der eigentliche Aufruf mittels der IDispatch Methode Invoke erfolgen darf, sind die Argumente in Standardtypen zu wandeln und von rechts nach links in ein Array vom Typ VARIANTARG, ein Mitglied der DISPPARAMS-Struktur, abzulegen. Die DISPPARAMS-Struktur unterstützt im weiteren auch die Aufnahme benannter Argumente. Zulässige Datentypen sind, ähnlich wie bei CORBA, ausschließlich Varianten wie VT_BOOL, VT_DATE, VT_BSTR etc. [Vergleiche IDL, Kapitel 2.1.3]. Varianten besitzen die Struktur einer Union und führen neben ihrem Wert auch den Bezeichner ihres Datentyps mit sich.


Abbildung 9: Erlaubte Varianttypen für IDispatch

Der Aufruf einer Servermethode via Dispatchinterface erfolgt wie eingangs erwähnt mittels Invoke. IDispatch kann anhand der DISPID die zugehörige Methode lokalisieren und durch Entpacken der Varianten sowie deren Sortierung entsprechend instruieren. Neben der Angabe einer Länderkennung ist zwischen vier Aufruftypen zu unterscheiden:

DISPATCH_METHOD Die Funktion wird als Methode aufgerufen.
DISPATCH_PROPERTYGET Aufruf als lesende Eigenschaft.
DISPATCH_PROPERTYPUT Aufruf als schreibende Eigenschaft.
DISPATCH_PROPERTYPUTREF Aufruf als schreibende, referenzierende Eigenschaft.

Die Performance eines solchen Vorganges ist auf Grund seiner Komplexität und seines vergleichbar großen Overheads gering. Für jeden Methodenaufruf sind clientseitig die Aufrufkonventionen zu erfahren und die Parameter zu verpacken sowie serverseitig wieder zu entpacken. Weiterhin lässt IDispatch neben Ausnahme- und Fehlerbehandlungen auch untypisierte Aufrufe zu, so dass die Argumente seitens der Schnittstelle für jeden einzelnen Parameter zu prüfen sind.

Vorteilhaft hingegen ist, dass ein Automationsserver für das Marshaling [s. Kapitel 2.5.1] keinen expliziten Proxy- und Stubcode fordert, so dass ein manuelles Erstellen aus dem MIDL-Kompilat dieser Module entfallen darf. Die Funktionalität des Marshalings liegt nämlich nicht in IDispatch selbst, sondern wird durch einen sogenannten Automation-Marshaler, der bei Remote-Servern für die erforderlichen Generierung der Proxy- und Stub-Objekte sorgt, bereitgestellt [oleaut.dll] und im Bedarfsfall einfach nachgeladen. Ein Automationsserver beschreibt den Aufbau seiner Dispatchschnittstelle in einer TypeLibrary, die entweder fest eingebunden oder durch eine externe Bibliothek repräsentiert wird.

2.1.6 Kombination: Duale Interfaces

Der Zusammenschluss aus einem Custominterface [VTable] und einer Automationschnittstelle [IDispatch] nennt sich Duales Interface. Duale Schnittstellen, in MIDL durch das Schlüsselwort dual definiert, vereinen die Vorzüge der beiden gängigen COM-Interfaces. Der Benutzer hat gemäß des Anwendungskontextes die Möglichkeit, zu entscheiden, welchem Typen er den Vorzug gibt. Ein Server, der sich über duale Interfaces offen legt, bietet mit der sehr frühen, der frühen und der späten Bindung sämtliche COM-Bindungsarten an. In Visual Basic erstellte Komponenten besitzen ausschließlich duale Interfaces.

2.1.7 CoClass, Paketbildung

Eine CoClass [Component Object Class] beschreibt eine COM-Komponente, auch COM-Klasse genannt. Sie [bezeichnet durch ihre ClassID oder CLSID] vereinigt alle implementierten Schnittstellen [IUnknown, IDispatch, IClassFactory, IMoniker, IConnectionPoint, IPersistFile, Custominterfaces etc.]. Die CoClass ist demnach die konkrete Codeimplementierung aller beteiligten abstrakten Klassen. Die CoClass lässt sich als Paket zur Gruppierung von Klassen und Interfaces definieren, das logisch zusammengehörende Elemente miteinander verknüpft. Pakete dürfen weitere Pakete enthalten, so dass sich Hierarchien ergeben können.

Die reale Ausprägung einer COM-Klasse, also die Instanz heißt COM-Objekt. Die Implementierung der Schnittstellen erfolgt nicht durch das COM-Objekt, sondern auf der Ebene der enthaltenen COM-Klasse. Es ist statthaft, einer Komponente neue Schnittstellen hinzuzufügen [existente Interfaces müssen binär kompatibel bleiben] und damit die Versionierung zuzulassen. Jedes durch Visual Basic erstellte Objekt ist gleichzeitig ein COM-Objekt und verfügt über ein Attribut, das seinen Gültigkeitsbereich beschreibt. Zulässige Eigenschaften sind:

Private Der Zugriff auf ein Objekt dieser Klassenart kann nur innerhalb eines Projektes bzw. der Anwendung erfolgen. Instanzen diesen Typs lassen sich zwar exportieren, praktisch ist aber hiervon abzusehen, da keine sichere und stabile Referenzierung stattfindet [s. Kapitel 2.5.2].
PublicNonCreateable Erstellt ein öffentliches Objekte, welches nicht instanzierbar ist und damit bei Weglassung von Implementierungen die Bedingung einer abstrakten Klasse erfüllt. [Vergl. Kapitel 2.1.5]
SingleUse Diese Variante ist lediglich in OutProc-Servern zulässig und legt deren Instanzierungsverhalten fest. Bei Verwendung von SingleUse startet jede neue Referenz einen weiteren OutProc-Server, andernfalls vollzieht sich die Erstellung nur ein einziges Mal.
MultiUse Instanzen dieser Klasse sind öffentlich sowie in beliebiger Anzahl erstellbar und in InProc- als auch OutProc-Servern zulässig.
GlobalMultiUse Globale Objekte sind VB-spezifisch. Ihre Instanzierung muss nicht explizit stattfinden. Der Aufruf von Methoden des Objekts darf ohne Objektnennung, aber auch qualifiziert erfolgen. Ansonsten besitzen Instanzen dieses Typs die gleichen Eigenschaften, wie die von MultiUse-Klassen.
GlobalSingleUse Entspricht der globalen Variante des SingleUse-Typs.
Nächste Seite >>
2.2 Instanzierung von Objekten, IClassFactory
<< Vorherige Seite
2.0 COM Server