Dépannage des problèmes liés à l'interopérabilité (Visual Basic)

Lors d'interopérations entre COM et le code managé du .NET Framework, un ou plusieurs des problèmes courants suivants peuvent se poser.

Marshaling d'interopérabilité

Il se peut que vous devez parfois 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 devez 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 indiquer le type non managé BStr sur les chaînes envoyées à des objets COM créés par Visual Basic 6.0 et par des versions antérieures. Dans ce cas, vous pouvez utiliser l'attribut MarshalAsAttribute pour que les types managés soient exposés en tant que types non managés.

Exportation de chaînes de longueur fixe dans du code non managé

Dans Visual Basic 6.0 et les versions antérieures, les chaînes sont exportées dans les objets COM sous la forme de séquences d'octets sans caractère null de fin. Dans un souci de compatibilité avec d'autres langages, Visual Basic 2005 inclut un caractère de fin lors de l'exportation de chaînes. La meilleure façon de résoudre cette incompatibilité consiste à exporter les chaînes qui n'ont pas ce caractère comme des tableaux de Byte ou Char.

Exportation de hiérarchies d'héritage

Les hiérarchies de classes managées s'aplatissent lorsqu'elles sont exposées sous forme d'objets COM. Par exemple, si vous définissez une classe de base avec un membre, puis héritez de cette classe dans une classe dérivée qui est exposée sous forme d'objet COM, les clients qui utilisent la classe dérivée dans l'objet COM ne sont pas en mesure d'utiliser les membres hérités. Les membres de la classe de base ne sont accessibles à partir d'objets COM que sous forme d'instances d'une classe de base, et uniquement si la classe de base est également créée sous forme d'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 comme objet COM, de nouveaux noms de méthodes sont générés pour les méthodes surchargées.

Par exemple, considérez une classe avec deux surcharges de la méthode Synch. Lorsque la classe est exposée comme objet COM, les nouveaux noms de méthodes générés peuvent être Synch et Synch_2.

Le changement de nom peut provoquer deux problèmes pour les consommateurs de l'objet COM.

  1. Il se peut que les clients n'attendent pas les noms de méthodes générés.

  2. Les noms de méthodes générés dans la classe exposée comme objet COM peuvent changer lorsque de nouvelles surcharges sont ajoutées à la classe ou sa classe de base. Ce qui peut provoquer des problèmes de versioning.

Pour résoudre les deux problèmes, donnez à chaque méthode un nom unique au lieu d'utiliser la surcharge, lors du développement d'objets qui seront exposés comme objets COM.

Utilisation d'objets COM via les assemblys d'interopérabilité

Vous utilisez presque les assemblys d'interopérabilité comme s'il s'agissait de remplacements de code managé pour les objets COM qu'ils représentent. Toutefois, dans la mesure où ce sont des wrappers et non des objets COM réels, il existe quelques différences entre l'utilisation d'assemblys d'interopérabilité et celle d'assemblys standard. Ces différences comprennent l'exposition des classes ainsi que les types de données pour les paramètres et les valeurs de retour.

Classes exposées à la fois sous forme d'interfaces et de classes

À la différence des classes dans les assemblys standard, les classes COM sont exposées dans les assemblys d'interopérabilité à la fois sous la forme d'une interface et d'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, auquel le mot "Class" a été ajouté. Par exemple, supposons que vous avez un projet avec une référence à un assembly d'interopérabilité pour un objet COM. Si le nom de la classe COM est 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 en utilisant l'instruction New avec un nom de classe. Le fait d'avoir une classe COM représentée par un assembly d'interopérabilité est le seul cas dans lequel vous pouvez utiliser l'instruction New avec une interface. À moins d'utiliser la classe COM avec une instruction Inherits, vous pouvez utiliser l'interface de la même manière qu'une classe. Le code suivant montre comment créer un objet Command dans un projet qui fait référence à l'objet COM de la bibliothèque Microsoft ActiveX Data Objects 2.8 :

Dim cmd As New ADODB.Command

Toutefois, si vous utilisez la classe COM comme base d'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

Notes

Les assemblys d'interopérabilité implémentent de façon implicite les interfaces qui représentent des classes COM. Vous ne devez pas essayer d'utiliser l'instruction Implements pour implémenter ces interfaces afin de ne pas provoquer d'erreur.

Types de données pour les paramètres et valeurs de retour

À la différence des membres des assemblys standard, ceux des assemblys d'interopérabilité peuvent posséder des types de données différents de ceux utilisés dans la déclaration initiale de l'objet. Même si les assemblys d'interopérabilité convertissent de façon implicite les types COM en types Common Language Runtime compatibles, vous devez prêter attention aux types de données utilisés des deux côtés pour éviter les erreurs d'exécution. Par exemple, dans les objets COM créés avec Visual Basic 6.0 et les versions antérieures, les valeurs du type Integer adoptent le type .NET Framework équivalent, 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 de niveau module

La plupart des objets COM sont utilisés par la création d'une instance de classe COM avec le mot clé New, puis par l'appel aux méthodes associées à ces objets. Une exception à cette règle concerne les objets COM qui contiennent des classes COM AppObj ou GlobalMultiUse. Ces classes sont semblables aux méthodes de niveau de module dans les classes Visual Basic 2005. Visual Basic 6.0 et les 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'instance au préalable :

Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.

