Problembehandlung bei interoperabilität (Visual Basic)
Wenn Sie zwischen COM und dem verwalteten Code von .NET Framework zusammenarbeiten, treten möglicherweise ein oder mehrere der folgenden häufig auftretenden Probleme auf.
Interop Marshalling
Manchmal müssen Sie möglicherweise Datentypen verwenden, die nicht Teil von .NET Framework sind. Interop-Assemblys verarbeiten die meiste Arbeit für COM-Objekte, aber möglicherweise müssen Sie die Datentypen steuern, die verwendet werden, wenn verwaltete Objekte für COM bereitgestellt werden. Beispielsweise müssen Strukturen in Klassenbibliotheken den BStr
nicht verwalteten Typ für Zeichenfolgen angeben, die an COM-Objekte gesendet werden, die von Visual Basic 6.0 und früheren Versionen erstellt wurden. In solchen Fällen können Sie das attribut MarshalAsAttribute verwenden, um zu bewirken, dass verwaltete Typen als nicht verwaltete Typen verfügbar gemacht werden.
Exportieren von Zeichenfolgen mit fester Länge in nicht verwalteten Code
In Visual Basic 6.0 und früheren Versionen werden Zeichenfolgen als Bytesequenzen ohne NULL-Beendigungszeichen in COM-Objekte exportiert. Aus Gründen der Kompatibilität mit anderen Sprachen enthält Visual Basic .NET beim Exportieren von Zeichenfolgen ein Beendigungszeichen. Die beste Möglichkeit, diese Inkompatibilität zu beheben, besteht darin, Zeichenfolgen, die das Beendigungszeichen fehlen, als Arrays von Byte
oder Char
zu exportieren.
Exportieren von Vererbungshierarchien
Verwaltete Klassenhierarchien werden vereinfacht, wenn sie als COM-Objekte verfügbar gemacht werden. Wenn Sie beispielsweise eine Basisklasse mit einem Element definieren und dann die Basisklasse in einer abgeleiteten Klasse erben, die als COM-Objekt verfügbar gemacht wird, können Clients, die die abgeleitete Klasse im COM-Objekt verwenden, die geerbten Member nicht verwenden. Auf die Mitglieder einer Basisklasse kann von COM-Objekten nur als Instanzen einer Basisklasse zugegriffen werden, und dann nur, wenn die Basisklasse ebenfalls als COM-Objekt erstellt wird.
Überladene Methoden
Obwohl Sie überladene Methoden mit Visual Basic erstellen können, werden sie von COM nicht unterstützt. Wenn eine Klasse, die überladene Methoden enthält, als COM-Objekt verfügbar gemacht wird, werden neue Methodennamen für die überladenen Methoden generiert.
Betrachten Sie beispielsweise eine Klasse mit zwei Überladungen der Synch
-Methode. Wenn die Klasse als COM-Objekt verfügbar gemacht wird, können die neuen generierten Methodennamen Synch
und Synch_2
werden.
Die Umbenennung kann für Verbraucher des COM-Objekts zwei Probleme verursachen.
Clients erwarten möglicherweise nicht die generierten Methodennamen.
Die generierten Methodennamen in der Klasse, die als COM-Objekt verfügbar gemacht werden, können sich ändern, wenn der Klasse oder deren Basisklasse neue Überladungen hinzugefügt werden. Dies kann zu Versionsverwaltungsproblemen führen.
Um beide Probleme zu lösen, weisen Sie jeder Methode einen eindeutigen Namen zu, anstatt Überladungen zu verwenden, wenn Sie Objekte entwickeln, die als COM-Objekte verfügbar gemacht werden.
Verwendung von COM-Objekten über Interop-Assemblies
Sie können Interopassemblys fast so verwenden, als wären sie verwalteter Code für die COM-Objekte, die sie repräsentieren. Da sie jedoch Wrapper und keine tatsächlichen COM-Objekte sind, gibt es einige Unterschiede bei der Verwendung von Interop-Assemblys im Vergleich zu Standard-Assemblys. Diese Unterschiede umfassen die Exposition von Klassen und Datentypen für Parameter und Rückgabewerte.
Klassen, die sowohl als Schnittstellen als auch als Klassen verfügbar gemacht werden
Im Gegensatz zu Klassen in Standardassemblys werden COM-Klassen in Interopassemblys sowohl als Schnittstelle als auch als Klasse verfügbar gemacht, die die COM-Klasse darstellt. Der Name der Schnittstelle ist identisch mit der der COM-Klasse. Der Name der Interopklasse ist identisch mit der der ursprünglichen COM-Klasse, aber mit dem angefügten Wort "Klasse". Ein Beispiel: Angenommen, Sie haben ein Projekt mit einem Verweis auf eine Interopassembly für ein COM-Objekt. Wenn die COM-Klasse MyComClass
benannt ist, zeigen IntelliSense und der Objektbrowser eine Schnittstelle mit dem Namen MyComClass
und eine Klasse mit dem Namen MyComClassClass
an.
Erstellen von Instanzen einer .NET Framework-Klasse
Im Allgemeinen erstellen Sie eine Instanz einer .NET Framework-Klasse mithilfe der New
-Anweisung mit einem Klassennamen. Eine COM-Klasse, die durch eine Interopassembly dargestellt wird, ist der einzige Fall, in dem Sie die New
-Anweisung mit einer Schnittstelle verwenden können. Wenn Sie die COM-Klasse nicht mit einer Inherits
-Anweisung verwenden, können Sie die Schnittstelle genauso wie eine Klasse verwenden. Der folgende Code veranschaulicht, wie sie ein Command
-Objekt in einem Projekt erstellen, das einen Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8 Library enthält:
Dim cmd As New ADODB.Command
Wenn Sie jedoch die COM-Klasse als Basis für eine abgeleitete Klasse verwenden, müssen Sie die Interopklasse verwenden, die die COM-Klasse darstellt, wie im folgenden Code:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Anmerkung
Interopassemblys implementieren implizit Schnittstellen, die COM-Klassen darstellen. Sie sollten nicht versuchen, die Implements
-Anweisung zu verwenden, um diese Schnittstellen zu implementieren, da sonst ein Fehler auftritt.
Datentypen für Parameter und Rückgabewerte
Im Gegensatz zu Membern von Standardassemblys enthalten Interopassemblys möglicherweise Datentypen, die sich von denen unterscheiden, die in der ursprünglichen Objektdeklaration verwendet werden. Obwohl Interopassemblys COM-Typen implizit in kompatible Common Language Runtime-Typen konvertieren, sollten Sie auf die Datentypen achten, die von beiden Seiten verwendet werden, um Laufzeitfehler zu vermeiden. Zum Beispiel nehmen in COM-Objekten, die in Visual Basic 6.0 und früheren Versionen erstellt wurden, Werte vom Typ Integer
den .NET-Framework-Äquivalenttyp Short
an. Es wird empfohlen, die Merkmale importierter Member mithilfe des Objektbrowsers zu untersuchen, bevor Sie sie verwenden.
COM-Methoden auf Modulebene
Die meisten COM-Objekte werden verwendet, indem eine Instanz einer COM-Klasse mithilfe des schlüsselworts New
erstellt und anschließend Methoden des Objekts aufgerufen werden. Eine Ausnahme dieser Regel umfasst COM-Objekte, die AppObj
oder GlobalMultiUse
COM-Klassen enthalten. Solche Klassen ähneln Methoden auf Modulebene in Visual Basic .NET-Klassen. Visual Basic 6.0 und frühere Versionen erstellen implizit Instanzen solcher Objekte für Sie, wenn Sie eine ihrer Methoden zum ersten Mal aufrufen. In Visual Basic 6.0 können Sie beispielsweise einen Verweis auf die Microsoft DAO 3.6-Objektbibliothek hinzufügen und die DBEngine
-Methode aufrufen, ohne zuerst eine Instanz zu erstellen:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Visual Basic .NET erfordert, dass Sie immer Instanzen von COM-Objekten erstellen, bevor Sie deren Methoden verwenden können. Um diese Methoden in Visual Basic zu verwenden, deklarieren Sie eine Variable der gewünschten Klasse, und verwenden Sie das neue Schlüsselwort, um das Objekt der Objektvariablen zuzuweisen. Das schlüsselwort Shared
kann verwendet werden, wenn Sie sicherstellen möchten, dass nur eine Instanz der Klasse erstellt wird.
' Class level variable.
Shared DBEngine As New DAO.DBEngine
Sub DAOOpenRecordset()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim fld As DAO.Field
' Open the database.
db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Open the Recordset.
rst = db.OpenRecordset(
"SELECT * FROM Customers WHERE Region = 'WA'",
DAO.RecordsetTypeEnum.dbOpenForwardOnly,
DAO.RecordsetOptionEnum.dbReadOnly)
' Print the values for the fields in the debug window.
For Each fld In rst.Fields
Debug.WriteLine(fld.Value.ToString & ";")
Next
Debug.WriteLine("")
' Close the Recordset.
rst.Close()
End Sub
Unbehandelte Fehler in Ereignishandlern
Ein häufiges Interopproblem betrifft Fehler in Ereignishandlern, die von COM-Objekten ausgelöste Ereignisse behandeln. Solche Fehler werden ignoriert, es sei denn, Sie suchen gezielt nach Fehlern mithilfe von On Error
- oder Try...Catch...Finally
-Anweisungen. Das folgende Beispiel stammt beispielsweise aus einem Visual Basic .NET-Projekt mit einem Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8 Library.
' To use this example, add a reference to the
' Microsoft ActiveX Data Objects 2.8 Library
' from the COM tab of the project references page.
Dim WithEvents cn As New ADODB.Connection
Sub ADODBConnect()
cn.ConnectionString = "..."
cn.Open()
MsgBox(cn.ConnectionString)
End Sub
Private Sub Form1_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load
ADODBConnect()
End Sub
Private Sub cn_ConnectComplete(
ByVal pError As ADODB.Error,
ByRef adStatus As ADODB.EventStatusEnum,
ByVal pConnection As ADODB.Connection) Handles cn.ConnectComplete
' This is the event handler for the cn_ConnectComplete event raised
' by the ADODB.Connection object when a database is opened.
Dim x As Integer = 6
Dim y As Integer = 0
Try
x = CInt(x / y) ' Attempt to divide by zero.
' This procedure would fail silently without exception handling.
Catch ex As Exception
MsgBox("There was an error: " & ex.Message)
End Try
End Sub
In diesem Beispiel wird ein Fehler wie erwartet ausgelöst. Wenn Sie jedoch dasselbe Beispiel ohne den Try...Catch...Finally
-Block ausprobieren, wird der Fehler ignoriert, als ob Sie die OnError Resume Next
-Anweisung verwendet haben. Ohne Fehlerbehandlung tritt bei der Division durch 0 unbemerkt ein Fehler auf. Da solche Fehler niemals unbehandelte Ausnahmefehler auslösen, ist es wichtig, dass Sie eine Form der Ausnahmebehandlung in Ereignishandlern verwenden, die Ereignisse von COM-Objekten behandeln.
Grundlegendes zu COM-Interoperabilitätsfehlern
Ohne Fehlerbehandlung generieren Interop-Aufrufe häufig Fehler, die kleine Informationen bereitstellen. Verwenden Sie nach Möglichkeit eine strukturierte Fehlerbehandlung, um weitere Informationen zu Problemen bereitzustellen, wenn sie auftreten. Dies kann besonders hilfreich sein, wenn Sie Anwendungen debuggen. Zum Beispiel:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Sie finden Informationen wie die Fehlerbeschreibung, HRESULT und die Quelle von COM-Fehlern, indem Sie den Inhalt des Ausnahmeobjekts untersuchen.
ActiveX-Steuerelementprobleme
Die meisten ActiveX-Steuerelemente, die mit Visual Basic 6.0 arbeiten, funktionieren ohne Probleme mit Visual Basic .NET. Die wichtigsten Ausnahmen sind Containersteuerelemente oder Steuerelemente, die visuell andere Steuerelemente enthalten. Einige Beispiele für ältere Steuerelemente, die mit Visual Studio nicht ordnungsgemäß funktionieren, sind wie folgt:
Microsoft Forms 2.0-Framesteuerelement
Up-Down Steuerelement, auch als Spinsteuerung bezeichnet
Sheridan-Registersteuerelement
Es gibt nur wenige Problemumgehungen für Probleme mit nicht unterstützten ActiveX-Steuerelementen. Sie können vorhandene Steuerelemente zu Visual Studio migrieren, wenn Sie den ursprünglichen Quellcode besitzen. Andernfalls können Sie sich an Softwareanbieter wenden, um aktualisierte .NET-kompatible Versionen von Steuerelementen zu erhalten, die die nicht unterstützten ActiveX-Steuerelemente ersetzen können.
ByRef-Übergeben von ReadOnly-Eigenschaften von Steuerelementen
Visual Basic löst manchmal COM-Fehler aus, z. B. „Error 0x800A017F CTL_E_SETNOTSUPPORTED“, wenn Sie ReadOnly
-Eigenschaften einiger älterer ActiveX-Steuerelemente als ByRef
-Parameter an andere Prozeduren übergeben. Ähnliche Prozeduraufrufe von Visual Basic 6.0 lösen keinen Fehler aus, und die Parameter werden behandelt, als ob Sie sie als Wert übergeben hätten. Die Visual Basic .NET-Fehlermeldung gibt an, dass Sie versuchen, eine Eigenschaft zu ändern, die keine Eigenschaft Set
Prozedur aufweist.
Wenn Sie Zugriff auf die aufgerufene Prozedur haben, können Sie diesen Fehler verhindern, indem Sie das schlüsselwort ByVal
verwenden, um Parameter zu deklarieren, die ReadOnly
Eigenschaften akzeptieren. Zum Beispiel:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Wenn Sie keinen Zugriff auf den Quellcode für die aufgerufene Prozedur haben, können Sie erzwingen, dass die Eigenschaft nach Wert übergeben wird, indem Sie eine zusätzliche Gruppe von Klammern um die aufrufende Prozedur hinzufügen. In einem Projekt mit einem Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8 Library können Sie beispielsweise Folgendes verwenden:
Sub PassByVal(ByVal pError As ADODB.Error)
' The extra set of parentheses around the arguments
' forces them to be passed by value.
ProcessParams((pError.Description))
End Sub
Bereitstellen von Assemblys, die Interop verfügbar machen
Das Bereitstellen von Assemblys, die COM-Schnittstellen verfügbar machen, stellt einige eindeutige Herausforderungen dar. Beispielsweise tritt ein potenzielles Problem auf, wenn separate Anwendungen auf dieselbe COM-Assembly verweisen. Diese Situation ist üblich, wenn eine neue Version einer Assembly installiert ist und eine andere Anwendung weiterhin die alte Version der Assembly verwendet. Wenn Sie eine Assembly deinstallieren, die eine DLL gemeinsam verwendet, können Sie sie unbeabsichtigt für die anderen Assemblies unverfügbar machen.
Um dieses Problem zu vermeiden, sollten Sie freigegebene Assemblys im globalen Assemblycache (GAC) installieren und ein MergeModule für die Komponente verwenden. Wenn Sie die Anwendung nicht im GAC installieren können, sollte sie in CommonFilesFolder in einem versionsspezifischen Unterverzeichnis installiert werden.
Nicht gemeinsam genutzte Assemblys sollten sich neben der aufrufenden Anruf im selben Verzeichnis befinden.