Freigeben über


Erstellen von COM-Komponenten für die Interoperabilität

Wenn Sie vorhaben, COM-basierte Anwendungen zu schreiben, können Sie den Code so entwerfen, dass er mit verwaltetem Code effizient zusammenwirkt. Indem Sie im Voraus planen, können Sie auch die Migration von nicht verwaltetem Code in verwalteten Code vereinfachen.

In den folgenden Empfehlungen werden die empfohlenen Vorgehensweisen zum Schreiben von COM-Typen zusammengefasst, die mit verwaltetem Code zusammenwirken.

Bereitstellen von Typbibliotheken

In den meisten Situationen benötigt die Common Language Runtime Metadaten für alle Typen (einschließlich von COM-Typen). Das Type Library Importer-Tool (Tlbimp.exe), das in Windows Software Development Kit (SDK) enthalten ist, kann COM-Typbibliotheken in .NET Framework-Metadaten konvertieren. Nachdem die Typbibliothek in Metadaten konvertiert wurde, können verwaltete Clients den COM-Typ übergangslos aufrufen. Um die Verwendung zu vereinfachen, sollten Sie immer Typinformationen in einer Typbibliothek zur Verfügung stellen.

Sie können eine Typbibliothek als separate Datei packen oder als Ressource in eine DLL-, EXE- oder OCX-Datei einbetten. Darüber hinaus können Sie Metadaten direkt generieren, sodass Sie die Metadaten mit dem Herausgeberschlüsselpaar signieren können. Metadaten, die mit einem Schlüssel signiert sind, haben eine definitive Quelle und können das Binden verhindern, wenn der Aufrufer den falschen Schlüssel hat. Hierdurch wird also die Sicherheit erhöht.

Registrieren von Typbibliotheken

Die Laufzeit muss zum richtigen Marshallen von Aufrufen möglicherweise die Typbibliothek suchen, die einen bestimmten Typ beschreibt. Die Laufzeit kann die Typbibliothek außer beim späten Binden erst dann sehen, wenn sie registriert wurde.

Sie können eine Typbibliothek registrieren, indem Sie die Microsoft Win32-API-Funktion LoadTypeLibEx aufrufen, wobei das regkind-Flag auf REGKIND_REGISTER festgelegt sein muss. Die Datei Regsvr32.exe registriert Typbibliotheken, die in eine DLL-Datei eingebettet sind, automatisch.

Verwenden von sicheren Arrays anstelle von Arrays variabler Länge

Sichere COM-Arrays sind selbstbeschreibend. Der Laufzeitmarshaller kann durch Untersuchen des sicheren Arrays den Rang, die Größe, die Grenzwerte und in der Regel auch den Typ des Arrayinhalts zur Laufzeit feststellen. Arrays variabler Länge (bzw. Arrays im C-Format) sind nicht auf dieselbe Weise selbstbeschreibend. Die folgende nicht verwaltete Methodensignatur enthält beispielsweise neben dem Elementtyp keine weiteren Informationen über den Arrayparameter.

HRESULT DoSomething(int cb, [in] byte buf[]);

Das Array kann vielmehr nicht von anderen durch Verweis übergebene Parameter unterschieden werden. Daher konvertiert die Datei Tlbimp.exe den Arrayparameter der DoSomething-Methode nicht. Stattdessen wird das Array als Verweis auf einen Byte-Typ dargestellt (siehe folgenden Code).

Public Sub DoSomething(cb As Integer, ByRef buf As Byte)
public void DoSomething(int cb, ref Byte buf);

Sie können die Interoperation verbessern, indem Sie das Argument als SAFEARRAY in die nicht verwaltete Methodensignatur schreiben. Beispiel:

HRESULT DoSomething(SAFEARRAY(byte)buf);

Die Datei Tlbimp.exe konvertiert SAFEARRAY in den folgenden verwalteten Arraytyp:

Public Sub DoSomething(buf As Byte())
public void DoSomething(Byte[] buf);

Verwenden von automatisierungskompatiblen Datentypen

Der Laufzeitmarshallingdienst unterstützt automatisch alle automatisierungskompatiblen Datentypen. Nicht kompatible Typen werden möglicherweise unterstützt, dies ist jedoch nicht zwingend der Fall.

Bereitstellen von Version und Gebietsschema in Typbibliotheken

Wenn Sie eine Typbibliothek importieren, werden auch die Version und das Gebietsschema der Typbibliothek in die Assembly übertragen. Verwaltete Clients können dann an eine bestimmte Version oder ein bestimmtes Gebietsschema der Assembly oder an die aktuellste Version der Assembly gebunden werden. Da die Versionsinformationen in der Typbibliothek angegeben werden, können Clients genau wählen, welche Version der Assembly verwendet werden soll.

