Problemen met interoperabiliteit oplossen (Visual Basic)
Wanneer u werkt tussen COM en de beheerde code van de .NET Framework, kunnen een of meer van de volgende veelvoorkomende problemen optreden.
Interop Marshalling
Soms moet u mogelijk gegevenstypen gebruiken die geen deel uitmaken van de .NET Framework. Interoperabiliteitsassembly's verwerken het grootste deel van het werk voor COM-objecten, maar mogelijk moet u de gegevenstypen beheren die worden gebruikt wanneer beheerde objecten worden blootgesteld aan COM. Structuren in klassebibliotheken moeten bijvoorbeeld het BStr
onbeheerde type opgeven voor tekenreeksen die worden verzonden naar COM-objecten die zijn gemaakt met Visual Basic 6.0 en eerdere versies. In dergelijke gevallen kunt u het MarshalAsAttribute kenmerk gebruiken om ervoor te zorgen dat beheerde typen worden weergegeven als niet-beheerde typen.
Fixed-Length-tekenreeksen exporteren naar niet-beheerde code
In Visual Basic 6.0 en eerdere versies worden tekenreeksen geëxporteerd naar COM-objecten als reeksen van bytes zonder een null-beëindigingsteken. Voor compatibiliteit met andere talen bevat Visual Basic .NET een beëindigingsteken bij het exporteren van tekenreeksen. De beste manier om deze incompatibiliteit aan te pakken, is door tekenreeksen zonder het beëindigingsteken te exporteren als matrices van Byte
of Char
.
Overnamehiërarchieën exporteren
Beheerde klassehiërarchieën worden afgevlakt wanneer ze worden weergegeven als COM-objecten. Als u bijvoorbeeld een basisklasse definieert met een lid en vervolgens de basisklasse overneemt in een afgeleide klasse die wordt weergegeven als een COM-object, kunnen clients die de afgeleide klasse in het COM-object gebruiken, de overgenomen leden niet gebruiken. Basisklasseleden zijn alleen toegankelijk vanuit COM-objecten als exemplaren van een basisklasse en vervolgens alleen als de basisklasse ook als een COM-object is gemaakt.
Overbelaste methoden
Hoewel u overbelaste methoden kunt maken met Visual Basic, worden ze niet ondersteund door COM. Wanneer een klasse die overbelaste methoden bevat, wordt weergegeven als een COM-object, worden nieuwe methodenamen gegenereerd voor de overbelaste methoden.
Denk bijvoorbeeld aan een klasse met twee overbelastingen van de Synch
methode. Wanneer de klasse wordt weergegeven als een COM-object, kunnen de nieuwe gegenereerde methodenamen en Synch_2
zijnSynch
.
De naamswijziging kan twee problemen veroorzaken voor gebruikers van het COM-object.
Clients verwachten mogelijk niet de gegenereerde methodenamen.
De gegenereerde methodenamen in de klasse die als een COM-object wordt weergegeven, kunnen worden gewijzigd wanneer nieuwe overbelastingen worden toegevoegd aan de klasse of de basisklasse. Dit kan problemen met versiebeheer veroorzaken.
Als u beide problemen wilt oplossen, geeft u elke methode een unieke naam, in plaats van overbelasting te gebruiken, wanneer u objecten ontwikkelt die als COM-objecten worden weergegeven.
Gebruik van COM-objecten via interoperabiliteitsassembly's
U gebruikt interoperabiliteitsassembly's bijna alsof het beheerde codevervangingen zijn voor de COM-objecten die ze vertegenwoordigen. Omdat het echter wrappers zijn en geen werkelijke COM-objecten, zijn er enkele verschillen tussen het gebruik van interoperabiliteitsassembly's en standaardassembly's. Deze verschilgebieden omvatten de blootstelling van klassen en gegevenstypen voor parameters en retourwaarden.
Klassen die worden weergegeven als interfaces en klassen
In tegenstelling tot klassen in standaardassembly's worden COM-klassen weergegeven in interop-assembly's als zowel een interface als een klasse die de COM-klasse vertegenwoordigt. De naam van de interface is identiek aan die van de COM-klasse. De naam van de interop-klasse is hetzelfde als die van de oorspronkelijke COM-klasse, maar met het woord 'Klasse' toegevoegd. Stel dat u een project hebt met een verwijzing naar een interoperabiliteitsassembly voor een COM-object. Als de COM-klasse de naam MyComClass
heeft, worden in IntelliSense en de objectbrowser een interface met de naam MyComClass
en een klasse met de naam weergegeven MyComClassClass
.
Exemplaren van een .NET Framework-klasse maken
Over het algemeen maakt u een exemplaar van een .NET Framework-klasse met behulp van de New
instructie met een klassenaam. Het hebben van een COM-klasse die wordt vertegenwoordigd door een interoperabiliteitsassembly is het enige geval waarin u de New
instructie met een interface kunt gebruiken. Tenzij u de COM-klasse met een Inherits
-instructie gebruikt, kunt u de interface net als een klasse gebruiken. De volgende code laat zien hoe u een Command
object maakt in een project met een verwijzing naar het COM-object Microsoft ActiveX Data Objects 2.8 Library:
Dim cmd As New ADODB.Command
Als u echter de COM-klasse gebruikt als basis voor een afgeleide klasse, moet u de interop-klasse gebruiken die de COM-klasse vertegenwoordigt, zoals in de volgende code:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Notitie
Interop-assembly's implementeren impliciet interfaces die COM-klassen vertegenwoordigen. Probeer de Implements
instructie niet te gebruiken om deze interfaces te implementeren, anders treedt er een fout op.
Gegevenstypen voor parameters en retourwaarden
In tegenstelling tot leden van standaardassembly's kunnen leden van een interoperabiliteitsassembly gegevenstypen hebben die verschillen van de typen die in de oorspronkelijke objectdeclaratie zijn gebruikt. Hoewel interoperabiliteitsassembly's impliciet COM-typen converteren naar compatibele runtimetypen voor algemene talen, moet u aandacht besteden aan de gegevenstypen die door beide zijden worden gebruikt om runtimefouten te voorkomen. In COM-objecten die zijn gemaakt in Visual Basic 6.0 en eerdere versies, gaan waarden van het type Integer
bijvoorbeeld uit van het .NET Framework equivalente type, Short
. Het wordt aanbevolen om de objectbrowser te gebruiken om de kenmerken van geïmporteerde leden te onderzoeken voordat u ze gebruikt.
COM-methoden op moduleniveau
De meeste COM-objecten worden gebruikt door een instantie van een COM-klasse te maken met behulp van het New
trefwoord en vervolgens methoden van het object aan te roepen. Een uitzondering op deze regel betreft COM-objecten die of GlobalMultiUse
COM-klassen bevattenAppObj
. Dergelijke klassen lijken op methoden op moduleniveau in Visual Basic .NET-klassen. Visual Basic 6.0 en eerdere versies maken impliciet exemplaren van dergelijke objecten voor de eerste keer dat u een van hun methoden aanroept. In Visual Basic 6.0 kunt u bijvoorbeeld een verwijzing toevoegen naar de Microsoft DAO 3.6-objectbibliotheek en de DBEngine
methode aanroepen zonder eerst een exemplaar te maken:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Visual Basic .NET vereist dat u altijd exemplaren van COM-objecten maakt voordat u de methoden kunt gebruiken. Als u deze methoden in Visual Basic wilt gebruiken, declareert u een variabele van de gewenste klasse en gebruikt u het nieuwe trefwoord om het object toe te wijzen aan de objectvariabele. Het Shared
trefwoord kan worden gebruikt als u ervoor wilt zorgen dat er slechts één exemplaar van de klasse wordt gemaakt.
' 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
Niet-verwerkte fouten in gebeurtenis-handlers
Een veelvoorkomend interoperabiliteitsprobleem betreft fouten in gebeurtenis-handlers die gebeurtenissen verwerken die zijn gegenereerd door COM-objecten. Dergelijke fouten worden genegeerd, tenzij u specifiek controleert op fouten met behulp van On Error
of-instructies Try...Catch...Finally
. Het volgende voorbeeld is bijvoorbeeld afkomstig van een Visual Basic .NET-project met een verwijzing naar het COM-object 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 dit voorbeeld wordt een fout gegenereerd zoals verwacht. Als u echter hetzelfde voorbeeld zonder de Try...Catch...Finally
blokkering probeert, wordt de fout genegeerd alsof u de OnError Resume Next
-instructie hebt gebruikt. Zonder foutafhandeling mislukt de deling door nul op de achtergrond. Omdat dergelijke fouten nooit onverwerkte uitzonderingsfouten veroorzaken, is het belangrijk dat u een vorm van uitzonderingsafhandeling gebruikt in gebeurtenis-handlers die gebeurtenissen van COM-objecten verwerken.
Com-interoperabiliteitsfouten begrijpen
Zonder foutafhandeling genereren interop-aanroepen vaak fouten die weinig informatie bieden. Gebruik waar mogelijk gestructureerde foutafhandeling om meer informatie te geven over problemen wanneer deze zich voordoen. Dit kan vooral handig zijn wanneer u fouten in toepassingen opspoort. Bijvoorbeeld:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
U vindt informatie zoals de foutbeschrijving, HRESULT en de bron van COM-fouten door de inhoud van het uitzonderingsobject te controleren.
Problemen met ActiveX-beheer
De meeste ActiveX-besturingselementen die werken met Visual Basic 6.0 werken probleemloos met Visual Basic .NET. De belangrijkste uitzonderingen zijn containerbesturingselementen of besturingselementen die visueel andere besturingselementen bevatten. Enkele voorbeelden van oudere besturingselementen die niet goed werken met Visual Studio zijn:
Microsoft Forms 2.0-besturingselement Frame
Up-Down, ook wel bekend als het kringregelaar
Sheridan Tab Control
Er zijn slechts enkele tijdelijke oplossingen voor problemen met niet-ondersteunde ActiveX-besturingselementen. U kunt bestaande besturingselementen migreren naar Visual Studio als u eigenaar bent van de oorspronkelijke broncode. Anders kunt u contact op nemen met softwareleveranciers voor bijgewerkte . NET-compatibele versies van besturingselementen ter vervanging van niet-ondersteunde ActiveX-besturingselementen.
ReadOnly-eigenschappen van besturingselementen doorgeven doorRef
Visual Basic .NET veroorzaakt soms COM-fouten, zoals 'Fout 0x800A017F CTL_E_SETNOTSUPPORTED', wanneer u eigenschappen van een aantal oudere ActiveX-besturingselementen doorgeeft ReadOnly
als ByRef
parameters aan andere procedures. Vergelijkbare procedureaanroepen van Visual Basic 6.0 veroorzaken geen fout en de parameters worden behandeld alsof u ze op waarde hebt doorgegeven. Het Visual Basic .NET-foutbericht geeft aan dat u een eigenschap probeert te wijzigen die geen eigenschapsprocedure Set
heeft.
Als u toegang hebt tot de procedure die wordt aangeroepen, kunt u deze fout voorkomen door het ByVal
trefwoord te gebruiken om parameters te declareren die eigenschappen accepteren ReadOnly
. Bijvoorbeeld:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Als u geen toegang hebt tot de broncode voor de procedure die wordt aangeroepen, kunt u afdwingen dat de eigenschap door de waarde wordt doorgegeven door een extra set haakjes rond de aanroepende procedure toe te voegen. In een project met een verwijzing naar het COM-object Microsoft ActiveX Data Objects 2.8 Library kunt u bijvoorbeeld het volgende gebruiken:
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
Assembly's implementeren die interoperabiliteit beschikbaar maken
Het implementeren van assembly's die COM-interfaces beschikbaar maken, biedt een aantal unieke uitdagingen. Er treedt bijvoorbeeld een mogelijk probleem op wanneer afzonderlijke toepassingen verwijzen naar dezelfde COM-assembly. Deze situatie komt vaak voor wanneer een nieuwe versie van een assembly is geïnstalleerd en een andere toepassing nog steeds de oude versie van de assembly gebruikt. Als u een assembly verwijdert die een DLL deelt, kunt u deze onbedoeld niet beschikbaar maken voor de andere assembly's.
U kunt dit probleem voorkomen door gedeelde assembly's te installeren in de Global Assembly Cache (GAC) en een MergeModule voor het onderdeel te gebruiken. Als u de toepassing niet in de GAC kunt installeren, moet deze worden geïnstalleerd in CommonFilesFolder in een versiespecifieke submap.
Assembly's die niet worden gedeeld, moeten zich naast elkaar in de map met de aanroepende toepassing bevinden.