Résolution des problèmes d’interopérabilité (Visual Basic)
Lorsque vous interopérez entre COM et le code managé du .NET Framework, vous pouvez rencontrer un ou plusieurs des problèmes courants suivants.
Marshaling d'interopérabilité
Parfois, vous devrez peut-être utiliser des types de données qui ne font pas partie du .NET Framework. Les assemblys d’interopérabilité gèrent la plupart du travail pour les objets COM, mais vous devrez peut-être contrôler les types de données utilisés lorsque des objets managés sont exposés à COM. Par exemple, les structures des bibliothèques de classes doivent spécifier le type BStr
non managé sur les chaînes envoyées aux objets COM créés par Visual Basic 6.0 et les versions antérieures. Dans ce cas, vous pouvez utiliser l’attribut MarshalAsAttribute pour exposer des types managés en tant que types non managés.
Exportation de chaînes Fixed-Length vers du code non managé
Dans Visual Basic 6.0 et versions antérieures, les chaînes sont exportées vers des objets COM sous forme de séquences d’octets sans caractère d’arrêt Null. Pour la compatibilité avec d’autres langages, Visual Basic .NET inclut un caractère d’arrêt lors de l’exportation de chaînes. La meilleure façon de traiter cette incompatibilité est d'exporter des chaînes qui n'ont pas de caractère de terminaison sous forme de tableaux Byte
ou Char
.
Exportation des hiérarchies d’héritage
Les hiérarchies de classes gérées s'aplatissent lorsqu'elles sont exposées en tant qu'objets COM. Par exemple, si vous définissez une classe de base avec un membre, puis héritez de la classe de base dans une classe dérivée exposée en tant qu’objet COM, les clients qui utilisent la classe dérivée dans l’objet COM ne pourront pas utiliser les membres hérités. Les membres de classe de base sont accessibles à partir d’objets COM uniquement en tant qu’instances d’une classe de base, puis uniquement si la classe de base est également créée en tant qu’objet COM.
Méthodes surchargées
Bien que vous puissiez créer des méthodes surchargées avec Visual Basic, elles ne sont pas prises en charge par COM. Lorsqu’une classe qui contient des méthodes surchargées est exposée en tant qu’objet COM, les nouveaux noms de méthodes sont générés pour les méthodes surchargées.
Par exemple, considérez une classe qui a deux surcharges de la méthode Synch
. Lorsque la classe est exposée en tant qu’objet COM, les nouveaux noms de méthode générés peuvent être Synch
et Synch_2
.
Le changement de nom peut entraîner deux problèmes pour les consommateurs de l’objet COM.
Les clients peuvent ne pas s’attendre à ce que les noms de méthode générés soient conformes à leurs attentes.
Les noms de méthode générés dans la classe exposée en tant qu’objet COM peuvent changer lorsque de nouvelles surcharges sont ajoutées à la classe ou à sa classe de base. Cela peut entraîner des problèmes de contrôle de version.
Pour résoudre les deux problèmes, donnez à chaque méthode un nom unique, au lieu d’utiliser une surcharge, lorsque vous développez des objets qui seront exposés en tant qu’objets COM.
Utilisation d’objets COM via des assemblys d’interopérabilité
Vous utilisez les assemblages interopérables presque comme s'ils étaient des remplacements de code géré pour les objets COM qu'ils représentent. Toutefois, étant donné qu’ils sont des wrappers et non des objets COM réels, il existe des différences entre l’utilisation d’assemblys d’interopérabilité et d’assemblys standard. Ces domaines de différence incluent l’exposition des classes et les types de données pour les paramètres et les valeurs de retour.
Classes exposées en tant qu’interfaces et classes
Contrairement aux classes dans les assemblys standard, les classes COM sont exposées dans les assemblys d’interopérabilité comme une interface et une classe qui représente la classe COM. Le nom de l’interface est identique à celui de la classe COM. Le nom de la classe d’interopérabilité est le même que celui de la classe COM d’origine, mais avec le mot « Classe » ajouté. Par exemple, supposons que vous disposez d’un projet avec une référence à un assembly d’interopérabilité pour un objet COM. Si la classe COM est nommée MyComClass
, IntelliSense et l’Explorateur d’objets affichent une interface nommée MyComClass
et une classe nommée MyComClassClass
.
Création d’instances d’une classe .NET Framework
En règle générale, vous créez une instance d’une classe .NET Framework à l’aide de l’instruction New
avec un nom de classe. La présence d’une classe COM représentée par un assembly d’interopérabilité est le cas dans lequel vous pouvez utiliser l’instruction New
avec une interface. Sauf si vous utilisez la classe COM avec une instruction Inherits
, vous pouvez utiliser l’interface comme vous le feriez pour une classe. Le code suivant montre comment créer un objet Command
dans un projet qui a une référence à l’objet COM bibliothèque Microsoft ActiveX Data Objects 2.8 :
Dim cmd As New ADODB.Command
Toutefois, si vous utilisez la classe COM comme base pour une classe dérivée, vous devez utiliser la classe d’interopérabilité qui représente la classe COM, comme dans le code suivant :
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Remarque
Les assemblys d’interopérabilité implémentent implicitement des interfaces qui représentent des classes COM. Vous ne devez pas essayer d’utiliser l’instruction Implements
pour implémenter ces interfaces ou une erreur se produit.
Types de données pour les paramètres et les valeurs de retour
Contrairement aux membres des assemblies standard, les membres des assemblies d'interopérabilité peuvent avoir des types de données qui diffèrent de ceux utilisés dans la déclaration d'objet d'origine. Bien que les assemblys d’interopérabilité convertissent implicitement les types COM en types Common Language Runtime compatibles, vous devez prêter attention aux types de données utilisés par les deux côtés pour empêcher les erreurs d’exécution. Par exemple, dans les objets COM créés dans Visual Basic 6.0 et versions antérieures, les valeurs de type Integer
supposent le type équivalent .NET Framework, Short
. Il est recommandé d’utiliser l’Explorateur d’objets pour examiner les caractéristiques des membres importés avant de les utiliser.
Méthodes COM au niveau du module
La plupart des objets COM sont utilisés en créant une instance d’une classe COM à l’aide du mot clé New
, puis en appelant des méthodes de l’objet. Une exception à cette règle implique des objets COM qui contiennent AppObj
ou GlobalMultiUse
classes COM. Ces classes ressemblent à des méthodes de niveau module dans les classes .NET Visual Basic. Visual Basic 6.0 et versions antérieures créent implicitement des instances de ces objets pour vous la première fois que vous appelez l’une de leurs méthodes. Par exemple, dans Visual Basic 6.0, vous pouvez ajouter une référence à la bibliothèque d’objets Microsoft DAO 3.6 et appeler la méthode DBEngine
sans créer d’abord une instance :
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Visual Basic .NET nécessite que vous créez toujours des instances d’objets COM avant de pouvoir utiliser leurs méthodes. Pour utiliser ces méthodes dans Visual Basic, déclarez une variable de la classe souhaitée et utilisez le nouveau mot clé pour affecter l’objet à la variable objet. Le mot clé Shared
peut être utilisé lorsque vous souhaitez vous assurer qu’une seule instance de la classe est créée.
' 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
Erreurs non gérées dans les gestionnaires d’événements
Un problème d’interopérabilité courant implique des erreurs dans les gestionnaires d’événements qui gèrent les événements déclenchés par des objets COM. Ces erreurs sont ignorées à moins que vous ne vérifiiez spécifiquement les erreurs à l'aide des instructions On Error
ou Try...Catch...Finally
. Par exemple, l’exemple suivant provient d’un projet Visual Basic .NET qui a une référence à l’objet COM de bibliothèque 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
Cet exemple génère une erreur comme prévu. Toutefois, si vous essayez le même exemple sans le bloc Try...Catch...Finally
, l’erreur est ignorée comme si vous utilisiez l’instruction OnError Resume Next
. Sans gestion des erreurs, la division par zéro échoue silencieusement. Étant donné que ces erreurs ne déclenchent jamais d’erreurs d’exception non gérées, il est important d’utiliser une certaine forme de gestion des exceptions dans les gestionnaires d’événements qui gèrent les événements à partir d’objets COM.
Présentation des erreurs d’interopérabilité COM
Sans gestion des erreurs, les appels d’interopérabilité génèrent souvent des erreurs qui fournissent peu d’informations. Dans la mesure du possible, utilisez la gestion structurée des erreurs pour fournir plus d’informations sur les problèmes lorsqu’ils se produisent. Cela peut être particulièrement utile lorsque vous déboguez des applications. Par exemple:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Vous trouverez des informations telles que la description d’erreur, HRESULT et la source des erreurs COM en examinant le contenu de l’objet exception.
Problèmes de contrôle ActiveX
La plupart des contrôles ActiveX qui fonctionnent avec Visual Basic 6.0 fonctionnent avec Visual Basic .NET sans problème. Les principales exceptions sont les contrôles de conteneur ou les contrôles qui contiennent visuellement d’autres contrôles. Voici quelques exemples de contrôles plus anciens qui ne fonctionnent pas correctement avec Visual Studio :
Contrôle de trame Microsoft Forms 2.0
Contrôle haut-bas, également connu sous le nom de contrôle de rotation
Contrôle de tabulation Sheridan
Il n’existe que quelques solutions de contournement pour les problèmes de contrôle ActiveX non pris en charge. Vous pouvez migrer des contrôles existants vers Visual Studio si vous possédez le code source d’origine. Sinon, vous pouvez vérifier auprès des fournisseurs de logiciels s'ils proposent des versions de contrôles compatibles avec .NET pour remplacer les contrôles ActiveX non pris en charge.
Transmission des propriétés en lecture seule des contrôles par référence (ByRef)
Visual Basic génère parfois des erreurs COM, telles que « Erreur 0x800A017F CTL_E_SETNOTSUPPORTED », lorsque vous transmettez des propriétés ReadOnly
de certains contrôles ActiveX plus anciens en tant que paramètres ByRef
à d’autres procédures. Les appels de procédure similaires de Visual Basic 6.0 ne déclenchent pas d’erreur et les paramètres sont traités comme si vous les avez passés par valeur. Le message d’erreur Visual Basic .NET indique que vous essayez de modifier une propriété qui n’a pas de propriété Set
procédure.
Si vous avez accès à la procédure appelée, vous pouvez empêcher cette erreur à l’aide du mot clé ByVal
pour déclarer des paramètres qui acceptent des propriétés ReadOnly
. Par exemple:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Si vous n’avez pas accès au code source de la procédure appelée, vous pouvez forcer le passage de la propriété par valeur en ajoutant un ensemble supplémentaire de crochets autour de la procédure appelante. Par exemple, dans un projet qui a une référence à l’objet COM de bibliothèque Microsoft ActiveX Data Objects 2.8, vous pouvez utiliser :
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
Déployer des assemblages qui exposent l'interopérabilité
Le déploiement d’assemblys qui exposent des interfaces COM présente certains défis uniques. Par exemple, un problème potentiel se produit lorsque des applications distinctes font référence au même assembly COM. Cette situation est courante lorsqu’une nouvelle version d’un assembly est installée et qu’une autre application utilise toujours l’ancienne version de l’assembly. Si vous désinstallez un assembly qui partage une DLL, vous pouvez involontairement le rendre indisponible pour les autres assemblys.
Pour éviter ce problème, vous devez installer des assemblys partagés dans le Global Assembly Cache (GAC) et utiliser un MergeModule pour le composant. Si vous ne pouvez pas installer l’application dans le GAC, elle doit être installée sur CommonFilesFolder dans un sous-répertoire spécifique à la version.
Les assemblies qui ne sont pas partagées doivent être situées côte à côte dans le répertoire avec l'application appelante.