6.5.Notions de base sur la technologie COM
implantation en Delphi

Plan du chapitre:
Introduction

1. Les notions d'interfaces COM de microsoft
 

1.1 Qu'est-ce qu'une interface logicielle
1.2 L'interface IUnknown
1.3 Les CoClasses
1.4 l'architecture COM et le registre Windows
1.5 Instanciation d'un objet COM dans une application Delphi


2. Exemples d'utilisation d'objets et d'interfaces COM
 

2.1 Utilisation de l'interface IShellLink
2.2 Utilisation de word
2.3 ActiveX est un objet COM



Introduction

COM signifie Component Object Model, c'est le modèle proposé par Microsoft pour créer des composants logiciels distribués. La principale information à retenir de COM est son but : il s'agit de permettre l'interopérabilité des composants logiciels suivant cette norme (notamment dans les applications de type client/serveur), c'est-à-dire de faire en sorte qu'ils puissent communiquer facilement et efficacement entre eux, et ce quelque soit le langage utilisé pour les créer, et quelque soit la plate-forme sur laquelle ils fonctionnent.

COM est donc un modèle universel de spécification et d'implantation de communication objet. Il est évident que lorsque l'on veut pouvoir réutiliser dans un langage L1 un objet ou un composant écrit avec un autre langage L2 et fourni en langage binaire, il est nécessaire d'employer un protocole de communication universel : l'élément majeur de COM permettant cette communication client/serveur, est la notion d'interface.

La principale fonction de COM que nous voyons dans ce cours, consiste à construire des composants fenêtrés nommés ActiveX qui peuvent être utilisés sur Internet à travers "Internet Explorer" et développés et manipulés par des langages différents. Les ActiveX peuvent être développés comme les composants Delphi, dans un environnement de développement Delphi, C++, VB6,… et être réutilisés dans l'un quelconque de ces environnements. Les ActiveX peuvent aussi être convertis en Winforms dans la plate-forme .Net et être ainsi réutilisés par les langages de .Net comme : C#, VB Net,…

La famille de systèmes d'exploitation Windows utilise cette architecture, et de très nombreux développements ont été centrés sur cette norme. L'architecture .Net de microsoft se positionne comme remplaçant de COM tout en gardant dores et déjà une compatibilité ascendante avec celle-ci.

 
 
 

1. Les notions d'interfaces COM de microsoft
 

1.1 Qu'est-ce qu'une interface logicielle

Avec COM, un client n’a pas besoin de savoir où réside un objet, il suffit de faire un appel à une interface de l’objet. COM accomplit les étapes nécessaires à cet appel. Ces étapes sont différentes selon que l’objet réside dans le même processus que le client, dans un autre processus sur la machine du client ou sur une autre machine du réseau.

Les clients COM communiquent avec des objets par le biais d'interfaces COM. Les interfaces sont des groupes de routines, liées par la logique ou par la sémantique, qui assurent la communication entre le fournisseur d'un service (objet serveur) et ses clients. L'interface est l'unique moyen de mettre à disposition du client le service fourni par l'objet, sans lui donner les détails de l'implémentation sur la façon dont ce service est fourni.
 
 
Une interface est un pointeur sur un pointeur dans un objet COM, elle pointe en fait sur la table des méthodes de l'objet COM. Une interface n'est donc pas un objet, elle est en outre identifiée de manière unique par un nombre appelé GUID.

 

Une interface correspond en fait en Delphi, à une classe abstraite pure, c’est-à-dire qui ne comporte que des méthodes abstraites. Une interface ne contient donc pas en elle-même de code binaire et ne peut être instanciée. Elle représente juste un contrat, c'est-à-dire que la classe chargée d’implémenter cette interface s’engage à implémenter toutes les méthodes déclarées dans l’interface.

Voici un exemple d’interface réalisé en Delphi :
 
type TVoiture = class

public

procedure Demarrer ;virtual ;abstract ;

procedure Accelerer ;virtual ;abstract ;

end ;

Cette déclaration est tout à fait correcte ; cependant Delphi dispose d’un mot clé spécifique, interface :

 
type IVoiture = interface
procedure Demarrer ;

procedure Accelerer ;

end ;
 
Ce nouveau mot clé facilite le travail du développeur, car il impose des règles inhérentes au concept d’interface :
 
 
  • toutes les méthodes déclarées ainsi sont automatiquement considérées publiques et abstraites ;
  • il est impossible d’inclure dans l’interface des données membres ;
  • la classe devant implémenter une interface devra obligatoirement traiter toutes ses méthodes, ce qui n’est pas le cas pour les classes abstraites, qui peuvent n’être que partiellement implémentées par une classe dérivée.

Note :
les interfaces sont conventionnellement préfixées par ‘I’, tout comme les classes de la VCL sont préfixées par ‘T’ ou les données membres par ‘F’.

