III - De COM à ActiveX

 

Dans ce chapitre nous allons rapidement présenter l'architecture logicielle d'OLE.

 

1. Architecture

Sous l'appellation OLE, on désigne en fait toute l'architecture logicielle de communication inter-applications de Microsoft, allant de la spécification COM aux contrôles ActiveX. OLE c'est en fait un certain nombre de services que le programmeur peut exploiter dans ses applications Windows.

La figure ci-dessous précise les différents services disponibles dans l'environnement OLE.

  

Cette architecture est basée sur le modèle COM (Component Object Model), dont nous avons déjà un peu parlé. On trouve ensuite un ensemble de service concernant les documents composés (Compound Objects) permettant l'échange de données entre documents. Ces services sont représentés par trois couches dans la figure précédente. Au dessus, on distingue le service OLE Automation qui permet à un programme d'exécuter des méthodes d'un objet serveur. Enfin, la dernière brique de cet édifice est ActiveX qui correspond en fait à des contrôles OLE renommés début 96 par Microsoft. On distingue également à ce niveau le modèle DCOM, version distribuée du modèle COM.

Nous n'allons détailler que les principaux services cités ci-dessus.

 

2. COM

Le Component Object Model est, comme nous l'avons dit, le format des applications OLE, leur permettant ainsi de pouvoir communiquer entre elles. Ce format est indépendant du langage de programmation choisi, l'essentiel est que l'exécutable obtenu respecte le modèle. Notons que nous parlons d'exécutable mais en réalité il s'agit plutôt de bibliothèques de fonctions exécutables par plusieurs applications OLE.

Il est important de remarquer qu'OLE est un environnement basé sur des objets client/serveur. Un objet, hébergé par un serveur (bibliothèque DLL, application etc.), peut être utilisé par plusieurs programmes clients simultanément.

Du point de vue du programmeur, l'élément de base en programmation OLE est l'interface, classe abstraite ou classe virtuelle. Il s'agit exactement de ce que nous avons vu pour Java : une interface est une classe dont toutes les méthodes sont abstraites. Le programmeur va donc devoir implémenter chacune de ces méthodes pour une interface donnée. L'accès à un objet se fait via des pointeurs sur ses interfaces.

L'interface de base, qu'il est obligatoire d'implémenter pour un objet, est l'interface IUnknown. Cette interface est disponible pour tous les objets OLE/COM.

L'interface IUnknown possède, entre autres, une méthode appelée QueryInterface() qui permet à un programme utilisant l'objet considéré de prendre connaissance de l'existence d'une interface implémentée dans l'objet. Si l'interface demandée existe, la méthode QueryInterface() renvoie un pointeur sur celle-ci afin que l'objet demandeur puisse l'utiliser.

Chaque interface d'un objet est identifiée de façon unique par un GUID (Globally Unique IDentifier) attribué par Microsoft pour éviter tout conflit d'identifiant. Ce dernier, codé sur 128 bits, est stocké dans la base des registres de Windows. Dans cette base, pour chaque GUID, on trouve le nom et la localisation du serveur (DLL par exemple) fournissant l'objet dont on veut accéder à l'une de ses interfaces. On trouve également pour chaque interface un certain nombre d'informations telles que sa version, les types de données reconnus, etc.

A chaque fois que l'interface d'un objet est utilisée par un client, un compteur de référence est incrémenté par ce client, via l'appel d'une fonction AddRef(). Lorsque le client n'utilise plus cette interface, il appelle la méthode Release() qui décrémente le compteur. Dès que la valeur de ce compteur est nulle, la mémoire utilisée par l'objet considéré est libérée automatiquement.

Une autre interface de base est IClassFactory. Cette interface permet de créer un objet, via une méthode appelée CreateInstance() à laquelle on indique en argument le GUID de l'objet à instancier. Cette méthode renvoie un pointeur vers l'interface IUnknown que nous venons de présenter, permettant ainsi d'utiliser l'objet considéré. Précisons par ailleurs que le mécanisme que nous venons d'évoquer est automatisé par l'appel de la fonction COM CoGetClassObject() à laquelle on fournit un GUID qui renvoie donc le pointeur vers l'interface IUnknown correspondante.

