Partager via


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.

  1. Les clients peuvent ne pas s’attendre à ce que les noms de méthode générés soient conformes à leurs attentes.

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

Voir aussi