La compatibilité de la déclaration des interfaces pour tout language susceptible de les utiliser est assurée grâce à un langage standard, l'IDL (Interface Description Language). Delphi, par exemple, s'occupe en arrière-plan de la traduction en IDL. Il existe plusieurs compilateurs se chargeant de la traduction de l'IDL vers le C++, Java...
 
 
 

1.2 L'interface IUnknown

Nous avons précisé plus haut qu'afin d'obtenir des objets indépendants (du langage, de la plate-forme,...) nous devions pouvoir disposer d'une interface de base commune ou universelle.
 
l'interface IUnknown est l’interface de base commune et universelle du modèle COM. Elle doit être présente dans tout langage implantant l'architecture COM. C'est d'elle qu'hérite directement ou indirectement toute autre interface.

Elle indique notamment au client les interfaces qui sont disponibles sur le serveur. IUnknown possède seulement 3 méthodes :
 
  • QueryInterface
  • _AddRef
  • _Release

 

QueryInterface permet de naviguer entre les différentes interfaces implémentées par un objet COM (comme toute interface dispose de cette méthode, on peut accéder à n’importe qu’elle interface à partir de IUnknown).

_AddRef permet l’incrémentation d’un compteur de références : : lorsqu’un objet se connecte à l’interface, le compteur est incrémenté de 1.

_Release permet la décrémentation du compteur de référence : lorsqu’un objet se déconnecte de l’interface, le compteur est décrémenté de 1. Ainsi, lorsque le compteur est arrivé à 0, l’objet COM sait qu’il peut libérer la mémoire qu'il occupait.


Chaque langage de programmation (C++, Delphi, Java,...) se charge ensuite d'implanter l'interface IUnknown afin d'instancier un objet COM qui dérive obligatoirement et au minimum cette interface IUnknown.

En Delphi, c'est la classe TInterfacedObject qui est chargée d'implémenter l'interface IUnknown. Elle est déclarée comme suit :




1.3 Les CoClasses en Delphi

Dans Delphi un objet COM est nécessairement comme tout objet de Delphi, une instanciation d'une classe Delphi. Pour un objet COM cette classe doit obligatoirement être une coclasse.
 
  • Une coclasse est une classe chargée d’implémenter une ou plusieurs interfaces. C'est elle qui va "contenir" le code machine et donc constituer le moule de fabrication de l'objet COM. 
  • Une coclasse peut bien sûr dériver d’une classe existante. 
  • Une coclasse doit dériver d'une classe implantant l'interface IUnknown.

Voici un exemple de déclaration d’une coclasse :

En Delphi :
type TVoiture = class( TInterfacedObject , IVoiture )
procedure Demarrer ;

procedure Accelerer ;

end ;

Toute coclasse dérivant de TInterFacedObject hérite de l'implémentation de IUnknown. Il est donc conseillé de construire les coclasses permettant d'instancier des objets COM à partir de cette classe.
 
 

1.4 l'architecture COM et le registre Windows

Pour assurer l'unicité des identificateurs d'objets COM, on utilise des GUIDs (Globally Unique IDentifiers), qui comme leur nom l'indique permettent d'avoir des identifiants (presque) uniques au monde. Ils sont construits grâce à un algorithme particulier qui combine des informations sur la machine locale, sur l'heure, la date actuelle, le nom d'utilisateur..., ce qui assure qu'il n'y a qu'une infime possibilité que 2 GUIDs identiques soient un jour générés.

Ces GUIDs sont codés sur 128 bits et sont souvent représentés sous une forme standard en hexadécimal afin de les rendre plus "lisibles" à l'homme :

Exemple de GUID
{00000002-0000-0000-C000-000000000046}
 
Un GUID peut servir à identifier un objet COM (on parle alors de CLSID ou class ID), une interface (IID), ou bien tout élément que l'on doit identifier très précisement (par exemple une instance d'application).Tout objet COM, pour pouvoir être instancié, doit être au préalable inscrit dans le registre système de Windows (grâce à un utilitaire comme tregsvr, qui est livré avec Delphi). Une fois incrit, son GUID est présent dans la rubrique HKEY_CLASSES_ROOT\CLSID, en compagnie de tous les autres objets COM présents sur le système :

Les informations ici présentes dans la sous-clé concernent le type de l'objet (ici il s'agit d'un serveur in-process, c'est-à-dire situé dans une DLL), sa localisation sur la machine, et son ProgID, qui est un identifiant local de l'objet, plus facile à retenir que le CLSID.
 
 

1.5 Instanciation d'un objet COM dans une application Delphi

Il existe en Delphi trois différentes façons d'instancier un objet COM, selon que l'objet COM est local (interne au programme), externe au programme ou bien distant.

Nous indiquons comment cela se passe dans le cas externe, la documentation de base de Delphi détaillant les différents outils permettant toutes ces constructions.

