Risoluzione dei problemi di 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.
Interop Marshalling
In alcuni casi, 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 tipo BStr
non gestito sulle stringhe inviate agli oggetti COM creati da Visual Basic 6.0 e versioni precedenti. In questi casi, è possibile usare l'attributo MarshalAsAttribute per far sì che i tipi gestiti vengano esposti come tipi non gestiti.
Esportazione di stringhe Fixed-Length in 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 di gerarchie di ereditarietà
Le gerarchie della classe gestita si appiattiscono quando vengono 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 di base e quindi solo se la classe base viene creata anche come oggetto COM.
Metodi sovraccaricati
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 sovraccarichi del metodo Synch
. Quando la classe viene esposta come oggetto COM, i nuovi nomi di metodi generati possono essere Synch
e Synch_2
.
La ridenominazione può causare due problemi per gli utilizzatori 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à
Gli assembly di interoperabilità vengono usati 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 sia come interfacce sia come 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 New
con un'interfaccia . 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 oggetto Command
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 si verifica 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. Anche se gli assembly di interoperabilità convertono 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 parola chiave New
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. Ad esempio, in Visual Basic 6.0 è possibile aggiungere un riferimento alla libreria oggetti microsoft DAO 3.6 e chiamare il metodo DBEngine
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 nuova parola chiave per assegnare l'oggetto alla variabile oggetto. La parola chiave Shared
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 comporta errori nei gestori eventi che gestiscono gli eventi generati dagli oggetti COM. Tali errori vengono ignorati a meno che non si verifichino in modo specifico la presenza di errori usando istruzioni On Error
o Try...Catch...Finally
. Ad esempio, l'esempio seguente proviene da un progetto .NET di Visual Basic con un riferimento all'oggetto COM 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
Questo esempio genera un errore come previsto. Tuttavia, se si tenta lo stesso esempio senza il blocco Try...Catch...Finally
, l'errore viene ignorato come se fosse stata usata l'istruzione OnError Resume Next
. Senza la gestione degli errori, la divisione per zero fallisce silenziosamente. 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.
Comprendere gli 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 strutturata degli errori per fornire ulteriori informazioni sui problemi quando si verificano. Ciò può risultare particolarmente utile quando si esegue il debug delle applicazioni. Per esempio:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
È possibile trovare informazioni quali 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 di Microsoft Forms 2.0
Up-Down controllo, noto anche come controllo spin
Controllo scheda 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 alternativa, è possibile contattare i fornitori di software per ottenere versioni aggiornate dei controlli compatibili con .NET e sostituire i controlli ActiveX non supportati.
Passaggio delle proprietà ReadOnly dei controlli ByRef
Visual Basic genera talvolta errori COM, ad esempio "Errore 0x800A017F CTL_E_SETNOTSUPPORTED", quando vengono passate proprietà ReadOnly
di alcuni controlli ActiveX più vecchi come parametri ByRef
ad altre procedure. Le chiamate di routine simili da Visual Basic 6.0 non generano un errore e i parametri vengono considerati come se siano stati 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 procedura di proprietà Set
.
Se si ha accesso alla procedura chiamata, è possibile evitare questo errore usando la parola chiave ByVal
per dichiarare i parametri che accettano ReadOnly
proprietà. Per 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 l'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 per versione.
Gli assembly non condivisi devono trovarsi affiancati nella directory con l'applicazione chiamante.