Problembehandlung bei Interoperabilität (Visual Basic)
Bei der gemeinsamen Verwendung von COM und verwaltetem Code von .NET Framework sind möglicherweise die folgenden allgemeinen Themen von Belang.
Interop-Marshalling
Gelegentlich müssen Sie Datentypen verwenden, die nicht Bestandteil von .NET Framework sind. Interop-Assemblys bewältigen den Großteil der für COM-Objekte erforderlichen Aufgaben. Möglicherweise müssen Sie jedoch die Datentypen steuern, die beim Verfügbarmachen verwalteter Objekte für COM verwendet werden. Strukturen in Klassenbibliotheken müssen z. B. den nicht verwalteten Typ BStr für Zeichenfolgen angeben, die an mit Visual Basic 6.0 und älteren Versionen erstellte COM-Objekte gesendet werden. In diesen Fällen können Sie das MarshalAsAttribute-Attribut verwenden, damit verwaltete Typen als nicht verwaltete Typen verfügbar gemacht werden.
Exportieren von Zeichenfolgen fester Länge in nicht verwalteten Code
Bei Visual Basic 6.0 und älteren Versionen werden Zeichenfolgen als Bytefolgen ohne abschließendes NULL-Zeichen in COM-Objekte exportiert. Aus Gründen der Kompatibilität mit anderen Sprachen fügt Visual Basic 2005 beim Exportieren von Zeichenfolgen ein abschließendes Zeichen ein. Um diese Inkompatibilität zu beheben, können Sie Zeichenfolgen ohne abschließendes Zeichen als Byte oder Char exportieren.
Exportieren von Vererbungshierarchien
Hierarchien verwalteter Klassen gehen beim Verfügbar machen als COM-Objekte verloren. Wenn Sie z. B. eine Basisklasse mit einem Member definieren und diese an eine abgeleitete Klasse vererben, die als COM-Objekt offen gelegt wird, können Clients, die diese abgeleitete Klasse als COM-Objekt verwenden, die geerbten Member nicht verwenden. Auf Basisklassenmember kann von COM-Objekten nur als Instanzen einer Basisklasse zugegriffen werden, und dies auch nur dann, wenn die Basisklasse ebenfalls als COM-Objekt erstellt wurde.
Überladene Methoden
Sie können mit Visual Basic zwar überladene Methoden erstellen, diese werden jedoch von COM nicht unterstützt. Wenn eine Klasse, die überladene Methoden enthält, als COM-Objekt verfügbar gemacht wird, werden für die überladenen Methoden neue Methodennamen generiert.
Angenommen, Sie verfügen über eine Klasse mit zwei Überladungen der Synch-Methode. Wenn die Klasse als COM-Objekt verfügbar gemacht wird, könnten die neu generierten Methodennamen Synch und Synch_2 lauten.
Die Umbenennung kann für Benutzer des COM-Objekts zu zwei Problemen führen.
Möglicherweise erwarten die Clients die generierten Methodennamen nicht.
Die generierten Methodennamen in der als COM-Objekt verfügbar gemachten Klasse ändern sich möglicherweise, wenn der Klasse oder ihrer Basisklasse neue Überladungen hinzugefügt werden. Dies kann Versionsprobleme verursachen.
Zum Lösen beider Probleme müssen Sie bei der Entwicklung von Objekten, die als COM-Objekte verfügbar gemacht werden, jeder Methode einen eindeutigen Namen zuweisen, anstatt Überladungen zu verwenden.
Verwenden von COM-Objekten über Interop-Assemblys
Interop-Assemblys lassen sich nahezu wie Ersetzungen der von ihnen dargestellten COM-Objekte durch verwalteten Code verwenden. Da sie aber Wrapper und keine eigentlichen COM-Objekte sind, weist die Verwendung von Interop-Assemblys und Standardassemblys einige Unterschiede auf. Diese betreffen das Verfügbar machen von Klassen ebenso wie Datentypen für Parameter und Rückgabewerte.
Klassen, die als Schnittstellen und als Klassen offen gelegt werden
Im Gegensatz zu Klassen in Standardassemblys werden COM-Klassen in Interop-Assemblys sowohl als Schnittstelle als auch als Klasse offen gelegt, die die COM-Klasse darstellt. Der Name der Schnittstelle ist mit dem der COM-Klasse identisch. Der Name der Interop-Klasse stimmt mit dem Namen der ursprünglichen COM-Klasse überein, allerdings wird "Class" angefügt. Angenommen, ein Projekt enthält einen Verweis auf eine Interop-Assembly für ein COM-Objekt. Wenn die COM-Klasse den Namen MyComClass hat, wird in IntelliSense und im Objektkatalog die Schnittstelle MyComClass und die Klasse MyComClassClass angezeigt.
Erstellen von Instanzen einer .NET Framework-Klasse
Im Allgemeinen erstellen Sie eine Instanz einer .NET Framework-Klasse mithilfe der New-Anweisung und einem Klassennamen. Sie können die New-Anweisung nur dann mit einer Schnittstelle verwenden, wenn Sie über eine durch eine Interop-Assembly dargestellte COM-Klasse verfügen. Wenn Sie die COM-Klasse nicht mit einer Inherits-Anweisung verwenden, können Sie die Schnittstelle wie eine Klasse verwenden. Der folgende Code demonstriert die Erstellung eines Command-Objekts in einem Projekt, das über einen Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8-Bibliothek verfügt:
Dim cmd As New ADODB.Command
Bei Verwendung der COM-Klasse als Basis für eine abgeleitete Klasse müssen Sie jedoch die Interop-Klasse verwenden, die die COM-Klasse darstellt, wie im folgenden Code gezeigt:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Tipp
Interop-Assemblys implementieren implizit Schnittstellen, die COM-Klassen darstellen. Implementieren Sie diese Schnittstellen nicht mit der Implements-Anweisung. Dies löst einen Fehler aus.
Datentypen für Parameter und Rückgabewerte
Im Gegensatz zu Membern von Standardassemblys können Member von Interop-Assemblys über Datentypen verfügen, die nicht mit denen in der ursprünglichen Deklaration des Objekts übereinstimmen. Interop-Assemblys konvertieren COM-Typen implizit in mit Common Language Runtime kompatible Typen. Sie sollten jedoch die auf beiden Seiten verwendeten Datentypen überprüfen, um Laufzeitfehler zu vermeiden. Bei COM-Objekten, die in Visual Basic 6.0 und älteren Versionen erstellt wurden, gehen z. B. Werte vom Typ Integer vom äquivalenten .NET Framework-Typ (Short) aus. Verwenden Sie nach Möglichkeit den Objektkatalog, um die Merkmale importierter Member vor der Verwendung zu überprüfen.
COM‑Methoden auf Modulebene
Die meisten COM‑Objekte werden verwendet, indem mit dem New-Schlüsselwort eine Instanz einer COM‑Klasse erstellt wird und dann die Methoden des Objekts aufgerufen werden. Eine Ausnahme dieser Regel betrifft COM-Objekte, die AppObj- oder GlobalMultiUse-COM-Klassen enthalten. Solche Klassen entsprechen den Methoden auf Modulebene in Visual Basic 2005-Klassen. Visual Basic 6.0 und frühere Versionen erstellen Instanzen solcher Objekte implizit, wenn Sie das erste Mal eine ihrer Methoden aufrufen. Beispielsweise können Sie in Visual Basic 6.0 einen Verweis auf die Microsoft DAO 3.6 Object-Bibliothek hinzufügen und die DBEngine-Methode aufrufen, ohne zuerst eine Instanz erstellen zu müssen:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
In Visual Basic 2005 müssen immer zuerst Instanzen von COM-Objekten erstellt werden, bevor Sie deren Methoden verwenden können. Um diese Methoden in Visual Basic 2005 zu verwenden, deklarieren Sie eine Variable der gewünschten Klasse und weisen das Objekt mit dem new-Schlüsselwort der Objektvariable zu. Das Shared-Schlüsselwort können Sie verwenden, 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
Nicht behandelte Fehler in Ereignishandlern
Ein häufig vorkommendes Interop-Problem hängt mit Fehlern in Ereignishandlern zusammen, die durch COM‑Objekte ausgelöste Ereignisse behandeln. Solche Fehler werden ignoriert, es sei denn, Sie führen mit einer On Error-Anweisung oder einer Try...Catch...Finally-Anweisung explizit eine Fehlerprüfung durch. Das folgende Beispiel stammt aus einem Visual Basic 2005-Projekt mit einem Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8-Bibliothek.
' 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 =
"Provider=Microsoft.Jet.OLEDB.4.0;" &
"Data Source=C:\NWIND.MDB"
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 den gleichen Beispielcode jedoch ohne den Try...Catch...Finally-Block ausführen, wird der Fehler ignoriert, so als ob Sie die OnError Resume Next-Anweisung verwendet hätten. Ohne die Fehlerbehandlung schlägt die Division durch Null ohne weitere Meldung fehl. Da diese Fehler zu keiner Zeit unbehandelte Ausnahmefehler auslösen, müssen Sie in Ereignishandlern für Ereignisse aus COM-Objekten eine Form der Ausnahmebehandlung anwenden.
Untersuchen von COM‑Interop-Fehlern
Ohne Fehlerbehandlung lösen Interop-Aufrufe häufig Fehler aus, die nur wenig Hintergrundinformationen enthalten. Verwenden Sie, wenn möglich, eine strukturierte Fehlerbehandlung, um mehr Informationen über die Probleme bereitzustellen, die eventuell auftreten. Dies kann beim Debuggen von Anwendungen besonders hilfreich sein. Beispiel:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Sie können Informationen wie die Fehlerbeschreibung, HRESULT, und den Ursprung von COM-Fehlern abrufen, indem Sie den Inhalt des Ausnahmeobjekts untersuchen.
Probleme mit ActiveX-Steuerelementen
Die meisten ActiveX-Steuerelemente, die in Visual Basic 6.0 funktionieren, können auch in Visual Basic 2005 problemlos verwendet werden. Die wichtigste Ausnahme hierzu stellen Containersteuerelemente dar, oder Steuerelemente, die andere Steuerelemente sichtbar enthalten. Einige Beispiele für ältere Steuerelemente, die in Visual Studio nicht ordnungsgemäß ausgeführt werden:
Microsoft Forms 2.0 Frame-Steuerelement
Auf-Ab-Steuerelement, auch Drehfeld-Steuerelement genannt
Sheridan-Registersteuerelement
Für Probleme mit nicht unterstützten ActiveX-Steuerelementen gibt es nur wenige Problemumgehungen. Sie können vorhandene Steuerelemente in Visual Studio migrieren, wenn Sie im Besitz des ursprünglichen Quellcodes sind. Andernfalls können Sie bei Softwareanbietern nach aktualisierten .NET-kompatiblen Versionen von Steuerelementen fragen, mit denen Sie nicht unterstützte ActiveX-Steuerelemente ersetzen können.
Übergabe der ReadOnly-Eigenschaften von Steuerelementen als ByRef-Parameter
Visual Basic 2005 löst unter Umständen COM-Fehler (z. B. "Error 0x800A017F CTL_E_SETNOTSUPPORTED") aus, wenn ReadOnly-Eigenschaften älterer ActiveX-Steuerelemente als ByRef-Parameter an andere Prozeduren übergeben werden. Ähnliche Prozeduraufrufe in Visual Basic 6.0 lösen keinen Fehler aus, und die Parameter werden behandelt, als ob Sie sie nach Wert übergeben würden. Mit der in Visual Basic 2005 angezeigten Fehlermeldung meldet das COM-Objekt, dass Sie versuchen eine Eigenschaft ohne Set-Eigenschaftenprozedur zu ändern.
Wenn Sie Zugriff auf die aufzurufende Prozedur haben, können Sie diesen Fehler verhindern, indem Sie mit dem ByVal-Schlüsselwort Parameter deklarieren, die ReadOnly-Eigenschaften akzeptieren. Beispiel:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Wenn Sie keinen Zugriff auf den Quellcode für die aufzurufende Prozedur haben, können Sie die Übergabe der Eigenschaft nach Wert erzwingen, indem Sie ein zusätzliches Paar eckige Klammern um die aufrufende Prozedur herum einfügen. So können Sie z. B. in einem Projekt mit einem Verweis auf das COM-Objekt der Microsoft ActiveX Data Objects 2.8-Bibliothek 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
Beim Bereitstellen von Assemblys, die COM‑Schnittstellen verfügbar machen, sind einige besondere Herausforderungen zu bewältigen. Beispielsweise entsteht ein potenzielles Problem, wenn separate Anwendungen auf die gleiche COM-Assembly verweisen. Diese Situation tritt häufig ein, wenn eine neue Version einer Assembly installiert wird und gleichzeitig eine andere Anwendung noch die alte Version der Assembly verwendet. Wenn eine Assembly mit einer gemeinsam verwendeten DLL deinstalliert wird, steht diese DLL den anderen Assemblys möglicherweise nicht mehr zur Verfügung.
Um dies zu verhindern, installieren Sie freigegebene Assemblys im globalen Assemblycache und verwenden für die Komponenten ein Mergemodul. Wenn Sie die Anwendung nicht im GAC installieren können, installieren Sie sie in einem versionsspezifischen Unterverzeichnis des CommonFilesFolder.
Nicht freigegebene Assemblys sollten sich im gleichen Verzeichnis befinden wie die aufrufende Anwendung.
Siehe auch
Aufgaben
Exemplarische Vorgehensweise: Implementieren der Vererbung mit COM-Objekten (Visual Basic)
Gewusst wie: Hinzufügen von Mergemodulen zu einem Bereitstellungsprojekt
Referenz
Tlbimp.exe (Type Library Importer-Tool)
Tlbexp.exe (Type Library Exporter-Tool)