Delphi pour un objet COM externe, fournit une fonction utilitaire, CreateComObject, qui permet de créer n'importe quel objet déjà inscrit dans le registre, pourvu que l'on connaisse son CLSID ou son ProgID (on peut effectuer la transformation de l'un en l'autre grâce aux fonctions ClassIDToProgID et ProgID to CassID).
 
function CreateComObject(const ClassID: TGUID): IUnknown;
CreateComObject CreateComObject crée un objet non initialisé de la classe associée au CLSID spécifié par ClassID. CreateComObject renvoie une référence à l'interface IUnknown, à utiliser pour communiquer avec l'objet.

On peut aussi tout de suite spécifier l'interface désirée à la place de IUnknown, en utilisant le transtypage avec le mot-clé as (il faut bien sur que l'objet supporte l'interface demandée) :
 
const
 monGuid:string='{35900002-0000-1100-C000-00770E000046}';

var
 id : IVoiture;

id := CreateComObject(StringToGuid(monGuid)) as IVoiture;
 

Ce transtypage est un raccourci; on pourrait aussi, après l'obtention d'une référence sur IUnknown, effectuer un appel à QueryInterface.

On peut maintenant faire appel à une quelconque méthode de l'interface souhaitée :
 
const
 monGuid:string='{35900002-0000-1100-C000-00770E000046}';

var
 id : IVoiture;

id := CreateComObject(StringToGuid(monGuid)) as IVoiture;
id.Demarrer;
id.Accelerer;
.....
 


 

Note :
pour pouvoir compiler un tel programme avec delphi, il faut disposer d'unités pascal objet effectuant la déclaration des interfaces utilisées (Delphi est livré avec des unités adéquates pour de nombreuses interfaces liées au système, cf documentation de base du langage).

 
 
 

2.Exemples d'utilisation d'objets et d'interfaces COM
 
 

2.1 Utilisation de l'interface IShellLink

Utilisation de l'interface IShellLink pour la création de fichiers de liens (.lnk).

Sous Windows, l'interface graphique offre plusieurs moyens de créer un raccourci vers un exécutable : par exemple par glisser-déplacer vers le bureau, par un item dans le menu contextuel dans l'explorateur... Toutes ces solutions passent par le même mécanisme : dans tous les cas, la création d'un fichier de lien (.lnk) est prise en charge par l'interface système IShellLink.

Nous pouvons bien sûr employer cette interface dans une application Delphi, pour créer dynamiquement des raccourcis. Le projet exemple se trouve dans le répertoire "Liens Windows".

Tout d'abord, nous constituons la fiche : le but est que l'utilisateur choisisse à la fois le nom et le chemin de l'exécutable et ceux du fichier de lien. Nous utilisons pour cela deux objets TOpenDialog :

Lorsque l'utilisateur a fait son choix, il le valide en appuyant sur le bouton, ce qui va entraîner l'appel à la fonction createfilelink reproduite ici :

Il faut noter que les déclarations nécessaires à l'utilisation de IShellLink sont situées dans l'unité ShlObj, qu'il faut donc inclure dans la liste uses, avec ComObj, qui contient la déclaration de CreateComObject ainsi que celle de nombreuses fonctions utilitaires concernant COM.

IShellLink contient d'autres méthodes, comme SetIconLocation qui permet d'indiquer où se trouve le fichier contenant l'icône associée au raccourci (par défaut le système utilise l'icône de l'exécutable). Cf l'aide du SDK Windows pour les autres méthodes.

Par curiosité nous avons cherché dans le registre l'objet identifié par le GUID correspondant à CLSID_ShellLink (cf shlobj.pas) et nous avons vu que l'objet COM implémentant IShellLink se trouve dans la DLL système shell32.dll.
 
 

2.2 Utilisation de word

Utilisation de Word dans une application grâce à l'automatisation.

Microsoft Word est un logiciel supportant l'automatisation, c'est-à-dire qu'il "publie" de nombreuses fonctionnalités aux autres logiciels. Cela permet notamment l'interaction avec Visual Basic et VBA, mais puisque l'automatisation est un protocole standard, on peut aussi accéder à ces fonctions avec d'autres langages de programmation, par exemple Delphi.

Plusieurs autres applications sont des serveurs d'automatisation : les plus connues sont celles de la suite MS Office (Access, PowerPoint, Excel, Outlook), mais il y a aussi Adobe Photoshop, Corel Draw, MathCad...Pour avoir la liste complète des serveurs d'automatisation installés sur la machine, il faut consulter le registre.

Nous avons choisi de reprendre l'exemple traité dans l'aide de Delphi, car il est court et simple à comprendre. Il suffit d'instancier l'objet "Word.Application" (c'est-à-dire de lancer l'exécutable Word s'il n'est pas déjà en mémoire) puis d'utiliser ses fonctions.