Notons enfin qu'il n'existe pas de mécanisme d'héritage pour les interfaces du modèle COM. On utilise à la place soit des agrégations (désignation d'un agrégat de plusieurs objets désigné par une seule et même interface IUnknown) soit des délégations (où une interface en utilise une autre). Nous n'en dirons pas plus sur ces mécanismes.

 

3. OLE Automation

OLE Automation est un service très puissant qui permet de manipuler une application en appelant dynamiquement les méthodes des objets, respectant le format COM, qu'elle contient. Cette manipulation est typiquement faite via un langage de scripts tel que VBA, mais ce n'est pas une obligation.

En fait, OLE Automation permet à une application de découvrir dynamiquement les objets et méthodes disponibles dans une autre application afin de pouvoir les utiliser. OLE Automation permet également de construire des serveurs d'objets qui sont capables de savoir quelle méthode de quel objet appeler suite à une requête d'une application cliente.

On définit dans OLE Automation des objets automates (Automation Objects) qui pourront être manipulés à l'aide de langages de scripts tels que VBA ou être invoqués dynamiquement. Ces applications clientes sont appelées des contrôleurs d'automates.

Nous avons vu plus haut que pour tout objet, on devait obligatoirement implémenter l'interface IUnknown. Dans le cas d'un objet automate, on doit également implémenter de façon obligatoire une autre interface, appelée IDispatch et qui permet les invocations dynamiques dont nous venons de parler. Cette interface est utilisée afin d'obtenir le nom de la fonction à appeler tandis que la signature de celle-ci est extraite d'une librairie de types des fonctions des objets automates. Cette librairie est généralement stockée dans un DLL ou un fichier annexe.

Notons enfin que la signature de ces fonctions est décrite grâce à un langage appelé ODL pour Object Description Language.

Passons maintenant à un élément qui nous intéresse tout particulièrement : le contrôle OLE ou ActiveX.

 

4. ActiveX

Les contrôles OLE, renommés ActiveX, sont des composants autonomes permettant de réaliser des applications. Ces composants s'appuient sur COM et OLE Automation que nous venons de voir.

Les contrôles OLE de première génération, appelés contrôles OCX (OLE Control eXtension), étaient des composants pouvant réagir à des événements extérieurs. La deuxième version de ces contrôles, désormais appelés contrôles ActiveX étend les possibilités d'OCX en permettant d'utiliser des composants distribués sur plusieurs systèmes interconnectés par un réseau tel qu'Internet, exploitant le modèle DCOM, extension de COM, que nous décrirons plus loin.

Une application incorporant des contrôles ActiveX est appelée, nous l'avons déjà dit, un document. Notons qu'un même document peut contenir des contrôles de types très différents comme des tableaux Excel ou des documents HTML.

Une illustration de la puissance de ce mécanisme est par exemple l'affichage d'un document Word via MSIE. On constate en effet que, dès qu'un tel fichier est récupéré sur un serveur web, il y a incorporation de la barre d'outils de Word dans la fenêtre de MSIE, de façon transparente pour l'utilisateur. Ce mécanisme est supérieur à celui que nous avons vu dans le paragraphe précédent qui permettait de manipuler un composant incorporé dans un document, bien qu'un contrôle ActiveX puisse également faire l'objet d'une telle manipulation.

En résumé, un contrôle ActiveX peut être piloté par un document le comprenant, via une interface OLE Automation mais il peut également envoyer des ordres au document le contenant, via un deuxième type d'interface, comme nous venons de l'illustrer.

On distingue par ailleurs le container ActiveX, celui qui contient un contrôle, du component ActiveX qui est le composant en lui même. Concernant un container, il contient un certain nombre de propriétés de type ambient qui sont partagées entre tous les contrôles ActiveX d'un même document. Il peut s'agir par exemple de la couleur ou de la taille de la police de caractères, etc. L'accès à ces données se fait via une interface IDispatch que nous avons présentée plus haut.