Visual Basic 2005 exige que vous créiez toujours des instances d'objets COM pour pouvoir utiliser leurs méthodes. Pour employer ces méthodes dans Visual Basic 2005, déclarez une variable de la classe voulue et assignez l'objet à la variable objet à l'aide du mot clé New. Vous pouvez utiliser le mot clé Shared pour 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

L'un des problèmes courants d'interopérabilité concerne les 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, sauf si vous les recherchez spécifiquement à l'aide d'instructions On Error ou Try...Catch...Finally. L'exemple suivant est extrait d'un projet Visual Basic 2005 qui fait référence à l'objet COM de la 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 =
    "Provider=Microsoft.Jet.OLEDB.4.0;" &
    "Data Source=C:\NWIND.MDB"
    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. Cependant, si vous essayez le même exemple sans le bloc Try...Catch...Finally, 'erreur est ignorée comme si vous utilisiez l'instruction OnError Resume Next. Sans la gestion des erreurs, la division par zéro échoue silencieusement. Étant donné que ces erreurs ne déclenchent jamais d'erreur de type exception non gérée, il est important que vous utilisiez un certain mode de gestion des exceptions dans les gestionnaires d'événements qui gèrent les événements provenant des objets COM.

Compréhension des erreurs COM Interop

Sans la gestion des erreurs, les appels Interop génèrent souvent des erreurs qui fournissent peu d'informations. Autant que possible, utilisez la gestion structurée des erreurs pour fournir de plus amples informations sur les problèmes lorsque ceux-ci 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 pouvez obtenir des informations telles que la description des erreurs, les valeurs HRESULT et la source des erreurs COM par l'examen du contenu de l'objet exception.

Problèmes liés aux contrôles ActiveX

La plupart des contrôles ActiveX qui fonctionnent avec Visual Basic 6.0 acceptent Visual Basic 2005 sans problème. Les principales exceptions sont les contrôles conteneur, des contrôles qui contiennent visuellement d'autres contrôles. Voici quelques exemples d'anciens contrôles qui ne fonctionnent pas correctement avec Visual Studio :

  • le contrôle Frame de Microsoft Forms 2.0 ;

  • le contrôle Up-Down, également désigné contrôle Spin ;

  • le contrôle Sheridan Tab.

Il n'existe que peu de solutions aux problèmes des contrôles ActiveX non pris en charge. Vous pouvez faire migrer les contrôles existants vers Visual Studio si vous possédez le code source d'origine. Sinon, vous pouvez rechercher auprès des fournisseurs de logiciels les versions de contrôles actualisées compatibles avec .NET pour remplacer les contrôles ActiveX non pris en charge.

Transmission des propriétés ReadOnly de contrôles avec ByRef

Visual Basic 2005 génère parfois des erreurs COM (par exemple, "Error 0x800A017F CTL_E_SETNOTSUPPORTED") lorsque vous tentez de transmettre à d'autres procédures les propriétés ReadOnly de certains anciens contrôles ActiveX sous forme de paramètres ByRef. Des appels de procédure similaires à partir de Visual Basic 6.0 ne génèrent pas d'erreur et les paramètres sont traités comme si vous les transmettiez par valeur. Le message d'erreur que vous visualisez dans Visual Basic 2005 correspond à l'objet COM qui signale que vous tentez de modifier une propriété sans procédure de propriété Set.

Si vous avez accès à la procédure qui est en cours d'appel, vous pouvez éviter cette erreur en employant le mot clé ByVal pour déclarer des paramètres qui acceptent les 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 qui est en cours d'appel, vous pouvez forcer la transmission de la propriété par valeur. Pour ce faire, encadrez la procédure appelante d'une paire de crochets supplémentaires. Par exemple, dans un projet qui fait référence à l'objet COM de la 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éploiement d'assemblys exposant l'interopérabilité

Le déploiement d'assemblys exposant des interfaces COM soulève des défis uniques. Ainsi, un problème potentiel se pose lorsque des applications distinctes référencent le même assembly COM. Cette situation est courante lorsqu'une nouvelle version d'un assembly est installée et qu'une autre application continue d'utiliser l'ancienne version de l'assembly. Si vous désinstallez un assembly partageant une DLL, vous pouvez involontairement rendre cette DLL indisponible aux autres assemblys.

Pour éviter ce problème, vous devez installer les assemblys partagés dans le Global Assembly Cache (GAC) et utiliser un module de fusion pour le composant. Si vous ne pouvez pas installer l'application dans le GAC, vous devez l'installer dans un sous-répertoire spécifique à la version du répertoire CommonFilesFolder.

Les assemblys qui ne sont pas partagés doivent être placés côte à côte dans le répertoire avec l'application appelante.

Voir aussi

Tâches

Procédure pas à pas : implémentation de l'héritage avec les objets COM (Visual Basic)

Comment : ajouter des modules de fusion à un projet de déploiement

Référence

MarshalAsAttribute

Tlbimp.exe (Type Library Importer)

Tlbexp.exe (exportateur de bibliothèques de types)

Inherits, instruction

Concepts

Global Assembly Cache

Autres ressources

COM Interop (Visual Basic)