Cependant, puisqu'il s'agit ici d'une application indépendante (contrairement à IShellLink qui fait partie intégrante de Windows), il faut obtenir une description des fonctionnalités offertes par Word. Pour cela, on doit importer la bibliothèque de type de Word. Une bibliothèque de type sert à décrire un objet COM, elle est écrite en IDL et peut soit être incluse dans le même exe ou la même DLL que l'objet qu'elle décrit, soit constituer un fichier indépendant (généralement d'extension .tlb).

En Delphi, cela correspond à la commande Projet/Importer une bibliothèque de type. On accède alors à cette boîte de dialogue :

La bibliothèque de type de Word s'appelle msword8.olb (pour Word97; pour Word2000 c'est msword9.olb).

Lorsque l'on clique sur "créer l'unité", Delphi effectue la traduction IDL->Pascal Objet et crée une unité nommée par défaut Word_TLB.pas (sauvegardée dans le répertoire \Imports de Delphi). Cette unité contient toutes les déclarations de constantes et d'interfaces qui nous sont nécessaires pour communiquer avec Word. Il faut donc l'ajouter à la liste uses de l'unité où l'automatisation sera utilisée. On remarque qu'à la fin de Word_TLB.pas, Delphi a crée une série de coclasses correspondant aux différents objets COM de Word : en fait le créateur de ces classes contient un simple appel à CreateComObject.

Notre exemple met en oeuvre une simple impression de fichier (choisi au préalable grâce à un TOpenDialog), mais qui va être effectuée par Word :

Ici, myword est déclaré comme _application, qui est une "interface double" : cela permet d'utiliser les méthodes de l'objet (il faut le savoir, on vous l'accorde). Mais on peut aussi plus simplement utiliser le type variant et la fonction CreateOleObject :

Le fichier d'aide décrivant ces fonctionnalités se situe dans le répertoire d'Office (vbawrd8.hlp); malheureusement il expose la manière de les utiliser en VBA seulement (comme pour la plupart des serveurs d'automatisation), il faut donc faire un gros effort d'adaptation...
 
 

2.3 ActiveX est un objet COM

COM a été initialement conçu pour fournir une fonctionnalité de communication de base et permettre l’enrichissement de cette fonctionnalité via des extensions. COM lui-même a étendu sa fonctionnalité première en définissant des ensembles spécialisés d’interfaces couvrant des besoins spécifiques.

ActiveX est une technologie qui permet aux composants COM, particulièrement les contrôles, d’être plus compacts et plus efficaces. Un contrôle ActiveX est un composant logiciel qui s’incorpore à et étend les fonctionnalités d’une application hôte gérant les contrôles ActiveX, un tel composant ne nécessite que l'interface IUnknown.
 
Un objet COM ne peut exister que s'il est stocké en particulier soit dans une application exécutable, soit dans un objet ActiveX.
  • Lorsque c'est une application qui contient l'objet COM, on dit que cette application est un serveur COM.
  • Lorsque c'est un ActiveX qui contient l'objet COM, on dit alors que c'est un serveur ActiveX.

Les contrôles ActiveX sont des serveurs en processus COM, généralement conçus pour être eux-mêmes incorporés dans une application client, ils s’exécutent alors dans le même espace processus que le client. Les contrôles proposent des comportements et des événements à la conception et à l’exécution.

Serveur en processus :
Un contrôle ActiveX incorporé dans une application est téléchargé sur la machine du client et appelé dans le même processus que l'application elle-même. Le client communique alors avec le serveur en processus grâce à des appels directs à l’interface COM.

 

Un contrôle ActiveX en Delphi doit au minimum implanter certaines interfaces nécessaires, dont voici la liste fournit par Inprise-Borland :

IUnknown, IDispatch,
IPersistStreamInit,
IOleInPlaceActiveObject,
IPersistStorage,
IViewObject, IOleObject,
IViewObject2,
IOleControl,
IPerPropertyBrowsing,
IOleInPlaceObject,
ISpecifyPropertyPages

Les 2 chapitres suivants traitent plus spécialement de méthodes pratiques pour construire des ActiveX en Delphi(5, 6, 7) et en Visual Basic 6. Ces deux RAD fournissent heureusement des experts de construction des ActiveX qui implantent incluent et génèrent automatiquement le code source associé aux diverses interfaces nécessaires. Le seul travail restant à effectuer est de programmer les nouvelles fonctionnalités du futur ActiveX. Ce chapitre-ci n'a que pour intention de souligner quelques aspects du fonctionnement de COM sous windows avec Delphi en particulier.

Depuis 2004, la construction des ActiveX n'est plus assistée dans la plate-forme .Net puisque celle-ci remplace la notion d'objet COM, donc Delphi 8.Net et VB .Net ne contiennent plus d'experts de construction ActiveX.