Felsöka samverkan (Visual Basic)
När du samverkar mellan COM och den hanterade koden i .NET Framework kan du stöta på ett eller flera av följande vanliga problem.
Interop Marshalling
Ibland kan du behöva använda datatyper som inte ingår i .NET Framework. Interop-sammansättningar hanterar det mesta av arbetet för COM-objekt, men du kan behöva kontrollera de datatyper som används när hanterade objekt exponeras för COM. Strukturer i klassbibliotek måste till exempel ange den BStr
ohanterade typen för strängar som skickas till COM-objekt som skapats av Visual Basic 6.0 och tidigare versioner. I sådana fall kan du använda MarshalAsAttribute attributet för att orsaka att hanterade typer exponeras som ohanterade typer.
Exportera Fixed-Length strängar till ohanterad kod
I Visual Basic 6.0 och tidigare versioner exporteras strängar till COM-objekt som sekvenser av byte utan null-avslutningstecken. För kompatibilitet med andra språk innehåller Visual Basic .NET ett avslutningstecken vid export av strängar. Det bästa sättet att åtgärda den här inkompatibiliteten är att exportera strängar som saknar avslutningstecken som matriser av Byte
eller Char
.
Exportera arvshierarkier
Hanterade klasshierarkier plattas ut när de exponeras som COM-objekt. Om du till exempel definierar en basklass med en medlem och sedan ärver basklassen i en härledd klass som exponeras som ett COM-objekt, kommer klienter som använder den härledda klassen i COM-objektet inte att kunna använda de ärvda medlemmarna. Basklassmedlemmar kan endast nås från COM-objekt som instanser av en basklass och sedan endast om basklassen också skapas som ett COM-objekt.
Överlagrade metoder
Även om du kan skapa överlagrade metoder med Visual Basic stöds de inte av COM. När en klass som innehåller överlagrade metoder exponeras som ett COM-objekt genereras nya metodnamn för de överlagrade metoderna.
Tänk dig till exempel en klass som har två överlagringar av Synch
metoden. När klassen exponeras som ett COM-objekt kan de nya genererade metodnamnen vara Synch
och Synch_2
.
Namnbytet kan orsaka två problem för användare av COM-objektet.
Klienterna kanske inte förväntar sig de genererade metodnamnen.
De genererade metodnamnen i klassen som exponeras som ett COM-objekt kan ändras när nya överlagringar läggs till i klassen eller dess basklass. Detta kan orsaka versionshanteringsproblem.
Lös båda problemen genom att ge varje metod ett unikt namn, i stället för att använda överlagring, när du utvecklar objekt som ska exponeras som COM-objekt.
Användning av COM-objekt via interop-sammansättningar
Du använder interop-sammansättningar nästan som om de är hanterade kodersättningar för DE COM-objekt som de representerar. Men eftersom de är omslutningar och inte faktiska COM-objekt finns det vissa skillnader mellan att använda interop-sammansättningar och standardsammansättningar. Dessa skillnader omfattar exponering av klasser och datatyper för parametrar och returvärden.
Klasser som exponeras som både gränssnitt och klasser
Till skillnad från klasser i standardsammansättningar exponeras COM-klasser i interop-sammansättningar som både ett gränssnitt och en klass som representerar COM-klassen. Gränssnittets namn är identiskt med namnet på COM-klassen. Namnet på interop-klassen är samma som för den ursprungliga COM-klassen, men med ordet "Klass" tillagt. Anta till exempel att du har ett projekt med en referens till en interop-sammansättning för ett COM-objekt. Om COM-klassen heter MyComClass
visar IntelliSense och Object Browser ett gränssnitt med namnet MyComClass
och en klass med namnet MyComClassClass
.
Skapa instanser av en .NET Framework-klass
I allmänhet skapar du en instans av en .NET Framework-klass med hjälp av -instruktionen New
med ett klassnamn. Att ha en COM-klass som representeras av en interop-sammansättning är det enda fallet där du kan använda -instruktionen New
med ett gränssnitt. Om du inte använder COM-klassen med en Inherits
-instruktion kan du använda gränssnittet precis som du skulle göra med en klass. Följande kod visar hur du skapar ett Command
objekt i ett projekt som har en referens till COM-objektet Microsoft ActiveX Data Objects 2.8 Library:
Dim cmd As New ADODB.Command
Men om du använder COM-klassen som bas för en härledd klass måste du använda interop-klassen som representerar COM-klassen, som i följande kod:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Anteckning
Interop-sammansättningar implementerar implicit gränssnitt som representerar COM-klasser. Du bör inte försöka använda -instruktionen Implements
för att implementera dessa gränssnitt, annars uppstår ett fel.
Datatyper för parametrar och returvärden
Till skillnad från medlemmar i standardsammansättningar kan interop-sammansättningsmedlemmar ha datatyper som skiljer sig från dem som används i den ursprungliga objektdeklarationen. Även om interop-sammansättningar implicit konverterar COM-typer till kompatibla vanliga språkkörningstyper bör du vara uppmärksam på de datatyper som används av båda sidor för att förhindra körningsfel. I TILL exempel COM-objekt som skapats i Visual Basic 6.0 och tidigare versioner förutsätter värden av typen Integer
.NET Framework motsvarande typ, Short
. Vi rekommenderar att du använder Object Browser för att undersöka egenskaperna för importerade medlemmar innan du använder dem.
COM-metoder på modulnivå
De flesta COM-objekt används genom att skapa en instans av en COM-klass med hjälp av nyckelordet New
och sedan anropa metoder för objektet. Ett undantag till den här regeln omfattar COM-objekt som innehåller AppObj
eller GlobalMultiUse
COM-klasser. Sådana klasser liknar metoder på modulnivå i Visual Basic .NET-klasser. Visual Basic 6.0 och tidigare versioner skapar implicit instanser av sådana objekt för första gången som du anropar någon av deras metoder. I Visual Basic 6.0 kan du till exempel lägga till en referens till Microsoft DAO 3.6-objektbiblioteket och anropa DBEngine
metoden utan att först skapa en instans:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Visual Basic .NET kräver att du alltid skapar instanser av COM-objekt innan du kan använda deras metoder. Om du vill använda dessa metoder i Visual Basic deklarerar du en variabel för önskad klass och använder det nya nyckelordet för att tilldela objektet till objektvariabeln. Nyckelordet Shared
kan användas när du vill se till att endast en instans av klassen skapas.
' 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
Ohanterade fel i händelsehanterare
Ett vanligt interop-problem omfattar fel i händelsehanterare som hanterar händelser som genereras av COM-objekt. Sådana fel ignoreras om du inte specifikt söker efter fel med hjälp av On Error
- eller Try...Catch...Finally
-instruktioner. Följande exempel är från ett Visual Basic .NET-projekt som har en referens till COM-objektet 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
Det här exemplet genererar ett fel som förväntat. Men om du provar samma exempel utan Try...Catch...Finally
blocket ignoreras felet som om du använde -instruktionen OnError Resume Next
. Utan felhantering misslyckas divisionen med noll tyst. Eftersom sådana fel aldrig utlöser ohanterade undantagsfel är det viktigt att du använder någon form av undantagshantering i händelsehanterare som hanterar händelser från COM-objekt.
Förstå COM-interopfel
Utan felhantering genererar interop-anrop ofta fel som ger lite information. Använd om möjligt strukturerad felhantering för att ge mer information om problem när de uppstår. Detta kan vara särskilt användbart när du felsöker program. Ett exempel:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Du hittar information som felbeskrivning, HRESULT och källan till COM-fel genom att undersöka innehållet i undantagsobjektet.
Problem med ActiveX-kontroll
De flesta ActiveX-kontroller som fungerar med Visual Basic 6.0 fungerar utan problem med Visual Basic .NET. De viktigaste undantagen är containerkontroller eller kontroller som visuellt innehåller andra kontroller. Några exempel på äldre kontroller som inte fungerar korrekt med Visual Studio är följande:
Microsoft Forms 2.0 Ramkontroll
Up-Down kontroll, även kallat rotationskontroll
Kontroll av flik för
Det finns bara några få lösningar för ActiveX-kontrollproblem som inte stöds. Du kan migrera befintliga kontroller till Visual Studio om du äger den ursprungliga källkoden. Annars kan du fråga programvaruleverantörerna om de har uppdaterat . NET-kompatibla versioner av kontroller som ersätter ActiveX-kontroller som inte stöds.
Skicka ReadOnly-egenskaper för Controls ByRef
Visual Basic .NET genererar ibland COM-fel som "Fel 0x800A017F CTL_E_SETNOTSUPPORTED" när du skickar ReadOnly
egenskaper för vissa äldre ActiveX-kontroller som ByRef
parametrar till andra procedurer. Liknande proceduranrop från Visual Basic 6.0 utlöser inget fel och parametrarna behandlas som om du skickade dem med ett värde. Visual Basic .NET-felmeddelandet anger att du försöker ändra en egenskap som inte har en egenskapsprocedur Set
.
Om du har åtkomst till proceduren som anropas kan du förhindra det här felet genom att använda nyckelordet ByVal
för att deklarera parametrar som accepterar ReadOnly
egenskaper. Ett exempel:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Om du inte har åtkomst till källkoden för proceduren som anropas kan du tvinga egenskapen att skickas med värde genom att lägga till en extra uppsättning hakparenteser runt anropsproceduren. I ett projekt som till exempel har en referens till COM-objektet Microsoft ActiveX Data Objects 2.8 Library kan du använda:
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
Distribuera sammansättningar som exponerar interop
Distribution av sammansättningar som exponerar COM-gränssnitt medför vissa unika utmaningar. Ett potentiellt problem uppstår till exempel när separata program refererar till samma COM-sammansättning. Den här situationen är vanlig när en ny version av en sammansättning installeras och ett annat program fortfarande använder den gamla versionen av sammansättningen. Om du avinstallerar en sammansättning som delar en DLL kan du oavsiktligt göra den otillgänglig för de andra sammansättningarna.
För att undvika det här problemet bör du installera delade sammansättningar i den globala sammansättningscachen (GAC) och använda en MergeModule för komponenten. Om du inte kan installera programmet i GAC bör det installeras på CommonFilesFolder i en versionsspecifik underkatalog.
Sammansättningar som inte delas ska finnas sida vid sida i katalogen med det anropande programmet.