Verwenden von blitfähigen Typen

Datentypen sind entweder blitfähig oder nicht blitfähig. Blitfähige Typen verfügen innerhalb der Interop-Grenzen über eine gemeinsame Darstellung. Ganzzahl- und Gleitkommatypen sind blitfähig. Arrays und Strukturen von blitfähigen Typen sind ebenfalls blitfähig. Zeichenfolgen, Datumsangaben und Objekte sind Beispiele für nicht blitfähige Typen und werden während des Marshallingprozesses umgewandelt.

Sowohl blitfähige als auch nicht blitfähige Typen werden vom Interop-Marshallingdienst unterstützt. Typen, die beim Marshalling konvertiert werden müssen, haben nicht dieselbe Leistung wie blitfähige Typen. Bei Verwendung nicht blitfähiger Typen erhöht sich der mit dem Marshalling dieser Typen verbundene Aufwand.

Zu besonders großen Problemen kommt es bezüglich von Zeichenfolgen. Verwaltete Zeichenfolgen werden als Unicode-Zeichen gespeichert und können daher effizienter an nicht verwalteten Code gemarshallt werden, der Unicode-Zeichenargumente erwartet. Zeichenfolgen aus ANSI-Zeichen sollten möglichst nicht verwendet werden.

Implementieren von "IProvideClassInfo"

Beim Marshalling von nicht verwalteten Schnittstellen an verwalteten Code erstellt die Laufzeit einen Wrapper eines bestimmten Typs. Die Methodensignatur gibt in der Regel den Typ der Schnittstelle an, und der Typ des Objekts, das die Schnittstelle implementiert, ist möglicherweise nicht bekannt. Wenn der Typ des Objekts nicht bekannt ist, umschließt die Laufzeit die Schnittstelle mit einem allgemeinen COM-Objektwrapper, bei dem es sich weniger um funktionelle als um typspezifische Wrappers handelt.

Betrachten Sie beispielsweise die folgende COM-Methodensignatur:

interface INeedSomethng {
   HRESULT DoSomething(IBiz *pibiz);
}

Beim Importieren wird die Methode wie folgt konvertiert:

Interface INeedSomething
   Sub DoSomething(pibiz As IBiz)
End Interface
interface INeedSomething {
   void DoSomething(IBiz pibiz);
}

Wenn Sie ein verwaltetes Objekt übertragen, das die INeedSomething-Schnittstelle in die IBiz-Schnittstelle implementiert, versucht der Interop-Marshaller bei der erstmaligen Einführung von IBiz in den verwalteten Code, die Schnittstelle mit einem Objektwrapper eines bestimmten Typs zu umschließen. Der Marshaller muss den Typ des Objekts kennen, das die Schnittstelle implementiert, um den richtigen Typ des Wrappers zu identifizieren. Eine der Methoden, die der Marshaller zum Bestimmen des Objekttyps verwendet, ist die Durchführung einer Abfrage nach der IProvideClassInfo-Schnittstelle. Wenn das Objekt IProvideClassInfo implementiert, bestimmt der Marshaller den Typ des Objekts und umschließt die Schnittstelle mit einem typisierten Wrapper.

Verwenden von modularen Aufrufen

Das Marshallen von Daten zwischen verwaltetem und nicht verwaltetem Code verursacht Aufwand. Sie können diesen Aufwand verringern, indem Sie die Anzahl der Grenzüberschreitungen möglichst gering halten. Schnittstellen, die die Anzahl der Grenzüberschreitungen gering halten, zeigen i. d. R. eine bessere Leistung als Schnittstellen, die die Grenze häufig überschreiten und bei jedem Überschreiten kleine Aufgaben ausführen.

Verwenden von Fehler-HRESULTs auf traditionelle Weise

Wenn ein verwalteter Client ein COM-Objekt aufruft, verknüpft die Laufzeit die Fehler-HRESULTs des COM-Objekts mit Ausnahmen, die der Marshaller bei Rückgabe aus dem Aufruf auslöst. Das Modell der verwalteten Ausnahme wurde für diejenigen Fälle, die keine Ausnahmen darstellen, optimiert. Das Abfangen von Ausnahmen verursacht daher fast keinen Aufwand, wenn keine Ausnahme auftritt. Wenn jedoch eine Ausnahme auftritt, hat das Abfangen der Ausnahme einen großen Aufwand zur Folge.

