Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Wenn ein COM-Client ein .NET-Objekt aufruft, erstellt die Common Language Runtime das verwaltete Objekt und einen COM-aufrufbaren Wrapper (CCW) für das Objekt. Auf ein .NET-Objekt kann nicht direkt verwiesen werden, COM-Clients verwenden den CCW als Proxy für das verwaltete Objekt.
Die Laufzeit erstellt genau einen CCW für ein verwaltetes Objekt, unabhängig von der Anzahl der COM-Clients, die ihre Dienste anfordern. Wie die folgende Abbildung zeigt, können mehrere COM-Clients einen Verweis auf die CCW enthalten, die die INew-Schnittstelle verfügbar macht. Der CCW verfügt seinerseits über einen einzelnen Verweis auf das verwaltete Objekt, das die Schnittstelle implementiert und für das eine Garbage Collection durchgeführt wird. Sowohl COM- als auch .NET-Clients können Anforderungen an dasselbe verwaltete Objekt gleichzeitig senden.
COM-Aufrufbare Wrapper sind für andere Klassen, die innerhalb der .NET-Laufzeit ausgeführt werden, nicht sichtbar. Ihr Hauptzweck besteht darin, Aufrufe zwischen verwaltetem und nicht verwaltetem Code zu übertragen, gleichzeitig verwalten CCWs auch die Objektidentität und die Objektlebensdauer der verwalteten Objekte, die sie kapseln.
Objektidentität
Common Language Runtime belegt für das .NET-Objekt Speicher, der durch Garbage Collection gewonnen wird. Dadurch kann das Objekt bei Bedarf innerhalb des Speichers verschoben werden. Im Gegensatz dazu belegt die Common Language Runtime Speicherplatz für den CCW aus einem nicht gesammelten Heap. Dies hat zur Folge, dass COM-Clients direkt auf den Wrapper verweisen können.
Objektlebensdauer
Beim CCW erfolgt die Verweiszählung nach Art des herkömmlichen COM, anders als beim .NET-Client, der von ihm umschlossen wird. Wenn die Verweiszählung für den CCW 0 erreicht hat, gibt der Wrapper seinen Verweis auf das verwaltete Objekt frei. Beim nächsten Garbage Collection-Zyklus werden die verwalteten Objekte ohne verbleibende Verweise eingesammelt.
Simulieren von COM-Schnittstellen
CCW macht alle öffentlichen, COM-sichtbaren Schnittstellen, Datentypen und Rückgabewerte für COM-Clients auf eine Weise verfügbar, die mit der Durchsetzung der schnittstellenbasierten Interaktion von COM konsistent ist. Für einen COM-Client ist das Aufrufen von Methoden für ein .NET-Objekt identisch mit dem Aufrufen von Methoden für ein COM-Objekt.
Um diesen nahtlosen Ansatz zu schaffen, produziert das CCW traditionelle COM-Schnittstellen wie IUnknown und IDispatch. Wie die folgende Abbildung zeigt, unterhält der CCW einen einzigen Verweis auf das .NET-Objekt, den er einschließt. Sowohl der COM-Client als auch das .NET-Objekt arbeiten über die Proxy- und Stub-Struktur des CCW zusammen.
Zusätzlich zur Verfügbarmachen der Schnittstellen, die explizit von einer Klasse in der verwalteten Umgebung implementiert werden, stellt die .NET-Laufzeit Implementierungen der COM-Schnittstellen bereit, die in der folgenden Tabelle im Auftrag des Objekts aufgeführt sind. Eine .NET-Klasse kann das Standardverhalten überschreiben, indem eine eigene Implementierung dieser Schnittstellen bereitgestellt wird. Die Laufzeit stellt jedoch immer die Implementierung für die IUnknown - und IDispatch-Schnittstellen bereit.
| Schnittstelle | BESCHREIBUNG |
|---|---|
| IDispatch- | Stellt einen Mechanismus für die späte Bindung an den Typ bereit. |
| IErrorInfo- | Stellt eine textbezogene Beschreibung des Fehlers, seiner Quelle, einer Hilfedatei, eines Hilfekontexts und der GUID der Schnittstelle bereit, die den Fehler definiert hat (immer GUID_NULL für .NET-Klassen). |
| IProvideClassInfo | Ermöglicht COM-Clients den Zugriff auf die ITypeInfo-Schnittstelle , die von einer verwalteten Klasse implementiert wird. Gibt COR_E_NOTSUPPORTED für .NET Core für Typen zurück, die nicht aus COM importiert wurden. |
| ISupportErrorInfo | Ermöglicht einem COM-Client zu bestimmen, ob das verwaltete Objekt die IErrorInfo-Schnittstelle unterstützt. Wenn ja, kann der Client einen Zeiger auf das neueste Ausnahmeobjekt abrufen. Alle verwalteten Typen unterstützen die IErrorInfo-Schnittstelle . |
| ITypeInfo (nur.NET Framework) | Stellt Typinformationen für eine Klasse bereit, die genau mit den Typinformationen übereinstimmt, die von Tlbexp.exeerstellt werden. |
| IUnknown | Stellt die Standardimplementierung der IUnknown-Schnittstelle bereit, über die der COM-Client die Lebensdauer des CCWs verwalten und die Typkonvertierung erzwingt. |
Eine verwaltete Klasse kann auch die in der folgenden Tabelle beschriebenen COM-Schnittstellen bereitstellen.
| Schnittstelle | BESCHREIBUNG |
|---|---|
| Die Klassenschnittstelle (_classname) | Schnittstelle, die von der Laufzeit verfügbar gemacht und nicht explizit definiert wird, die alle öffentlichen Schnittstellen, Methoden, Eigenschaften und Felder verfügbar macht, die explizit für ein verwaltetes Objekt verfügbar gemacht werden. |
| IConnectionPoint und IConnectionPointContainer | Schnittstelle für Objekte, die Stellvertretungsbasierte Ereignisse (eine Schnittstelle zum Registrieren von Ereignisabonnenten) enthalten. |
| IDispatchEx (nur.NET Framework) | Schnittstelle, die von der Laufzeit bereitgestellt wird, wenn die Klasse IExpando implementiert. Die IDispatchEx-Schnittstelle ist eine Erweiterung der IDispatch-Schnittstelle. Im Gegensatz zu IDispatch ermöglicht sie das Aufzählen, Hinzufügen, Löschen und Aufrufen von Membern unter Berücksichtigung von Groß-/Kleinschreibung. |
| IEnumVARIANT | Schnittstelle für Auflistungstypklassen, die die Objekte in der Auflistung aufzählen, wenn die Klasse IEnumerable implementiert. |
Einführung in die Klassenschnittstelle
Die Klassenschnittstelle, die nicht explizit in verwaltetem Code definiert ist, ist eine Schnittstelle, die alle öffentlichen Methoden, Eigenschaften, Felder und Ereignisse verfügbar macht, die explizit für das .NET-Objekt verfügbar gemacht werden. Bei dieser Schnittstelle kann es sich um eine duale oder um eine nur für Dispatch vorgesehene Schnittstelle handeln. Die Klassenschnittstelle empfängt den Namen der .NET-Klasse selbst, gefolgt von einem Unterstrich. Beispielsweise ist die Klassenschnittstelle für die Klasse "Säugetier" _Mammal.
Für abgeleitete Klassen macht die Klassenschnittstelle auch alle öffentlichen Methoden, Eigenschaften und Felder der Basisklasse verfügbar. Die abgeleitete Klasse macht auch eine Klassenschnittstelle für jede Basisklasse verfügbar. Wenn die Klasse "Säugetier" beispielsweise die Klasse "MammalSuperclass" erweitert, die system.Object selbst erweitert, macht das .NET-Objekt COM-Clients drei Klassenschnittstellen namens _Mammal, _MammalSuperclass und _Object verfügbar.
Betrachten Sie beispielsweise die folgende .NET-Klasse:
' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
Sub Eat()
Sub Breathe()
Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
public void Eat() {}
public void Breathe() {}
public void Sleep() {}
}
Der COM-Client kann einen Zeiger auf eine Klassenschnittstelle mit dem Namen _Mammalabrufen. In .NET Framework können Sie das Tool für den Typbibliotheksexporteur (Tlbexp.exe) verwenden, um eine Typbibliothek zu generieren, die die _Mammal Schnittstellendefinition enthält. Der Typbibliotheksexportierer wird in .NET Core nicht unterstützt. Wenn die Mammal Klasse eine oder mehrere Schnittstellen implementiert hat, werden die Schnittstellen unter der Co-Klasse angezeigt.
[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
pRetVal);
[id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
VARIANT_BOOL* pRetVal);
[id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x6002000d)] HRESULT Eat();
[id(0x6002000e)] HRESULT Breathe();
[id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
[default] interface _Mammal;
}
Das Generieren der Klassenschnittstelle ist optional. Standardmäßig generiert COM-Interop für jede Klasse, die Sie in eine Typbibliothek exportieren, eine reine Dispatch-Schnittstelle. Sie können die automatische Erstellung dieser Schnittstelle verhindern oder ändern, indem Sie die ClassInterfaceAttribute Klasse anwenden. Obwohl die Klassenschnittstelle die Aufgabe erleichtern kann, verwaltete Klassen für COM verfügbar zu machen, sind die Verwendungen begrenzt.
Vorsicht
Wenn Sie die Klassenschnittstelle verwenden, können Sie die zukünftige Versionsverwaltung Ihrer verwalteten Klasse komplizieren, anstatt ihre eigenen explizit zu definieren. Lesen Sie die folgenden Richtlinien, bevor Sie die Klassenschnittstelle verwenden.
Definieren Sie eine explizite Schnittstelle für COM-Clients, die verwendet werden soll, anstatt die Klassenschnittstelle zu generieren.
Da COM-Interoperabilität automatisch eine Klassenschnittstelle generiert, können Änderungen nach der Version ihrer Klasse das Layout der Klassenschnittstelle ändern, die von der Common Language Runtime verfügbar gemacht wird. Da COM-Clients in der Regel nicht darauf vorbereitet sind, Änderungen am Layout einer Schnittstelle zu verarbeiten, funktionieren sie nicht mehr, wenn Sie das Mitgliederlayout der Klasse ändern.
Diese Richtlinie stärkt die Vorstellung, dass Schnittstellen, die COM-Clients ausgesetzt sind, unveränderlich bleiben müssen. Um das Risiko zu verringern, die COM-Clients durch die versehentliche Neuanordnung des Schnittstellenlayouts zu unterbrechen, isolieren Sie alle Änderungen an der Klasse vom Schnittstellenlayout, indem Sie die Schnittstellen explizit definieren.
Verwenden Sie ClassInterfaceAttribute , um die automatische Generierung der Klassenschnittstelle zu deaktivieren und eine explizite Schnittstelle für die Klasse zu implementieren, wie das folgende Codefragment zeigt:
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
int IExplicit.M() { return 0; }
}
Der Wert "ClassInterfaceType.None" verhindert, dass die Klassenschnittstelle generiert wird, wenn die Klassenmetadaten in eine Typbibliothek exportiert werden. Im vorherigen Beispiel können COM-Clients nur über die Schnittstelle auf die LoanAppIExplicit Klasse zugreifen.
Vermeiden Sie die Zwischenspeicherung von Verteilerbezeichnern (DispIds)
Die Verwendung der Klassenschnittstelle ist eine akzeptable Option für Skriptclients, Microsoft Visual Basic 6.0-Clients oder alle verspätet gebundenen Clients, die die DispIds von Schnittstellenmitgliedern nicht zwischenspeichern. DispIds erkennen Schnittstellenmitglieder, um die späte Bindung zu aktivieren.
Für die Klassenschnittstelle basiert die Generierung von DispIds auf der Position des Elements in der Schnittstelle. Wenn Sie die Reihenfolge des Elements ändern und die Klasse in eine Typbibliothek exportieren, ändern Sie die in der Klassenschnittstelle generierten DispIds.
Um eine Unterbrechung spät gebundener COM-Clients bei Verwendung der Klassenschnittstelle zu vermeiden, wenden Sie das ClassInterfaceAttribute mit dem Wert ClassInterfaceType.AutoDispatch an. Dieser Wert implementiert eine nur auf Dispatch ausgelegte Klassenschnittstelle, lässt aber die Schnittstellenbeschreibung aus der Typbibliothek wegfallen. Ohne eine Schnittstellenbeschreibung können Clients DispIds zur Kompilierungszeit nicht zwischenspeichern. Obwohl dies der Standardschnittstellentyp für die Klassenschnittstelle ist, können Sie den Attributwert explizit anwenden.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp
{
public int M() { return 0; }
}
Um die DispId eines Schnittstellenmitglieds zur Laufzeit abzurufen, können COM-Clients IDispatch.GetIdsOfNames aufrufen. Um eine Methode auf der Schnittstelle aufzurufen, übergeben Sie die zurückgegebene DispId als Argument an IDispatch.Invoke.
Beschränken Sie die Verwendung der Option der dualen Schnittstelle für die Klassenschnittstelle.
Duale Schnittstellen ermöglichen ein frühes und spätes Binden an Schnittstellenmember durch COM-Clients. Bei der Entwurfszeit und beim Testen ist es möglicherweise hilfreich, die Klassenschnittstelle auf "dual" festzulegen. Für eine verwaltete Klasse (und deren Basisklassen), die nie geändert werden, ist diese Option ebenfalls akzeptabel. Vermeiden Sie in allen anderen Fällen das Festlegen der Klassenschnittstelle auf "dual".
Eine automatisch generierte duale Schnittstelle kann in seltenen Fällen angemessen sein; Es entsteht jedoch häufiger eine versionsbezogene Komplexität. So können COM-Clients, die die Klassenschnittstelle einer abgeleiteten Klasse verwenden, beispielsweise schnell unterbrochen werden, wenn Änderungen an der Basisklasse vorgenommen werden. Wenn ein Drittanbieter die Basisklasse bereitstellt, haben Sie keine Kontrolle über das Layout der Klassenschnittstelle. Im Gegensatz zu einer reinen Dispatch-Schnittstelle stellt eine duale Schnittstelle (ClassInterfaceType.AutoDual) eine Beschreibung der Klassenschnittstelle in der exportierten Typbibliothek bereit. Eine solche Beschreibung kann dafür sorgen, dass spät gebundene Clients DispIds zur Kompilierungszeit zwischenspeichern.
Stellen Sie sicher, dass alle COM-Ereignisbenachrichtigungen spät gebunden sind.
Standardmäßig werden COM-Typinformationen direkt in verwaltete Assemblys eingebettet, sodass primäre Interopassemblys (PIAs) überflüssig sind. Eine der Einschränkungen der eingebetteten Typinformationen besteht jedoch darin, dass sie die Übermittlung von COM-Ereignisbenachrichtigungen durch früh gebundene vtable-Aufrufe nicht unterstützt, sondern nur spät gebundene IDispatch::Invoke Anrufe unterstützt.
Wenn Ihre Anwendung früh gebundene Aufrufe von COM-Ereignisschnittstellenmethoden erfordert, können Sie für die Eigenschaft Interoptypen einbetten in Visual Studio true festlegen, oder schließen Sie das folgende Element in Ihre Projektdatei ein:
<EmbedInteropTypes>True</EmbedInteropTypes>