Un container doit également pouvoir répondre aux événements générés par un composant ActiveX, également via une interface IDispatch.

Un composant ActiveX quant à lui est caractérisé par un certain nombre de propriétés et d'événements. On distingue quatre types de propriétés :

  1. ambiantes (ambient),
  2. standard,
  3. étendues,
  4. spécifiques.

Les propriétés ambiantes sont celles que nous avons vues plus haut et sont partagées par tous les composants ActiveX d'un même document. En cas de changement dans ces propriétés, tous les composants sont prévenus par la modification d'un flag spécifique

Les propriétés standard sont quant à elles spécifiques à un composant ActiveX et sont similaires aux propriétés ambiantes (couleur, taille des caractères,...).

Les propriétés étendues sont associées à un composant mais ce n'est pas lui qui en a le contrôle. Elles peuvent permettre, par exemple, de spécifier un comportement par défaut dans une boite de dialogue. C'est le document container qui peut les manipuler.

Les propriétés spécifiques sont, comme leur nom l'indique, propres à un composant précis. Elles sont définies par le programmeur du composant.

Les événements, générés par un composant et traités par un container, sont classés en quatre catégories distingues permettant ainsi d'en faciliter l'accès au document :

  1. événements de type requête,
  2. événements générés avant une opération,
  3. événements générés après,
  4. événements générés pendant une opération.

Les requêtes sont envoyées par le composant à un document pour lui demander l'exécution d'une certaine action.

Les événements envoyés au document avant et après une opération sont reçus par le document, sans qu'il n'ait à faire une action particulière. On signale juste au document que le composant va débuter et a terminé une certaine opération.

Enfin, les événements produits durant une opération sont en fait des événements standard que l'on rencontre fréquemment dans les interfaces graphiques, à savoir un clic ou déplacement de souris, etc.

Il convient également de préciser que tout composant ActiveX doit être identifié auprès de la base des registres OLE afin qu'il puisse être utilisable. Cette opération est produite automatiquement lorsqu'on récupère un contrôle à partir d'une page web, via la balise HTML <OBJECT> dont nous avons parlé brièvement au chapitre précédent. D'ailleurs, ce contrôle est identifié de manière unique par un GUID, dont nous avons également déjà parlé.

Abordons maintenant le dernier service de l'architecture OLE que nous présenterons dans ce chapitre, à savoir l'extension DCOM du modèle COM.

 

5. DCOM

Distributed COM est la version distribuée du modèle COM qui permet à des objets appartenant à différentes machines de communiquer entre eux via un réseau, alors que COM est limité à une même machine.

DCOM est basé sur l'environnement DCE (Distributed Computing Environment) , standardisé par l'OSF, dont il exploite en particulier les appels de procédures distantes RPC (Remote Procedure Call), comparables dans leur principe au système RMI dont nous avons parlé pour Java.

Par conséquent, à chaque fois qu'on envoie un message DCOM à un objet distant, ce message est en réalité transporté via RPC.

Dans un environnement DCOM, chaque machine impliquée dispose d'un serveur dit Object Explorer qui a pour but de gérer les objets COM disponibles sur la machine considérée. Quand une application distante fait référence à un objet, l'Object Explorer vérifie que cet objet existe bien dans la base du système sur lequel il tourne. Si c'est le cas, l'accès à cet objet se fait via une évolution de l'interface IUnknown que nous avons présenté plus haut, appelée IRemUnknown pour Remote Unknown.

Précisons enfin que la gestion des références d'accès aux objets dans un environnement DCOM est quelque peu améliorée pour tenir compte des aléas d'une communication via un réseau. En effet, en cas de plantage d'une machine distante, les références enregistrées par un client ou un serveur risqueraient de ne plus être les mêmes. C'est pourquoi DCOM inclut un mécanisme de ping des clients vers les serveurs permettant ainsi d'indiquer à un serveur qu'un client est toujours opérationnel.

Notons par ailleurs que des requêtes ping (vers plusieurs objets d'un même serveur) peuvent être regroupées afin de ne pas sur charger un réseau.

Nous allons maintenant aborder la programmation de script VBScript.