Ausnahmen sollten in möglichst geringem Maße verwendet werden, und es sollte vermieden werden, Fehler-HRESULTs lediglich zu Informationszwecken zurückzugeben. Fehler-HRESULTs sollten für außergewöhnliche Situationen vorbehalten sein, denn eine übermäßige Verwendung kann sich negativ auf die Leistung auswirken.

Explizites Freigeben von externen Ressourcen

Manche Objekte verwenden während ihrer Lebensdauer externe Ressourcen. So kann beispielsweise eine Datenbankverbindung ein Recordset aktualisieren. In der Regel behält ein Objekt eine externe Ressource während der gesamten Gültigkeit, während eine explizite Freigabe die Ressource umgehend zurückgeben kann. Sie können beispielsweise die Close-Methode für ein Dateiobjekt verwenden, anstatt die Datei im Klassendestruktor oder mit IUnknown.Release zu schließen. Durch Angabe einer Entsprechung für die Close-Methode im Code können Sie die externe Dateiressource freigeben, obwohl das Dateiobjekt weiterhin vorhanden ist.

Vermeiden der erneuten Definition nicht verwalteter Typen

Das richtige Implementieren einer vorhandenen COM-Schnittstelle in verwaltetem Code muss mit dem Importieren der Definition der Schnittstelle mit Tlbimp.exe oder einer entsprechenden API beginnen. Die sich daraus ergebenden Metadaten stellen eine kompatible Definition der COM-Schnittstelle dar (ebenso wie IID, DISIDS usw.).

Vermeiden Sie es, COM-Schnittstellen in verwaltetem Code manuell neu zu definieren. Dies ist sehr zeitaufwendig, und es wird selten eine verwaltete Schnittstelle erzeugt, die mit der vorhandenen COM-Schnittstelle kompatibel ist. Verwenden Sie stattdessen Tlbimp.exe, um die Kompatibilität der Definitionen zu erhalten.

Vermeiden der Verwendung von Erfolgs-HRESULTs

Verwaltete Anwendungen fangen Ausnahmen in Fehlersituationen in der Regel ab. Um das Verwenden von COM-Typen transparent zu gestalten, löst die Laufzeit automatisch eine Ausnahme aus, wenn eine COM-Methode ein Fehler-HRESULT zurückgibt.

Wenn das COM-Objekt ein Erfolgs-HRESULT zurückgibt, gibt die Laufzeit den Wert im retval-Parameter zurück. HRESULT wird standardmäßig verworfen, sodass es für den verwalteten Client sehr schwierig ist, den Wert eines Erfolgs-HRESULTs zu untersuchen. Sie können HRESULT zwar mit dem PreserveSigAttribute-Attribut beibehalten, dies ist jedoch mit einigem Aufwand verbunden. Sie müssen das Attribut manuell einer Assembly hinzufügen, die mit Tlbimp.exe oder einer entsprechenden API generiert wurde.

Erfolgs-HRESULTs sollten möglichst vermieden werden. Sie können stattdessen zum Zurückgeben von Informationen zum Status eines Aufrufs einen Out-Parameter verwenden.

Vermeiden der Verwendung von Modulfunktionen

Typbibliotheken können in einem Modul definierte Funktionen enthalten. In der Regel verwenden Sie diese Funktionen, um Typinformationen für DLL-Einstiegspunkte zur Verfügung zu stellen. Tlbimp.exe importiert diese Funktionen nicht.

Vermeiden der Verwendung von Membern von "System.Object" in Standardschnittstellen

Verwaltete Clients und Co-Klassen interagieren mithilfe von Wrappern, die von der Laufzeit zur Verfügung gestellt werden. Wenn Sie einen COM-Typ importieren, fügt der Konvertierungsprozess alle Methoden der Standardschnittstelle der Co-Klasse der von der System.Object-Klasse abgeleiteten Wrapperklasse hinzu. Achten Sie bei der Benennung der Member der Standardschnittstelle darauf, dass es nicht zu Konflikten mit den Namen der Member von System.Object kommt, denn bei Auftreten eines Konflikts überschreibt die importierte Methode die Basisklassenmethode.

Diese Aktion eignet sich, wenn die Methode der Standardschnittstelle und die Methode von System.Object dieselbe Funktion haben. Wenn Methoden der Standardschnittstelle jedoch auf unbeabsichtigte Art und Weise verwendet werden, kann dies jedoch zu Problemen führen. Um Namenskonflikte zu vermeiden, sollten folgende Namen in Standardschnittstellen nicht verwendet werden: Object, Equals, Finalize, GetHashCode, GetType, MemberwiseClone und ToString.

Siehe auch

Referenz

Tlbimp.exe (Type Library Importer-Tool)

Konzepte

Entwurfsüberlegungen für die Interoperation