|
|
|||||||||||||||||||||
|
Visual Basic est un langage de haut niveau permettant de programmer assez rapidement des applications axées essentiellement sur l'accès à des bases de données et la présentation d'informations dans des formulaires. Cependant, pour peu que l'application devienne plus conséquente et que le programme ait besoin de fonctionnalités supplémentaires, les limitations de Visual Basic se font très fortement ressentir. Afin d'outrepasser ces limitations, plusieurs possibilités s'offrent
au développeur :
La première partie de cet article va vous décrire les étapes à suivre afin de créer un composant COM simple : il présente des propriétés, des méthodes et remonte des événements à l'application. Nous verrons aussi comment instancier le composant dans un client Visual Basic et répondre aux événements. Pour démarrer, on crée un nouveau projet de type " WCE ATL COM AppWizard ". Il est fortement conseillé d'utiliser l'assistant, s'en passer n'ayant d'ailleurs aucun intérêt. Lorsque le projet vient d'être créé, il contient une série de fichiers décrits ci-dessous. A ce stade, aucun composant n'est encore créé car une DLL COM (aussi appelée in-proc server) peut contenir plusieurs composants de divers types.
Il est temps de commencer à créer ses composants. Les
opérations s'effectuent à partir du ClassView (onglet
au même niveau que la workspace). Cliquez droit sur le projet
et choisissez " New ATL Object ". Une boîte de dialogue
propose alors plusieurs types de composants. Créons un objet
simple pour débuter.
Les étapes suivantes décrivent l'implémentation des propriétés, méthodes et événements de notre composant. Ces opérations s'effectuent à partir de l'interface du composant Inomcomp dans le ClassView.
Créer une propriété de type " long "
est une étape des plus simples. Il suffit de cliquer sur "
Add Property " : Le type d'accès indique si la propriété est en lecture ou en écriture, toutes les combinaisons étant possibles. On peut très bien imaginer une propriété en lecture seule (ex. : handle d'un contrôle), en lecture et écriture (ex. : le titre d'une fenêtre), en écriture seule (ex. : envoi de données sur un port série). Attirons l'attention sur le fait qu'il existe deux types d'écriture
: Pour notre propriété "long", on a donc les méthodes get et put. L'assistant crée alors les entrées suivantes dans le fichier .idl :
Le but de cet article n'étant pas de décrire en détail IDL (Interface Description Language), le MSDN reste la meilleure source d'informations pour une description détaillée des possibilités. Au niveau du fichier nomcomp.h, les déclarations des méthodes
d'accès ont été ajoutées :
STDMETHOD est une macro déclarant la fonction de la manière suivante :
L'implémentation basique est également ajoutée dans le fichier nomcomp.cpp. Par contre, l'assistant est incapable de savoir comment implémenter la propriété. Il en va donc de la responsabilité du programmeur de savoir ce qu'il a à faire. Pour notre exemple, on déclare un membre long m_lLongValue au niveau de la classe Cnomcomp. Ce membre contiendra la valeur de la propriété. Le get et le put ne font alors que retourner et modifier le membre.
Il ne faut pas oublier d'initialiser le membre dans le constructeur.
Il est souvent nécessaire d'avoir une propriété de type chaîne dans son composant. Les questions souvent posées sont : comment VB gère-t-il les chaînes et comment les récupérons-nous dans notre composant C++ ? En réalité, le type de chaîne utilisé en VB est un type défini au niveau de COM, et porte le nom de BSTR (Basic String). Il existe bien entendu plusieurs fonctions de gestion pour ce type de chaîne. La Basic String se différencie d'une chaîne C par les
points suivants : Heureusement pour le programmeur C, le zéro est malgré tout ajouté directement en fin de chaîne par les fonctions d'allocations. Dans notre exemple, nous n'utilisons pas de chaîne C, le codage d'une telle propriété étant réduite à sa plus simple expression. Une propriété string se crée de la même façon que les autres. Dans l'idl :
Dans le .h :
Dans le .cpp :
Il ne faut pas oublier d'initialiser la variable membre dans le constructeur :
Il ne faut pas oublier également de libérer la chaîne dans le destructeur :
Pour une description détaillée des fonctions utilisées, reportez vous au MSDN. Schéma de représentation de la Basic String en mémoire
:
A. taille en bytes de la chaîne sur 32 bits Le "BSTR" pointe directement sur la chaîne et non pas sur le champ taille de la chaîne.
Bien que les propriétés d'un composant cachent en réalité des appels de fonctions, on préférera cependant utiliser des méthodes pour effectuer des traitements. Un nom de méthode est d'ailleurs généralement constitué d'un verbe explicite sur la nature du traitement. C'est pourquoi, il est rare de trouver des composants qui ne possèdent aucune méthode. Un tel composant serait d'une utilité très restreinte ou alors mal conçu. Dans notre exemple, nous allons ajouter une méthode
SetAttributes qui aura pour effet de modifier en un appel les propriétés
LongValue et StrValue. L'ajout de la méthode se fait de la
même façon que la propriété, excepté
que l'on choisit " Add Method " au lieu de " Add Property
". Il suffit alors de nommer sa méthode et d'énumérer
les paramètres. Les paramètres peuvent être de plusieurs sortes :
Dans l'idl :
Dans le .h :
Dans le .cpp :
Un composant peut remontrer au client des événements. Ce n'est en fait rien d'autre qu'une méthode implémentée au niveau d'une autre interface du composant, visible au niveau du ClassView, _InomcompEvents. Puisqu'il s'agit d'une méthode, son implémentation se fera de la même façon qu'une méthode tout à fait classique. Contrairement à une méthode, un événement doit être déclenché d'une certaine façon pour être remonté au client. Pour cela, l'assistant est toujours là pour nous aider. Dans le ClassView, au niveau de la classe Cnomcomp, le menu droit nous propose " Implement Connection Point ". En exécutant cette commande, l'assistant générera pour nous le code de déclenchement de l'événement. L'événement peut bien entendu être lancé à tout moment, pour cela il suffit d'appeler la fonction générée : Fire_nomevent. Dans notre exemple, on pourrait vérifier la valeur de la propriété LongValue (" est comprise entre -100 et 100 "). Si la valeur est incorrecte, on génère un événement BadValue.
Un composant ne serait pas complet sans une gestion poussée des erreurs. En COM, la gestion d'erreur se fait en remontant des exceptions. La manière la plus expéditive afin de remonter une exception est de retourner E_FAIL à la place de S_OK dans une méthode (ou méthode get / put / putref). Cependant, en agissant de la sorte, aucune information n'est fournie au client sur la nature de l'erreur. Pour cela, notre composant hérite d'une classe ATL encapsulant la génération des informations d'exceptions. Il suffit alors d'appeler la méthode Error. Dans la méthode SetAttribtues on pourrait générer une exception si la valeur de LongValue est incorrecte.
Le premier paramètre donne une description de l'exception (retrouvée parErr.Description), le deuxième donne le GUID de la source de l'exception (en eVB, Err.Source donne apparemment " VBScript... " comme source de l'exception) et le troisième paramètre donne la valeur du HRESULT retourné par la fonction. Il existe une liste de valeurs prédéfinies consultable dans le MSDN.
Pour ceux qui ont déjà programmé en VB, vous aurez sans doute remarqué qu'il est possible de définir des constantes contenues au niveau de la librairie. Ceci est facile à faire. Dans le fichier idl, il suffit d'ajout la déclaration suivante dans la section library :
Le client en VB est très simple à faire bien qu'il existe une petite subtilité pour pouvoir recevoir les événements. Il faut d'abord créer un module (.bas) qui contiendra, la déclaration de la variable qui référencera notre objet.
La création de l'objet se fera grâce à la fonction CreateObjectWithEvents qui permet d'instancier un objet et de spécifier un préfixe pour la récupération des événements sur cette instance.
Les fonctions commençant par objATL_ seront interprétées en tant que réponse aux événements. Par exemple, si nous avons déclaré l'événement BadValue, la fonction objATL_BadValue représente le gestionnaire pour cet événement pour l'instance référencée par la variable objATL. Il est clair que le préfixe de la fonction n'est pas forcément le même que le nom de la variable, de sorte que nous pourrions avoir la même fonction de réponse pour plusieurs instances. Le programmeur doit alors faire en sorte que chaque instance puisse être identifiée. Par exemple, le premier paramètre du message donne une référence sur l'objet qui l'a généré mais cette façon de faire n'est pas conforme à COM. Une fois l'objet instancié, les appels aux méthodes
et propriétés sont pareils à tous les objets.
Lorsqu'on crée un composant, on aimerait pouvoir
le déboguer. Ceci est facile à réaliser, il suffit
d'écrire un client. Notre client VB fera très bien l'affaire.
Comment dire au débogueur de lancer un fichier .vb pour exécuter
notre composant ? En fait, il faut lancer le " loader "
de VB en lui spécifiant le chemin de notre client VB.
Les classes de ATL fournissent un système de traces directement dans la fenêtre du débogueur. La quantité de messages reçus peut être configurée à l'aide de #define, l'idéal étant de les définir dans le fichier stdafx.h avant inclusion des autres fichiers.
Les flags de catégorie sont définis dans l'énumération enum atlTraceFlags. La valeur par défaut définit tous les flags à 1 (0xFFFFFFFF). |
|||||||||||||||||||||
|
|
|||||||||||||||||||||
|
Copyright 2001-2004 - Tous droits réservés
|
|||||||||||||||||||||
|
iPAQ
est un produit de COMPAQ.
|