Risoluzione dei problemi relativi all'interoperabilità (Visual Basic)
Quando si interagisce tra COM e il codice gestito di .NET Framework, è possibile che si verifichino uno o più dei problemi comuni seguenti.
Marshalling di interoperabilità
A volte, potrebbe essere necessario usare tipi di dati che non fanno parte di .NET Framework. Gli assembly di interoperabilità gestiscono la maggior parte del lavoro per gli oggetti COM, ma potrebbe essere necessario controllare i tipi di dati utilizzati quando gli oggetti gestiti vengono esposti a COM. Ad esempio, le strutture nelle librerie di classi devono specificare il BStr
tipo non gestito nelle stringhe inviate agli oggetti COM creati da Visual Basic 6.0 e versioni precedenti. In questi casi, è possibile usare l'attributo MarshalAsAttribute per fare in modo che i tipi gestiti vengano esposti come tipi non gestiti.
Esportazione di stringhe Fixed-Length nel codice non gestito
In Visual Basic 6.0 e versioni precedenti le stringhe vengono esportate in oggetti COM come sequenze di byte senza un carattere di terminazione Null. Per la compatibilità con altri linguaggi, Visual Basic .NET include un carattere di terminazione durante l'esportazione di stringhe. Il modo migliore per risolvere questa incompatibilità consiste nell'esportare stringhe che non dispongono del carattere di terminazione come matrici di Byte
o Char
.
Esportazione delle gerarchie di ereditarietà
Le gerarchie della classe gestita si appiattiscono quando esposte come oggetti COM. Ad esempio, se si definisce una classe base con un membro e quindi si eredita la classe base in una classe derivata esposta come oggetto COM, i client che usano la classe derivata nell'oggetto COM non potranno utilizzare i membri ereditati. È possibile accedere ai membri della classe base da oggetti COM solo come istanze di una classe base e quindi solo se la classe di base viene creata anche come oggetto COM.
Metodi di overload
Sebbene sia possibile creare metodi di overload con Visual Basic, non sono supportati da COM. Quando una classe che contiene metodi di overload viene esposta come oggetto COM, vengono generati nuovi nomi di metodo per i metodi di overload.
Si consideri ad esempio una classe con due overload del Synch
metodo . Quando la classe viene esposta come oggetto COM, i nuovi nomi dei metodi generati potrebbero essere Synch
e Synch_2
.
La ridenominazione può causare due problemi per i consumer dell'oggetto COM.
I client potrebbero non aspettarsi i nomi dei metodi generati.
I nomi dei metodi generati nella classe esposta come oggetto COM possono cambiare quando vengono aggiunti nuovi overload alla classe o alla relativa classe di base. Ciò può causare problemi di controllo delle versioni.
Per risolvere entrambi i problemi, assegnare a ogni metodo un nome univoco, anziché usare l'overload, quando si sviluppano oggetti che verranno esposti come oggetti COM.
Uso di oggetti COM tramite assembly di interoperabilità
Si usano assembly di interoperabilità quasi come se fossero sostituzioni di codice gestito per gli oggetti COM rappresentati. Tuttavia, poiché sono wrapper e non oggetti COM effettivi, esistono alcune differenze tra l'uso di assembly di interoperabilità e assembly standard. Queste aree di differenza includono l'esposizione delle classi e i tipi di dati per i parametri e i valori restituiti.
Classi esposte come interfacce e classi
A differenza delle classi negli assembly standard, le classi COM vengono esposte in assembly di interoperabilità sia come interfaccia che come classe che rappresenta la classe COM. Il nome dell'interfaccia è identico a quello della classe COM. Il nome della classe di interoperabilità è uguale a quello della classe COM originale, ma con la parola "Class" aggiunta. Si supponga, ad esempio, di avere un progetto con un riferimento a un assembly di interoperabilità per un oggetto COM. Se la classe COM è denominata MyComClass
, IntelliSense e Visualizzatore oggetti mostrano un'interfaccia denominata MyComClass
e una classe denominata MyComClassClass
.
Creazione di istanze di una classe .NET Framework
In genere, si crea un'istanza di una classe .NET Framework usando l'istruzione New
con un nome di classe. La presenza di una classe COM rappresentata da un assembly di interoperabilità è l'unico caso in cui è possibile usare l'istruzione con un'interfaccia New
. A meno che non si usi la classe COM con un'istruzione Inherits
, è possibile usare l'interfaccia esattamente come si farebbe con una classe. Il codice seguente illustra come creare un Command
oggetto in un progetto con un riferimento all'oggetto COM microsoft ActiveX Data Objects 2.8 Library:
Dim cmd As New ADODB.Command
Tuttavia, se si usa la classe COM come base per una classe derivata, è necessario utilizzare la classe di interoperabilità che rappresenta la classe COM, come nel codice seguente:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Nota
Gli assembly di interoperabilità implementano in modo implicito interfacce che rappresentano classi COM. Non è consigliabile provare a usare l'istruzione Implements
per implementare queste interfacce o verrà generato un errore.
Tipi di dati per parametri e valori restituiti
A differenza dei membri degli assembly standard, i membri dell'assembly di interoperabilità possono avere tipi di dati diversi da quelli usati nella dichiarazione dell'oggetto originale. Sebbene gli assembly di interoperabilità convertano in modo implicito i tipi COM in tipi Common Language Runtime compatibili, è necessario prestare attenzione ai tipi di dati usati da entrambi i lati per evitare errori di runtime. Ad esempio, negli oggetti COM creati in Visual Basic 6.0 e versioni precedenti, i valori di tipo Integer
presuppongono il tipo equivalente di .NET Framework, Short
. È consigliabile usare Visualizzatore oggetti per esaminare le caratteristiche dei membri importati prima di usarli.
Metodi COM a livello di modulo
La maggior parte degli oggetti COM viene usata creando un'istanza di una classe COM usando la New
parola chiave e quindi chiamando i metodi dell'oggetto . Un'eccezione a questa regola prevede oggetti COM che contengono AppObj
o GlobalMultiUse
classi COM. Tali classi sono simili ai metodi a livello di modulo nelle classi .NET di Visual Basic. Visual Basic 6.0 e versioni precedenti creano in modo implicito istanze di tali oggetti per la prima volta che si chiama uno dei relativi metodi. In Visual Basic 6.0, ad esempio, è possibile aggiungere un riferimento alla libreria di oggetti Microsoft DAO 3.6 e chiamare il DBEngine
metodo senza prima creare un'istanza:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Visual Basic .NET richiede di creare sempre istanze di oggetti COM prima di poter usare i relativi metodi. Per usare questi metodi in Visual Basic, dichiarare una variabile della classe desiderata e usare la parola chiave new per assegnare l'oggetto alla variabile oggetto. La Shared
parola chiave può essere usata quando si vuole assicurarsi che venga creata una sola istanza della classe .
' 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
Errori non gestiti nei gestori eventi
Un problema di interoperabilità comune riguarda gli errori nei gestori eventi che gestiscono gli eventi generati dagli oggetti COM. Tali errori vengono ignorati a meno che non vengano verificati in modo specifico la presenza di errori tramite On Error
istruzioni o Try...Catch...Finally
. Ad esempio, l'esempio seguente proviene da un progetto .NET di Visual Basic con un riferimento all'oggetto COM della libreria Microsoft ActiveX Data Objects 2.8.
' 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 questo esempio viene generato un errore come previsto. Tuttavia, se si tenta lo stesso esempio senza il Try...Catch...Finally
blocco, l'errore viene ignorato come se fosse stata usata l'istruzione OnError Resume Next
. Senza la gestione degli errori, la divisione per zero ha esito negativo automaticamente. Poiché tali errori non generano mai errori di eccezione non gestiti, è importante usare una forma di gestione delle eccezioni nei gestori eventi che gestiscono eventi da oggetti COM.
Informazioni sugli errori di interoperabilità COM
Senza la gestione degli errori, le chiamate di interoperabilità spesso generano errori che forniscono poche informazioni. Quando possibile, usare la gestione degli errori strutturati per fornire ulteriori informazioni sui problemi che si verificano. Ciò può risultare particolarmente utile quando si esegue il debug delle applicazioni. Ad esempio:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
È possibile trovare informazioni come la descrizione dell'errore, HRESULT e l'origine degli errori COM esaminando il contenuto dell'oggetto eccezione.
Problemi di controllo ActiveX
La maggior parte dei controlli ActiveX che funzionano con Visual Basic 6.0 funziona con Visual Basic .NET senza problemi. Le eccezioni principali sono controlli contenitore o controlli che contengono visivamente altri controlli. Di seguito sono riportati alcuni esempi di controlli meno recenti che non funzionano correttamente con Visual Studio:
controllo Frame Microsoft Forms 2.0
controllo Up-Down, noto anche come controllo spin
Controllo Tab Sheridan
Esistono solo alcune soluzioni alternative per problemi di controllo ActiveX non supportati. È possibile eseguire la migrazione dei controlli esistenti a Visual Studio se si è proprietari del codice sorgente originale. In caso contrario, è possibile rivolgersi ai fornitori di software per l'aggiornamento di . Versioni compatibili con NET dei controlli per sostituire i controlli ActiveX non supportati.
Passaggio delle proprietà ReadOnly dei controlli ByRef
Visual Basic .NET talvolta genera errori COM, ad esempio "Error 0x800A017F CTL_E_SETNOTSUPPORTED", quando si passano ReadOnly
proprietà di alcuni controlli ActiveX meno recenti come ByRef
parametri ad altre procedure. Le chiamate di routine simili da Visual Basic 6.0 non generano un errore e i parametri vengono considerati come se passati per valore. Il messaggio di errore di Visual Basic .NET indica che si sta tentando di modificare una proprietà che non dispone di una routine di proprietà Set
.
Se si ha accesso alla procedura chiamata, è possibile evitare questo errore usando la ByVal
parola chiave per dichiarare i parametri che accettano ReadOnly
proprietà. Ad esempio:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Se non si ha accesso al codice sorgente per la routine chiamata, è possibile forzare il passaggio della proprietà in base al valore aggiungendo un set aggiuntivo di parentesi quadre intorno alla routine chiamante. Ad esempio, in un progetto con un riferimento all'oggetto COM della libreria Microsoft ActiveX Data Objects 2.8, è possibile usare:
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
Distribuzione di assembly che espongono interoperabilità
La distribuzione di assembly che espongono interfacce COM presenta alcune problematiche specifiche. Ad esempio, si verifica un potenziale problema quando le applicazioni separate fanno riferimento allo stesso assembly COM. Questa situazione è comune quando viene installata una nuova versione di un assembly e un'altra applicazione usa ancora la versione precedente dell'assembly. Se si disinstalla un assembly che condivide una DLL, è possibile renderlo involontariamente non disponibile per gli altri assembly.
Per evitare questo problema, è necessario installare assembly condivisi nella Global Assembly Cache (GAC) e usare mergeModule per il componente. Se non è possibile installare l'applicazione nella GAC, deve essere installata in CommonFilesFolder in una sottodirectory specifica della versione.
Gli assembly non condivisi devono trovarsi affiancati nella directory con l'applicazione chiamante.