Delen via


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_2zijnSynch.

De naamswijziging kan twee problemen veroorzaken voor gebruikers van het COM-object.

  1. Clients verwachten mogelijk niet de gegenereerde methodenamen.

  2. 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 MyComClassheeft, 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.

Zie ook