Page d'accueil | Applications | Technologies | eBooks pour MS Reader | Me contacter
Documents techniques DELPHI

ActiveObject

(Janvier 2002)


Cet article présente une mise en application de l'enregistrement d'un objet COM, comme objet actif pour sa classe, de sorte que d'autres processus puissent accéder et utiliser cette unique instance à l'exécution.
Ce mécanisme puissant devrait permettre une meilleure collaboration entre applications et servir le modèle de conception "Médiateur".

La bibliothèque OLE offre un moyen d'enregistrer un objet COM comme étant l'objet actif pour sa classe (selon un CLSID donné), au moyen de la fonction RegisterActiveObject. Cet enregistrement fait figurer l'objet dans une table accessible par tout autre processus sur la machine par la fonction Delphi GetActiveOleObject qui appelle à son tour la fonction GetActiveObject de l'API Win32.

Le mécanisme d'enregistrement est implémenté dans la classe TActiveObject comprenant deux constructeurs.
Le premier demande comme premier argument une référence IUnknown et le CLSID de la classe de l'objet : celui-ci est déclaré comme constante dans l'unité correspondant à la bibliothèque de types décrivant l'objet.
Le second constructeur demande une référence IUnknown et le progID de l'objet.
Chacun de ces constructeurs procède à l'enregistrement de l'objet et le désenregistrement a lieu lors de la libération de l'objet TActiveObject.

Cette classe est utilisée dans les programmes de démonstration décris ci-après (voir la rubique Ressources pour télécharger les codes source).
Après avoir dézippé le fichier téléchargé, vous devriez voir apparaître les dossiers OleObject, AppTest et ClientTest.


OleObject

Le projet OleObject.dpr est simplement une bibliothèque ActiveX (DLL) comprenant un objet OLE automation de CoClass TTest, de progId "OleObject.Test", et supportant l'interface ITest. Celle-ci a une propriété nommée Message destinée à écrire ou lire une chaîne de caractères conservée dans la CoClass. Le serveur OLE autmation doit être recensé après compilation.


Apptest

Ce dossier contient le projet AppTest. C'est l'exécutable AppTest.exe qui crée une instance de TTest et l'enregistre en utilisant une instance de TActiveObject au démarrage. La libération de l'objet TActiveObject désenregistre l'instance de TTest lors de la fermeture de l'application. Plus aucune application extérieure ne peut plus alors y avoir accès.


ClientTest

Enfin, le projet ClientTest.dpr démontre l'accès à l'objet TTest recensé, par la fonction GetActiveOleObject en lui passant comme argument le progId "OleObject.Test".
Lancez plusieurs instances de ClientTest.exe. Deux boutons de la fiche principales permettent de lire ou d'écrire dans la propriété Message de l'objet. On peut ainsi observer qu'une chaîne de caractères écrite par l'une des instances est bien relue par l'autre.
S'il n'existe pas d'object recensé pour le progId passé à la fonction GetActiveOleObject, celle-ci lève une exception EOleSysError avec le message "Opération non disponible".


Ressources

Télécharger ActiveObject (Delphi 6)
ActiveObject.zip


Retour à la page d'accueil

Collaboration d'ActiveX et design pattern Médiateur

(Janvier 2001)


Le modèle de conception "Médiateur" offre une solution originale pour faire collaborer des ActiveX ensemble, situés dans des conteneurs ActiveX n'appartenant pas nécessairement à la même application.
Dans ce modèle, la classe ayant le rôle de médiateur possède une référence de tous les objets devant participer à la réalisation d'une tâche et contient toute la logique du traitement. Cela évite que tous les objets se fassent référence les uns aux autres en complexifiant les traitements.

La mise en ouvre de ce modèle s'appuie ici sur des ActiveX qui sont des ActiveForm créés avec Delphi et utilisant des interfaces OLE automation. Mais je gage que le lecteur pourra réutiliser les principes exposés dans cet article avec son propre outil de développement et/ou d'autres technologies.
Si vous permettez à votre navigateur d'exécuter des ActiveX, vous pourrez aussi tester le résultat de cette mise en ouvre dans une page HTML. Pour cela, reportez vous à la rubrique Ressources à la fin de cet article.


Exposition du problème

Nous souhaitons offrir une interface utilisateur (UI) pour la saisie de nombres sous la forme d'un ActiveX présentant des touches numériques ; cette saisie sera reflétée par un ou plusieurs autres objets COM basés sur un autre ActiveX se contentant d'afficher ce qui aura été saisi.

Voici le diagramme de classes UML de ces ActiveX d'interface utilisateur implémentés dans l'OCX VposUI.ocx :


Les classes TFrmKbUI et TFrmDisplayUI sont les coclasses implémentant chacune une interface COM distincte. Par simplification, ces interfaces ne définissent qu'une seule méthode identique, MiseAJour, susceptibe d'être appelée par le médiateur pour demander au contrôle de se rafraîchir. A ce moment les ActiveX "DisplayUI" pourront interroger le médiateur pour connaître le nombre à afficher.


Le médiateur

Le médiateur est implémenté dans l'exécutable Vpos.exe comprenant un objet OLE automation, TCoVlp, par la classe TMediateur. Cette classe est définie avec le modèle Singleton, c'est-à-dire que l'on ne crée et n'utilise qu'une seule instance de cette classe.
L'objet automation TCoVlp, qui est invoqué par les ActiveX d'interface utilisateur, est chargé de faire le lien avec l'instance unique du médiateur.

Le diagramme de classes UML de Vpos.exe est présenté ci-dessous :


La classe TFrmVpos représente la fiche principale de l'application, que l'on peut choisir de montrer ou non. Elle peut servir à présenter des messages destinés au déboguage par exemple.

Le diagramme de séquence ci-dessous présente les principales étapes d'exécution, en partant de l'instanciation de l'ActiveX UI "clavier" dans un conteneur :



Test

Pour tester ce code, vous devez tout d'abord télécharger l'exécutable Vpos.exe (renfermant le médiateur), placé dans le fichier Vpos.zip. Dézippez-le pour ensuite exécuter "Vpos /regserver", ce qui enregistrera le serveur dans la base de registres de Windows (Vpos.exe se désinstalle tout d'abord en exécutant "Vpos /unregserver", puis en supprimant ce fichier).
Vpos.zip

Vous pouvez maintenant utiliser le lien ci-dessous vers une page HTML qui comprend les ActiveX de VposUI.ocx (votre navigateur doit autoriser l'exécution d'ActiveX sur votre machine) :
medt.html


Retour à la page d'accueil

Commande

(Octobre 2002)


Cet article présente une implémentation en Pascal Objet du modèle de conception " Commande ". Ce modèle de conception est décrit dans l'ouvrage " DESIGN PATTERNS " (voir la rubrique Ressources en fin d'article).
La définition de ce modèle est la suivante :
" Encapsuler une requête comme un objet, autorisant ainsi le paramétrage des clients par différentes requêtes, files d'attente et récapitulatifs de requêtes, et de plus, permettant la réversion des opérations. "

La classe Pascal Objet sur lequel repose ce modèle est TCommand, les classes TMacroCommand (collection d'objet TCommand) et TMacroCommandLog en sont dérivées, comme illustré dans le diagramme de classes UML ci-dessous :



Un projet Delphi 6 très simple est fourni comme exemple d'utilisation de ces classes pour en montrer l'intérêt.

TCommand est une classe abstraite possédant la méthode virtuelle abstraite execute qui réalise l'action à effectuer ; une classe commande concrète (dérivant de TCommand) est créée avec les arguments qui serviront à l'exécution de la méthode surchargée execute. Une fois qu'un objet commande est instancié, un client peut invoquer à tout moment sa méthode execute.

La classe TCommand est basée sur TComponent afin d'utiliser le mécanisme de lecture/écriture de composant Delphi depuis ou dans un flux.
L'application exemple (voir la rubrique Ressources) déclare la classe TMaCommande dérivée de TCommand, afin de conserver dans des objets les choix successifs de l'utilisateur entre une entrée, un plat principal et un dessert.

Chaque objet TMaCommande conserve un choix de l'utilisateur sous forme d'entier (0..2), de sorte que si la méthode execute est appelé, le bouton radio correspondant de l'interface utilisateur est sélectionné.
Tous les choix faits depuis le démarrage de l'application sont conservés dans un objet descendant de TMacroCommand, TMacroCommandLog, qui permet de gérer une collection d'objets commandes.
La méthode surchargée execute de TMacroCommand se contente d'appeler cette même méthode avec tous les objets commandes qu'elle contient.

TMacroCommandLog a la particularité de conserver dans un fichier tout objet commande ajouté à la collection, de sorte que si l'on redémarre l'application, nous pouvons appliquer à nouveau tous les choix de l'utilisateur (comme après un crash système par exemple).
Pour que les opérations de lecture/écriture fonctionnent, l'unité Unit1 déclare dans sa section initialisation l'instruction :
RegisterClasses([TMaCommande]);


Il existe d'autres indications d'utilisation de ce modèle, telles que les transactions ou les opérations " défaire " (undo). Un objet commande pourrait également être transmis à un autre récepteur sur une machine distante et y être exécuté (par sérialisation/désérialisation dans une chaîne de caractères OLE via DCOM par exemple) !


Ressources

Livre " DESIGN PATTERNS "
Catalogue de modèles de conception réutilisables
Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides

Command.pas et projet d'exemple (Delphi 6)
cmdprj.zip


Retour à la page d'accueil

Composant TRCData

(Février 2001)


TRCData est un composant Delphi permettant d'inclure des fichiers dans vos applications à la conception, de manière à les restituer à l'exécution sous forme de flux de données tel qu'un fichier par exemple.
En effet, avec TRCData vous pouvez définir des ressources personnalisées qui sont des données ajoutées lors de la conception d'une fiche à partir d'un fichier, et pouvant être extraites à l'exécution, en les plaçant dans un flux de données.

Ce composant est très utile si vous souhaitez lier un fichier quelconque, comme un exécutable par exemple, à votre application, et l'extraire plus tard lors de l'exécution de celle-ci. Cela peut être un fichier de configuration que vous ne voulez pas distribuer sous forme de fichier, avec votre programme, lors de son installation.
A la conception les données d'un composant TRCData lui sont ajoutées à l'aide d'un éditeur spécialisé accessible en double-cliquant sur le composant ou en utilisant le menu "Edit" du menu surgissant.



Le composant TRCData peut contenir jusqu'à 2 Go de données dans sa version complète ; bien entendu la taille de l'exécutable augmente en conséquence.
TRCData comprend deux méthodes publiques permettant de lire ou d'écrire dans un flux de données :
procedure WriteData(Stream: TStream);
procedure ReadData(Stream: TStream);

Avec la méthode WriteData les données du composant sont écrites dans le flux Stream, tandis que la méthode ReadData permet d'initialiser les données du composant avec celles du flux Stream.
Une utilisation de ces méthodes est d'offrir un support pour les fichiers : cela est fourni au travers des deux autres méthodes publiques suivantes :
procedure LoadFromFile(const FileName: String);
procedure SaveToFile(const FileName: String);

Le programme d'exemple mentionné plus bas utilise la méthode SaveToFile pour extraire les données du composant RCData1 (le fichier d'origine est le fichier test.txt).
L'utilisation de flux TStream permet d'extraire les données dans tout objet descendant de TStream, en mémoire (TMemoryStream) ou dans une chaîne de caractères (TStringStream) par exemple.

Installation

Le fichier zip disponible par le lien de la rubrique Ressources contient les fichiers suivants : PRCData.bpl, RCData.dcu, EditRCData.dcu et EditRCDta.dfm, ainsi qu'un projet Delphi 5 servant d'exemple.
Pour installer le composant TRCData avec Delphi 5, placez-vous dans la boîte de dialogue d'installation des paquets, puis ajoutez le paquet PRCData.bpl. Le composant est placé dans la page "OE". Lors de son utilisation, assurez vous d'avoir dans les chemins de recherche l'accès aux fichiers dcu du composant.

Ressources

Avec le lien ci-dessous vous pouvez télécharger la version shareware de ce composant qui limite la taille des données à 200 Ko par composant.
rcdata.zip

La version complète du composant TRCData peut être commandée auprès du site ShareIt! à l'adresse ci-dessous :
https://secure.element5.com/register.html?productid=140534&language=French


Retour à la page d'accueil

DelphiDoc

(Mars 2001)


Présentation

DelphiDoc est un utilitaire de génération automatique de documentation de codes sources Pascal Objet au format HTML, dont je suis l'auteur, se présentant sous forme d'expert pour Delphi 5.
Au travers de commentaires spéciaux, DelphiDoc incorpore dans la documentation générée les descriptions correspondants à l'unité, ses constantes, variables, classes, procédures et fonctions.
On se reportera à l'unité Unit1, reprise dans le fichier Unit1.html ci-dessous, pour ce qui concerne la manière d'utiliser les commentaires spéciaux "(**" et "*)" employés par DelphiDoc :

delphidc/Unit1.html


L'utilisation de DelphiDoc requiert l'installation d'Internet Explorer (au minimum la version 5 est recommandée) venant avec celle du parser XML de Microsoft. Vous pouvez le vérifier en constatant la présence du fichier MSXML.DLL dans votre répertoire système.

DelphiDoc s'intègre dans l'IDE de Delphi :




Génération de la documentation

Après son installation, l'expert DelphiDoc est accessible depuis le menu "Voir/DelphiDoc" de Delphi ; celui-ci affiche dans une vue arborescente l'ensemble des unités et fiches du projet courant.
Il est alors possible de produire la documentation pour le projet entier, en sélectionnant d'abord le noud racine, ou celle d'une unité particulière que vous sélectionnez, en actionnant le bouton "Generate doc". Vous pouvez aussi appeler un menu contextuel avec le bouton droite de la souris pour demander la génération de la documentation de l'unité sélectionnée.
Le bouton "View doc", quand à lui, déclenchera l'exécution d'Internet Exporer (ou bien celle de l'exécutable rattaché à l'extension html) ; si c'est le noud racine qui est sélectionné et que la génération s'est bien déroulée, IE ouvrira le fichier index.html, ou bien le fichier html sommaire de l'unité sélectionnée (voir plus bas).
La génération des fichiers HTML ne fonctionnera que sur des unités syntaxiquement correctes, ce qui est assuré par une compilation sans erreur.


Fichiers générés

Voici les fichiers produits, leurs emplacment et leurs descriptions lors de la génération automatique de documentation du projet C:\fichiers\project1.dpr comprenant l'unité Unit1.pas (le . représente le répertoire courant du projet) :


.\Doc\index.html

index.html reprend l'ensemble des unités du projet avec leurs descriptions respectives, ainsi que des liens hypertextes vers celles-ci.


.\Doc\Unit1\Unit1-sommaire.html

Ce fichier constitue le sommaire de l'unité Unit1, ce qui comprend les descriptions éventuelles des éléments de l'unité, les constantes et les variables des parties interface et implélentation, les classes, ainsi que les procédures et fonctions globales.


.\Doc\Unit1\Unit1-TMaClasse.html

Ce fichier décrit la classe TMaClasse déclarée dans Unit1 et toutes ses données membres.


.\Doc\Unit1\Unit1-foncprocs.html

Les procédures et fonctions globales de l'unité Unit1 sont décrites ici.


.\Doc\Unit1\Unit1-index.html

L'index de l'unité Unit1 liste tous les éléments accessibles de l'unité.


Appliqué au projet Delphi 5 project1.dpr comportant l'unité Unit1, DelphiDoc produit les fichiers HTML suivants :

delphidc/index.html

delphidc/Unit1-sommaire.html

delphidc/Unit1-TMaClasse1.html

delphidc/Unit1-TMaClasse2.html

delphidc/Unit1-foncprocs.html

delphidc/Unit1-index.html



Téléchargement de DelphiDoc 1.0.0.7

La version shareware de DelphiDoc 1.0, que vous pouvez télécharger ci-après, générera les fichiers indiqués ci-dessus, mise à part le fichier index.html, dans le répertoire temporaire utilisé par Windows.

delphidc/delphidc.zip

DelphiDoc sera alors disponible au prochain démarrage de Delphi.

La version complète de DelphiDoc peut être commandée auprès du site ShareIt! à l'adresse ci-dessous :
https://secure.element5.com/register.html?productid=141389&language=French

Acquérir la version complète encourage la réalisation d'applications utiles et intéressantes ; d'autre part vous bénéficierez des évolutions futures de DelphiDoc, comme les mises à jour pour les versions suivantes de Delphi, la possibilité de créer vos propres modèles de documentation, etc.


Notes techniques

DelphiDoc utilise le parser XML de IE implementé dans MSXML.DLL aussi bien pour la génération de documents XML issus de l'analyse des unités que pour la création des fichiers HTML à l'aide de feuilles de style XSL.
Ces feuilles de style XSL sont incluses dans le fichier ExprtDelphiDoc.dll dans des composants TRCData dont il est aussi question sur ce site.

Retour à la page d'accueil

Distributed COM

(Février 2002)


Cet article décrit comment un programme client peut invoquer un objet COM situé sur une autre machine au moyen de DCOM (extension distribué du modèle objet COM Microsoft), et quels sont les paramétrages à effectuer de part et d'autre.
Si le serveur COM est de type automation, nous montrerons que le client n'a besoin de connaître que le nom de la machine distante et l'identificateur de la classe, sans aucun paramétrage particulier.

Cet article est d'avantage destiné à l'environnement Delphi, mais les principes utilisés s'appliquent à tout environnement de développement supportant COM. Nous préciserons à chaque fois ce qui est spécifique à Delphi.


Serveur COM

Nous partirons d'un serveur hors processus (exécutable) comprenant un objet automation (classe COM supportant l'interface IDispatch). Dans l'EDI Delphi, il suffit de créer une nouvelle application et d'y ajouter un objet automation au moyen de l'assistant adéquat.
Supposons que vous ayez saisi "Service" comme nom de CoClass.



L'éditeur de la bibliothèque de types vous permettra de compléter votre interface IService héritée de IDispatch déclarée dans l'unité générée par l'assistant. Cette unité, dont le nom est celui du projet suivi de "_TLB", comprend la classe utilitaire CoService définissant deux fonctions de classe destinées à instancier votre classe COM en renvoyant un pointeur d'interface ayant le type IService.
La première de ces méthodes, "Create", crée un objet local, la seconde, "CreateRemote" un objet distant sur la machine dont le nom est passé en paramètre. Au final, se sont respectivement les fonctions de l'API Windows CoCreateInstance et CoCreateInstanceEx qui sont appelées de manière interne.


Recensement du serveur

Le recensement du serveur peut être fait depuis Delphi, ou bien en démarrant l'application (auto-recensement), ou encore en démarrant l'application avec l'argument /regserver.
Dans le cas d'un objet COM en processus (DLL), l'utilitaire Microsort regsvr32 peut être appelé avec le chemin complet de la dll.


Appel distant du client

L'unité représentant en Pascal Objet la bibliothèque de types de votre serveur (unité projet_TLB) peut être réutilisée par une application cliente s'exécutant sur une autre machine. De cette manière, votre code s'appuie sur l'utilisation du pointeur d'interface IService (liaison précoce). Par exemple :

var
  Service: IService;
begin
  Service := CoService.CreateRemote('HOSTNAME');
  ... // faire quelque chose avec le pointeur d'interface IService
end;

Dans le code ci-dessus l'objet Service est créé sur la machine distante 'HOSTNAME'. Par défaut tous les chemins UNC, tels que \\serveur ou serveur, et les noms DNS, tels que serveur.com ou 144.19.56.38, sont permis.
La fonction CoCreateInstanceEx, appelée de manière interne par CreateRemote, ne recherche pas dans la base de registres du client le CLSID désiré (GUID de classe de l'objet Service), mais contacte plutôt le SCM qui réside sur la machine distante spécifiée et lui demande de rechercher le base de registres de ce serveur.
Avec cette technique, il est nécessaire soit de recenser l'activeX sur la machine cliente, ce qui oblige à avoir une copie de votre exécutable sur cette machine, soit de recenser uniquement la bibliothèque de types.
Votre projet comprend cette bibliothèque de types sous forme de ressource Windows incluse dans l'exécutable et sous forme de fichier projet.tlb. L'utilitaire Borland tregsvr.exe, situé normalement dans le répertoire Bin de Delphi vous permettra de recenser le fichier projet.tlb.
Il suffirait à un environnement de développement compatible COM d'importer cette bibliothèque de types pour obtenir les méthodes et propriétés de l'objet Service.

Il existe un moyen de se passer de tout paramétrage sur le poste client, du fait que le serveur supporte l'interface IDispatch.
En pascal objet nous utiliserions un variant de la manière suivante :

const
CLASS_MonService: TGUID = '{F845A45F-1A3C-4866-B44F-F92FB491015B}';
var
  Service: Variant;
begin
  Service := CreateRemoteComObject('HOSTNAME', CLASS_MonService) as IDispatch;
  ... // faire quelque chose avec Service
end;

La seule contrainte est de connaître le CLSID de la classe COM à invoquer sur la machine distante. Dans l'unité projet_TLB, ce GUID apparaît comme constante dans la partie interface de l'unité.
Le code du programme client utiliserait donc la liaison tardive (pas de vérification à la compilation) comme pour n'importe quel autre objet automation.


Appel local, mais distant !

Pour les applications instanciant des objets COM locaux, il est toujours possible d'utiliser DCOM sans devoir changer une seule ligne de code. Moyennant des modifications de la base de registres concernant les classes de votre serveur, vous pouvez spécifier sur quelle machine doit être instancié un objet COM. Pour vous y aider, utilisez l'utilitaire de configuration DCOM dcomcnfg.exe (voir la rubrique Ressources plus loin). Après avoir sélectionné "Service objet" comme application, cliquez sur le bouton Propriétés. Dans la boîte de dialogue qui s'ouvre vous pouvez alors sélectionner le nom de la machine sur laquelle instancier votre activeX.





Ressources

Utilisation de l'utilitaire dcomcnfg
http://support.microsoft.com/default.aspx?scid=kb;FR;q176799

Description de CoCreateInstanceEx
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/cmf_a2c_5ry0.asp


Retour à la page d'accueil

Ensemble de données TDataSet et collection d'objets

(Janvier 2001)


Basé sur la classe TDataSet pour les composants ensemble de données, le composant TObjectDataSet (que vous pouvez télécharger à l'aide du lien de la rubrique Ressource), sert de conteneur d'objets en gérant des objets de même type au lieu d'enregistrements tout en offrant la persistance des objets dans un fichier.
L'intérêt d'un tel composant est de pouvoir utiliser normalement tous les contrôles orientés données de Delphi pour manipuler et modifier des objets au lieu d'enregistrements, tout comme on le ferait avec le composant TTable par exemple.

Voici la marche à suivre pour utiliser le composant TObjectDataSet avec Delph 5 :

1. Installer la bibliothèque de paquet PObjectDataSet.bpl à partir de la page 'Paquets' de la boîte de dialogue 'Options de projet' ; le composant TObjectDataSet est alors placé dans la page 'AccèsBD' de la palette d'outils de Delphi.

2. Déposer le composant TObjectDataSet sur une nouvelle fiche et affecter un nom de classe dans la propriété ObjectClass du composant ; à l'exécution le composant vérifiera qu'il existe bien une classe de ce nom recensée dans l'application par l'instruction RegisterClass ou RegisterClasses de l'unité Classes.
Cette classe doit obligatoirement être dérivée de la classe TPersistent ou de l'une de ses classes descendantes, et surcharger la méthode virtuelle Assign. L'implémentation de Assign doit définir comment assigner un objet du même type, en faisant intervenir en particulier les propriétés publiées qui sont utilisées avec les contrôles orientés données.
En effet, à l'exécution, le composant TObjectDataSet analyse la classe grâce à RTTI pour créer les champs correspondants aux propriétés publiées ; TObjectDataSet reconnaît les propriétés de type String (le champ créé est de type TStringField avec une taille de 255 caractères), entier et flottant. De la sorte, les contrôles orientés données connectés à l'ensemble de données peuvent utiliser les noms des propriétés pour la propriété DataField.
D'autre part pour bénéficier du support de la persistance des objets contenus dans une instance de TObjectDataSet avec un fichier, à l'aide des méthodes SaveToFile et LoadFromFile, la classe de base doit être TComponent.

3. Il ne reste plus qu'à déposer sur la fiche un composant TDataSource, pour le relier au composant TObjectDataSet, et des composants orientés données eux-mêmes connectés au TDataSource. Comme précisé plus haut, la propriété DataField d'un composant orienté données doit être le nom d'une propriété publiée de la classe gérée par l'objet TObjectDataSet.


Le composant TObjectDataSet offre aussi un accès orientée objet à ses éléments par un ensemble de méthodes dont voici la description :


property ObjectData[const Index: Integer]: TObject ; default ;

ObjectData est une propriété tableau qui, en lecture, permet d'obtenir l'objet d'indice Index, et, en écriture affecte l'entrée d'indice Index avec un nouvel objet (une vérification de type est effectuée); comme le composant possède toutes les instances qu'on lui ajoute, les objets sont libérés automatiquement, soit lorsqu'un objet est remplacé par un autre, soit lorsque le composant est libéré de la mémoire.
Si le composant TObjectDataSet de nom ObjODS gère des instances de la classe TTest, on accèdera au premier élément avec une instruction du type :
ObjTest := ObjODS[0] as TTest ; // ObjTest de type TTest


procedure Insert(Index: Integer; const Obj: TObject);

Cette procédure permet d'insérer un objet TTest à l'indice Index ; une vérification de type est effectuée.


function Add(Obj: TObject): Integer;

Cette fonction ajoute un objet TTest à la fin de la liste des objets et renvoie l'index auquel l'objet est placé.


procedure Delete(Index: Integer);

Supprime l'objet d'indice Index. L'objet de type TTest est libéré de la mémoire.


procedure LoadFromFile(const FileName: String);

Cette procédure et la suivante gère la sérialisation des objets du composant dans un fichier. Pour en bénéficier la class TTest doit être dérivée de TComponent ou de l'une de ses classes descendantes.


procedure SaveToFile(const FileName: String);


Ressource

Le fichier zip comporte le composant TObjectDataSet ainsi qu'un projet de démonstration pour Delphi 5.
tobjds.zip


Retour à la page d'accueil

Evénements COM+

(Octobre 2001)


Cet article décrit la mise en oeuvre des événements COM+ au travers d'une application COM+ de test, composée d'un objet présentant une interface événement et d'un objet COM+ la supportant, avec Delphi. Nous montrerons comment ensuite exporter un proxy de cette application vers une machine cliente distante, pour que l'événement soit déclenché depuis celle-ci, et que l'objet qui a souscrit à cet événement soit instancié et réponde à cet événement.

MTS (Microsoft Transaction Server, pour les versions de Windows antérieures à Windows 2000) et COM+ (pour Windows 2000 ou plus) offrent un environnement distribué avec l'utilisation des services de transaction, la sécurité et la gestion des ressources. Delphi utilise le terme objet transactionnel pour désigner des objets qui utilisent ces services. L'application COM+ qui fait l'objet de cet article comprend deux objets transactionnels : l'objet événement et l'objet souscrivant à cet événement. Une application test permettra de créer une instance de l'objet événement, puis de déclencher un événement par appel de l'une des méthodes supportées par l'interface d'événement. COM+ se chargera alors d'invoquer les objets qui auront souscris à cet événement.


Objet événement

Créons tout d'abord l'objet événement en utilisant l'expert Objet Evénement COM+ de la palette ActiveX de Delphi. Pour cela, un projet de bibliothèque ActiveX est créé.
La création de cet objet est peu différente de celle d'un objet automation : vous devez saisir un nom de CoClasse et un nom d'interface qui aura IDispatch comme interface parent. Toutes les méthodes de l'interface devront être abstraites. Dans notre exemple, l'interface IObjEvent ne contient que la méthode Evenement et nous ne fournissons aucune implémentation de cette méthode.

TObjEvent = class(TAutoObject, IObjEvent)
protected
  procedure Evenement; virtual; safecall; abstract;
end;

Après compilation, l'étape suivante consiste à recenser le serveur ActiveX puis à installer l'objet COM+ en utilisant le menu " Exécuter|Installation des objets COM+ " de Delphi. Dans la boîte de dialogue " Installation des objets COM+ " cochez l'objet à installer et choisissez de l'installer dans une nouvelle application COM+ nommée " Test événement COM+ ".
Si vous démarrez le Service de composants à partir des " Outils d'administration " du " Panneau de configuration ", vous verriez " Test événement COM+ " avec notre composant, présent comme application COM+.




Objet client

Passons maintenant à l'objet transactionnel qui doit manifester son intérêt pour l'objet événement. Dans Delphi, nous démarrons une nouvelle bibliothèque ActiveX et y ajoutons un nouvel objet COM (par l'expert Objet COM). Celui-ci doit implémenter l'interface de l'objet événement.
Dans la boîte de dialogue de l'expert, vous devez sélectionner l'interface IObjEvent à l'aide du bouton Liste.
Après la génération de code, vous pouvez constater que la bibliothèque de types du projet ne comporte que la CoClasse pour laquelle vous allez donner une implémentation de la méthode Evenement.
Comme pour l'objet événement, après compilation, vous devez recenser le serveur et installer l'objet transactionnel dans l'application COM+ " Test événement COM+ ".
Dans le Service de composants, cette application contient maintenant deux objets.
Pour que l'objet client soit notifié de tout événement venant de l'objet événement, il faut créer un nouvel abonnement pour celui-ci dans le Service de composants. Sélectionnez l'icône " Abonnements " de l'objet client, puis avec le bouton droit de la souris créez un nouvel abonnement. L'assistant " Nouvel abonnement COM " vous guide alors dans la création de l'abonnement. Vous devez d'abord sélectionner la méthode qui sera utilisée pour l'abonnement, l'objet client qui implémente l'interface d'événement, et enfin un nom d'abonnement.
Il ne reste plus qu'à écrire une application de test qui instanciera l'objet événement et déclenchera la méthode Evenement de l'interface IObjEvent.


Application test

L'application de test peut effectuer un uses de l'unité générée à partir de la bibliothèque de type du l'objet événement pour utiliser la fonction de classe CoObjEvent.Create et créer une instance de l'objet événement :

procedure TForm1.Button1Click(Sender: TObject);
var
  FEvent: IObjEvent;
begin
  FEvent := CoObjEvent.Create;
  FEvent.Evenement;
end;

Le code ci-dessus a pour effet de déclencher l'exécution du code de réponse de l'objet client après son instanciation par COM+. Dans le Service de composants, l'activation d'un composant est signalé par une animation de l'icône du composant correspondant.


Exportation du proxy de l'application COM+

Vous pouvez utiliser le Services de composants pour exporter un proxy de l'application COM+ " Test événement COM+ ". Une fois le proxy installé sur une machine cliente, l' application de test pourra utiliser les informations du proxy pour, via DCOM, déclencher la réponse de l'objet client.
Pour cela, sélectionner " Test événement COM+ " et par le bouton droit de la souris exécuter l'assistant d'exportation d'applications COM+ en choisissant d'exporter en tant que proxy d'application. L'assistant génère alors les fichiers d'installation.

Retour à la page d'accueil

Information RTTI

(Février 2000)


Les informations de type à l'exécution (RTTI) sont générées pour les membres publiés d'une classe. Elles permettent à une application d'interroger dynamiquement les champs et les propriétés d'un objet et de localiser ses méthodes.
Une classe ne peut avoir de membres publiés que si elle est compilée avec l'état {$M+} ou si elle descend d'une classe compilée avec l'état {$M+}. La plupart des classes contenant des membres publiés dérivent de TPersistent qui est compilée avec l'état {$M+}, il est donc rarement nécessaire d'utiliser la directive $M.
L'unité TypInfo de Delphi contient les fonctions et les méthodes donnant l'accès aux membres publiés, comme la fonction GetPropList.

L'exemple de code ci-dessous permet d'obtenir dans une boîte de liste les noms des membres publiés de la classe TExmpl :

procedure TForm1.Button1Click(Sender: TObject);
var
  PropList: PPropList;
  i: Integer;
begin
  PropList := AllocMem(SizeOf(PropList^));
  i := 0;
  try
    GetPropList(TExmpl.ClassInfo, tkAny, PropList);
    while (PropList^[i] <> nil) and (i < High(PropList^)) do
    begin
      ListBox1.Items.Add(PropList^[i].Name + ':' + PropList^[i].PropType^.Name);
      Inc(i);
    end
  finally
    FreeMem(PropList);
  end;
end;

Retour à la page d'accueil

Interface d'activation

(Mars 2002)


La définition d'une interface d'activation personnalisée et son implémentation vont nous permettre de passer des arguments lors de la création d'un objet COM. Cet article montre comment créer une telle interface avec Delphi, et par suite le moyen d'implémenter le modèle de conception singleton en programmation COM.

Lorsque l'on crée un objet automation avec l'assistant de l'EDI Delphi, l'unité comprenant la déclaration de la nouvelle CoClasse, TTest par exemple, a, dans sa clause initialization, une instruction de la forme :

TAutoObjectFactory.Create(ComServer, TTest, Class_Test, ciMultiInstance, tmApartment);

Cette instruction crée au démarrage du serveur une instance de TAutoObjectFactory enregistrée comme étant l'usine de classe (ou fabrique d'objets) qui sert à créer des objets TTest. TAutoObjectFactory implémente en particulier l'interface COM IClassFactory.
Lorsqu'un client cherche à obtenir un pointeur d'interface sur un nouvel objet COM, que se soit par un appel indirect à CreateComObject ou par la fonction CreateOleObject, c'est la fonction de haut niveau CoCreateInstance de l'API Windows qui est appelé. Celle-ci détermine quel est le fabriquant de classe associé à l'objet demandé et obtient un pointeur d'interface IClassFactory de manière à invoquer sa méthode CreateInstance pour créer un nouvel objet.
Malheureusement il n'est pas possible de passer des arguments à CreateInstance pour avoir l'équivalent d'un constructeur de classe.
Le reste de cet article vous conduit pas à pas dans l'implémentation d'une interface personnalisée d'activation. Les codes source peuvent être obtenus dans la section Ressources en fin d'article.


Création d'un objet automation

Dans l'EDI Delphi, créez une nouvelle application Project1, puis ajouter un nouvel objet automation avec comme nom de CoClasse "Test". Nous enregistrons avec le nom Test l'unité générée par Delphi et comprenant la définition de la classe Test dérivée de TAutoObject et supportant l'interface ITest.
A des fins de test, nous définissons la propriété Message de type BSTR (WideString) pour l'interface ITest avec l'éditeur de la bibliothèque de types du projet. Nous nous contenterons de lire ou de modifier la variable privée FMessage de TTest.


Création et implémentation de l'interface personnalisée d'activation

Nous souhaitons définir une interface personnalisée nommée ITestFactory dont une des méthodes nous permettra d'instancier des objets TTest en lui passant une chaîne de caractères unicode en argument et renvoyant un pointeur d'interface ITest. En Pascal Objet :

function CreateTest(const Message: WideString): ITest; safecall;


Pour cela nous créons dans l'éditeur de la bibliothèque de types une nouvelle interface qui hérite de IUnknown et lui ajoutons la méthode décrite ci-dessus.
Il nous reste à définir la fabrique de classe supportant l'interface ITestFactory. Nous pouvons nous appuyer sur la classe TAutoObjectFactory :

TTestFactory = class(TAutoObjectFactory, ITestFactory)
protected
  function CreateTest(const Message: WideString): ITest; safecall;
end;

La méthode CreateTest se contente d'instancier un object TTest sous forme du pointeur d'interface ITest :

function TTestFactory.CreateTest(const Message: WideString): ITest;
begin
  Result := TTest.Create(Message) as ITest;
end;


Enregistrement de la fabrique de classe

Dans la clause initialization de l'unité Test, nous remplaçons l'instruction TAutoObjectFactory.Create(...) par TTestFactory.Create(...).
Cela a pour conséquence d'enregistrer TTestFactory comme fabrique de classe pour la classe TTest au lieu de TAutoObjectFactory qui aurait instancié des objets TTest à l'aide de IClassFactory.
Au démarrage du serveur, une instance de notre fabrique de classe est donc créée et enregistrée.


Accéder à la fabrique de classe

Une application cliente peut obtenir une référence vers l'instance enregistrée de la fabrique de classe du serveur à l'aide de la méthode GetObject définie dans l'unité ActivationObject.
Le serveur servant de test, Project1, doit bien sûr être recenser au préalable, soit en l'exécutant une première fois, soit en l'exécutant avec l'argument /regserver.
Cette fonction prend comme unique argument un GUID devant être celui de la classe pour laquelle celle-ci renvoie un fabriquant de classe. Dans notre exemple, c'est la constante CLASS_Test de la bibliothèque de types. GetObject renvoie une référence IUnknown qui peut ensuite être transtypée en ITestFactory à l'aide de l'opérateur as.
Le programme de test, Project1, du répertoire Client utilise donc la fabrique de classe de TTest pour instancier un objet TTest avec la méthode ITestFactory.CreateTest, de la manière suivante :

var
  TestFactory: ITestFactory;
begin
  TestFactory := GetObject(CLASS_Test) as ITestFactory;
  Test := TestFactory.CreateTest(Edit1.Text); // Test est de type ITest
end;

La fonction GetObject (nom d'une fonction équivalente en Visual Basic) est implémentée un utilisant un moniker de classe. La description des monikers déborde du cadre de cet article.


Singleton COM

L'utilisation d'une interface personnalisée d'activation permet l'implémentation du modèle de conception singleton en programmation COM. En effet, une fonction telle que CreateTest pourrait toujours renvoyer la même instance d'objet créée une seule fois dans le fabriquant de classe, au premier appel de cette méthode.


Ressources

Téléchargez les programmes d'exemple
Activation.zip


Retour à la page d'accueil

Interface Objet-Document XML

(Mai 2001)


XMLObject est le nom de l'unité Pascal Objet implémentant la classe TXMLObject. Cette classe offre un moyen simple de construire et de manipuler des documents XML en permettant une "sérialisation" des valeurs des propriétés publiées des objets au format XML.
En effet, TXMLObject se base sur l'information de type à l'exécution (RTTI) d'un objet -dont la classe étend TXMLObject- pour générer un flot de données XML représentant cet objet avec les valeurs de ses propriétés publiées.
TXMLObject reconnaît également les références vers d'autres instances de TXMLObject et prend aussi en charge des références de TCollection dont les éléments sont des instances de TXMLObject.
La création et la relecture de documents XML est ainsi grandement facilité puisqu'il n'est pas nécessaire d'écrire le code qui fait le lien entre les données de vos classes et un document XML, non plus celui qui lirait un document XML pour initialiser un ensemble d'objets.
TXMLObject peut être vu comme une interface objet/document XML.


Examinons un exemple simple tiré de l'unité Unit1 du projet test Delphi Project1.dpr (voir "Ressource" ci-dessous).

La première étape consiste à déclarer une classe étendant la classe TXMLObject :

TXMLObject1 = class(TXMLObject)
  private
    FPropString1, FPropString2: string;
    FPropInteger: Integer;
    FPropFloat: Double;
  published
    property PropString1: string read FPropString1 write FPropString1;
    property PropString2: string read FPropString2 write FPropString2;
    property PropInteger: Integer read FPropInteger write FPropInteger;
    property PropFloat: Double read FPropFloat write FPropFloat;
  end;

L'appel de la méthode TXMLObject.SaveToFile() sur l'objet XMLObject1, créé de la manière suivante :

XMLObject1 := TXMLObject1.Create(nil);
  with XMLObject1 do
  begin
    PropString1 := 'Hello';
    PropString2 := 'World !';
    PropInteger := 123;
    PropFloat := 1.23;
    SaveToFile('XMLObject1.xml');
  end;
end;

produit le fichier texte suivant :

<XMLOBJECT ClassName="TXMLObject1">
  <PropString1>Hello</PropString1>
  <PropString2>World !</PropString2>
  <PropInteger>123</PropInteger>
  <PropFloat>1,23</PropFloat>
</XMLOBJECT>


L'attribut ClassName est nécessaire puisqu'il permettra de recréer une instance du type indiqué dans ClassName à l'aide de l'une des deux fonctions de la partie interface de l'unité XMLObject :

function CreateXMLObjFromFile(const FileName: string): TXMLObject;
function CreateXMLObjFromString(const Text: string): TXMLObject;

Pour que cela fonctionne dans le cas de notre exemple, il faut avoir une instruction enregistrant la classe TXMLObject1, par exemple dans la partie d'initialisation de l'unité, RegisterClasses([TXMLObject1]).

On pourra donc recréer une instance de TXMLObject1 par :

XMLObject1 := CreateXMLObjFromFile('XMLObject1.xml') as TXMLObject1;

XMLObject1 étant bien sûr une référence de type TXMLObject1.


L'unité Unit1 déclare aussi les classes TXMLObject2 et TXMLObject3 destinées à démontrer comment les propriétés référençants d'autres instances de TXMLObject ou des objets de type TCollection (dont les éléments sont des TXMLObjects), sont utilisées.


Ressource

Téléchargez XMLObject 0.9 (Delphi 5)
xmlobj.zip


Retour à la page d'accueil

Manipulateur de contrôles

(Mars 2000)


Basée sur TCustomControl, la classe TControlHandler permet de manipuler à l'exécution tout objet descendant de TControl ; tout le code nécessaire à cette gestion est ainsi externe à vos classes.
Pour le réaliser, l'idée principale est de contruire une fenêtre qui entourera entièrement le contrôle désigné par la méthode SetControl, en combinant des régions Windows. Le reste consiste à répondre aux évènements classiques envoyés par la souris.

Vous pouvez télécharger le code source de cette classe (qui peut devenir aisément un composant), avec un projet Delphi 5. Celui-ci devrait fonctionner avec les autres versions de Delphi.


Ressource

Téléchargez control.zip :
control.zip


Retour à la page d'accueil

Messages EventLog

(Octobre 2001)


Journal d'événements NT : création d'un fichier de messages

Les fonctions de l'API Windows : OpenEventLog et ReportEvent permettent d'ouvrir et d'écrire dans le journal d'événements pour une application.
Le second argument de la fonction OpenEventLog, SourceName, doit désigner le nom de la source, qui peut être le nom de l'application.
Pour que l'Observateur d'événements puisse filtrer les événements, la clé " SourceName " doit exister dans le chemin de registres suivant :

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\SourceName

On peut préciser un fichier de messages qui sera utilisé pour récupérer le message associé à l'idenficateur d'événement passé à la fonction ReportEvent. Définissez pour la clé " SourceName ", la variable " EventMessageFile " de type REG_EXPAND_SZ, contenant le nom du module -généralement une dll de ressources- comprenant les messages.


Création du fichier de messages


La création d'un fichier de messages utilise l'utilitaire Microsoft Message Compiler (mc.exe), qui transforme un fichier texte, respectant une certaine syntaxe, en un fichier binaire devant être inclus dans un fichier script de ressources. Ce dernier fichier doit ensuite être compilé pour obtenir un fichier de ressources compilé qui sera lié à l'application.

Voici les étapes à suivre pour créer une dll de ressources comportant des messages utilisables par l'EventViewer :

1. Tout d'abord, créer un fichier texte d'extension mc (appelons le test.mc). On se reportera à l'aide du MS Message Compiler pour connaître la syntaxe à respecter. Par exemple :

MessageId=1
Language=English
HelloWorld
.

Prendre garde au fait qu'il faut toujours une nouvelle ligne après le point. Ici, nous utilisons " English " qui est la langue définit par défaut.


2. Invoquer mc.exe en passant en argument le fichier test.mc. Par exemple :

mc test.mc

Après cet appel mc aura créer un fichier en-tête .h, le fichier MSG00001.bin comportant les messages (nom par défaut pour la langue anglaise), et le fichier test.rc contenant, sous forme de script de ressouces, une référence au fichier MSG00001.bin.


3. Compiler le fichier de description de ressouces
Le fichier mc.rc peut ensuite être compilé pour obtenir le fichier de ressources compilé test.res qui sera lié au module final. On pourra utiliser le compilateur de ressources de Borland brcc32.exe.


4. Créer une nouvelle dll
Créer une nouvelle dll et y inclure le fichier de ressources compilé obtenu précédemment. Il ne reste plus qu'à compiler.
Dans une unité Pascal, on aura recours à directive {$R test.res}.
La valeur de la variable " EventMessageFile " (voir plus haut) doit contenir le chemin absolu de cette dll.

Retour à la page d'accueil

Navigateur hyperbolique

(Novembre 2001)


Description du navigateur hyperbolique, ou arbre hyperbolique

Le navigateur hyperbolique utilise la technique focus+contexte pour visualiser des données hiérarchiques avec un affichage dynamique.
L'utilisateur peut fixer son attention sur une partie détaillée des données, celle qui l'intéresse, comme si il la regardait au travers d'une loupe, tandis que les autres données, mises en retrait, apparaissent plus discrètement.






Composant Delphi

Le navigateur hyperbolique est implémenté sous forme de composant pour Delphi 6 que vous pouvez télécharger (voir la rubrique Ressources). Ce composant est conçu de manière suffisamment ouverte pour permettre l'affichage de tout type de données (texte, image, etc.) en donnant accès au Canvas du contrôle.
L'installation du contrôle requiert les fichiers suivants :

OEHPVControl.dpk
HPVControl.dcu
HPV.dcu
HPVControl.dcr

Il suffit d'installer le paquet OEHPVControl.dpk pour le contrôle HPVControl (HyPerbolic Viewer) apparaisse dans l'onglet OE (OBJECT-EVERYWHERE) de la palette d'outils de Delphi.
Le fichier zip à télécharger comprend également un projet Delphi 6 de démonstration, utilisant la parseur XML de Microsoft, ainsi qu'une documentation au format HTML.

Ce composant est libre d'utilisation dans un cadre non commercial.


Utilisation du navigateur

L'utilisateur manipule le navigateur hyperbolique à l'aide de la souris, au moyen d'un glisser/déplacer, pour amener au centre du contrôle les données qu'il souhaite examiner. Si pendant cette opération la touche SHIFT est maintenue enfoncée, il se produit une rotation autour du noud de départ. Et enfin, si la touche CTRL est maintenue enfoncée, le click gauche de la souris déclenche une animation, qui vient placer le point du click au centre du contrôle.


Ressources

Télécharger le navigateur hyperbolique pour Delphi 6
HPV.zip


Retour à la page d'accueil

Navigation web

(Décembre 2000)


A l'exécution un activeX peut demander au navigateur Internet de charger une nouvelle URL ou de se déplacer dans les adresses déjà consultées.
Pour cela on trouvera des fonctions dans l'unité UrlMon de Delphi telles que HlinkGoBack, HLinkGoForward ou HlinkNavigateString.
Si vous avez développé un ActiveForm que vous incluez dans une page HTML, provoquer le chargement d'une nouvelle URL peut s'obtenir par le code suivant :

procedure TActiveTest.BtnEnvoyerClick(Sender: TObject);
var
  Buffer: array[0..127] of WideChar;
begin
  StringToWideChar(Lien, Buffer, SizeOf(Buffer) div 2);
  HLinkNavigateString(IUnknown(VCLComObject), Buffer);
end;

Retour à la page d'accueil

Récepteur d'objet connectible COM

(Octobre 2000)


Qu'est-ce qu'un objet connectible ?

Un objet connectible est un objet COM capable d'avertir son client qu'un événement s'est produit dans sa sphère. Les types les plus célèbres d'objet connectible sont les ActiveX.
L'objet défini une interface appelée sortante ou source, qui est implémentée par le client. Par définition, l'objet récepteur, qui réside chez le client, est un objet implémentant l'interface sortante.
Pour utiliser un ActiveX et l'installer dans sa palette de composants, Delphi effectue entre autres, une déclaration de classe dérivée de la classe TOleControl de l'unité OleCtrls, dans une unité de compilation, en utilisant la bibliothèque de types de l'objet.
L'interface d'évènement de l'ActiveX est implémentée par la classe TEventDispatch dont une instance est créée à l'exécution par la classe de base TOleControl et qui a donc le rôle de récepteur de l'objet connectible. C'est la procédure virtuelle pure InitControlData de TOleControl, devant donc être implémentée par les classes descendantes, qui fournit en particulier l'identificateur de l'interface d'événements dans le champ EventIID de la structure TControlData.
On peut aussi noter qu'une interface sortante d'ActiveX semble toujours être une dispinterface, c'est-à-dire une interface IDispatch.

Prenons l'exemple de l'ActiveX VtChart fourni avec Delphi 5 ; nous trouvons bien dans le fichier source Delphi5\Ocx\Vci\Impress\Vcfi\vcfi.pas la déclaration de la classe TVtChart héritant de TOleControl et surchargeant la procédure InitControlData.


Ecriture d'un récepteur générique

Dans certaines circonstances il peut être intéressant de créer un ActiveX à l'exécution, en ayant uniquement son progID (c'est ce qui permet de faire le lien avec l'identificateur de classe, -le CLSID- de l'objet), tout en prenant en charge ses événements.
C'est le cas des contrôles ActiveX de type OPOS (OLE for Point Of Sell) offrant un accès aux périphériques d'encaissement. Par exemple, l'OPOS imprimante offre toujours les mêmes propriétés, méthodes et événements, mais ces contrôles sont fournis par différents constructeurs de caisse, ce qui entraîne des identificateurs GUID différents pour les classes et les interfaces. Il est donc nécessaire de les connaître au moment de la compilation.
L'idée ici est de définir un contrôle OLE générique pour un ensemble d'ActiveX supportant les mêmes interfaces.
Pour cela, nous partirons du source de l'unité OleCtrls.pas pour le modifier et que nous appellerons finalement OleCtrls2.pas. Vous pouvez télécharger ce fichier ainsi que le programme d'exemple créant l'ActiveX VtChart dynamiquement à partir de son progID qui est 'VCFI.VCFiCtrl.1'.
Dans ce programme nous créons également une classe TVtChart2 dérivant de TOleControl, redéfinissant la procédure InitControlData (mais sans fournir d'identificateur pour l'interface d'évènements), et ne prenant en charge, pour simplifier, que deux événements de l'ActiveX.
Le CLSID est obtenu grâce à la fonction ProgIDToClassID de l'unité ComObj, tandis que nous avons modifier la méthode InterfaceConnect de OleCtrls pour obtenir le point de connection de l'ActiveX.
Ici nous employons l'interface IEnumConnectionPoints pour obtenir le premier point de connection. Ensuite c'est l'appel de la méthode Advise de l'interface IConnectionPoint que supporte l'ActiveX qui fait le lien avec le récepteur implémentant l'interface sortante IDispatch.
Une autre méthode a été modifiée : la méthode QueryInterface de la classe TEventDispatch pour renvoyer un pointeur d'interface IDispatch sans vérification, si l'on demande autre chose qu'un pointeur d'interface de type IUnknown.

Une dernière chose : l'implémentation de la classe de sommet TObject étant compatible avec la norme binaire COM (vTable), il est logique de trouver d'abord les données membres correspondants aux gestionnaires d'évènement comme c'est le cas dans la déclaration de la classe TVtChart2.


Ressources

OleCtrls2.pas et le projet de test pour Delphi 5 :
com.zip

Un utilitaire à posséder absolument : OLE/COM Object Viewer
http://www.microsoft.com/com/resources/oleview.asp


"Au coeur de Distributed COM", Microsoft Press.

Retour à la page d'accueil

Sérialisation

(Mars 2000)


Les méthodes ReadComponent et WriteComponent de la classe abstraite TStream permettent de lire et d'écrire des composants avec leurs propriétés dans un flux. On peut ainsi utiliser les classes de flux TMemoryStream, TFileStream ou TStringStream par exemple, pour sérialiser des instances de toute classe descendant de TComponent.

L'exemple suivant écrit un composant TButton dans un flux mémoire, le relit par l'instruction ReadComponent(nil) qui construit un composant en se fondant sur les informations de type du flux et renvoie le composant qu'elle a créé. On obtient ici un copie du composant TButton qui est ensuite placé dans la fiche.

procedure TForm1.Button1Click(Sender: TObject);
// Ecriture et lecture dans un TMemoryStream
var
  Stream: TMemoryStream;
  Bouton: TButton;
begin
  Stream := TMemoryStream.Create;
  try
    // Ecrire le composant dans le flux
    Stream.WriteComponent(Button1);

    // Relire le composant en se positionnant d'abord au début du flux
    Stream.Position := 0;
    Bouton := TButton(Stream.ReadComponent(nil));
    Bouton.Left := Bouton.Left + 10;
    Bouton.Top := Bouton.Top + 10;

    // Le nouveau composant est inséré dans la fiche
    InsertControl(Bouton);
  finally
    Stream.Free;
  end;
end;

initialization
  RegisterClasses([TButton]);

Il nécessaire d'enregistrer les classes qui devront être relues depuis un flux, sinon on obtiendra une exception indiquant qu'une classe n'est pas enregistrée.

A suivre...

Retour à la page d'accueil

SharedMem

(Janvier 2002)


Il est possible de partager un espace de mémoire entre plusieurs applications au travers d'une DLL qui est chargée par chacune d'elles. Cet article vous permettra de télécharger cette DLL ainsi que son code source pour Delphi.

Au premier chargement, la DLL crée un fichier mappé en mémoire qui permettra aux différentes applications de lire ou d'écrire dans cette mémoire qui est ainsi allouée. Celle-ci est désallouée au moment où la DLL est déchargée par la dernière application qui l'utilise.
Les deux fonctions exportées suivantes permettent d'écrire et de lire dans la zone de mémoire partagée :

procedure SetSharedMem(lpszBuf: PChar);
procedure GetSharedMem(lpszBuf: PChar; cchSize: Integer);


Ressources

Télécharger SMem.dll ainsi qu'un programme d'exemple (testé avec Delphi 6 sous Windows 2000)
SMem.zip


Retour à la page d'accueil

SvcUtils

(Janvier 2002)


L'unité SvcUtils montre comment interagir avec le contrôleur de services de Windows NT pour démarrer un service, l'arrêter ou retrouver son implémentation physique, par programmation.


Ressources

Télécharger SvcUtils.pas (testé avec Delphi 6)
SvcUtils.zip


Retour à la page d'accueil

WebService

(Août 2001)


Cet article vous montrera comment écrire un serveur web implémentant un WebService avec SOAP (Simple Object Access Protocol) en tirant partie du composant THTTPSoapPascalInvoker de Delphi 6

En effet, la dernière mouture de Delphi permet de créer un service web exploitant le protocole SOAP 1.1, sous forme de dll ISAPI ou comme extension pour serveur Apache par exemple. Ici, je vous propose de partir des composants Internet freeware de F. Piette (voir la rubrique Ressources), en particulier du projet d'exemple WebServ utilisant le composant THttpServer et permettant d'exécuter un serveur web.
SOAP définit un format d'échange de données XML reposant sur le protocole HTTP. Lorsqu'un client émet une requête vers un service web, il s'agit d'une requête HTTP de méthode POST dont l'entête contient l'élément SOAPAction et le corps le message XML obéissant à SOAP.
Pour une telle requête, l'événement OnPostedData du composant THttpServer est déclenché. On détermine si l'entête de la requête a le paramètre SOAPAction en le cherchant dans la liste des chaînes de caractères de la propriété RequestHeader de l'objet de type TMyHttpConnection. S'il existe une valeur pour ce paramètre, on considère qu'il s'agit d'une requête SOAP et la méthode TWebServForm.DispatchSOAP est déclenchée :

const DefException =
   '<?xml version="1.0" encoding=''UTF-8''?>' +
   '<SOAP-ENV:Envelope' + ' ' +
   'xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" >' +
   '<SOAP-ENV:Body> ' +
   '<SOAP-ENV:Fault> ' +
   '<faultcode>SOAP-ENV:Server</faultcode>' +
   '<faultstring>%s</faultstring>' +
   '</SOAP-ENV:Fault>' +
   '</SOAP-ENV:Body>' +
   '</SOAP-ENV:Envelope>';

procedure TWebServForm.DispatchSOAP(Client: TMyHttpConnection; const SOAPAction: string);
var
    Stream: TStream;

    SoapPascalInv: THTTPSoapPascalInvoker;
    Request: TStringStream;
    Response: TMemoryStream;
    WResponse: WideString;

    procedure SendResponse;
    var
        Body: string;
        Header: string;
    begin
        Body := UTF8Encode(WResponse);
        Header := Client.Version + ' 200 OK' + #13#10 +
                  'Content-Type: text/xml' + #13#10 +
                  'Content-Length: ' +
                  IntToStr(Length(Body)) + #13#10 +
                  #13#10;
        Stream.Write(Header[1], Length(Header));
        Stream.Write(Body[1], Length(Body));
        Stream.Seek(0, 0);
        { Ask HTTP server component to send data stream for us }
        Client.DocStream := Stream;
        Client.SendStream;
    end;

begin
    try
        try
            Response := TMemoryStream.Create;
            try
                SoapPascalInv := THTTPSoapPascalInvoker.Create(nil);
                Request := TStringStream.Create(Client.FPostedDataBuffer);
                try
                    SoapPascalInv.DispatchSOAP('/soap', SOAPAction, Request, Response);
                finally
                    Request.Free;
                    SoapPascalInv.Free;
                end;

                { Now create output stream to send back to remote client }
                Stream := TMemoryStream.Create;
                SetLength(WResponse, Response.Size div 2);
                Response.Read(WResponse[1], Response.Size);

                SendResponse;
            finally
                Response.Free;
            end;
        except
            on E: Exception do
            begin
                WResponse := Format(DefException, [E.Message]);
                SendResponse;
            end;
        end;
    except
        on E: Exception do;
    end;
end;


Cette méthode crée un objet THTTPSoapPascalInvoker de manière à permettre l'appel de toute interface invoquable rencencée dans l'application avec l'exécution de sa méthode DispatchSOAP. Le troisième argument de cette méthode permet d'obtenir la réponse SOAP sous forme de flux TStream ; si il n'y a pas d'exception, ces données sont ensuite envoyées au client.


Ressources

ICS (Internet Component Suite) de F. Piette
http://overbyte.alexid.fr/eng/products/ics.html


Retour à la page d'accueil

XMLTransform

(Août 2001)


La classe TXMLTransform contient des fonctions de classe facilitant la transformation d'un document XML par un document de style XSL au moyen du parseur XML de Microsoft.

L'unité XMLTransform utilise l'unité MSXML de Delphi 6 qui est l'importation de la bibliothèque de type de msxml.dll. Pour les versions antérieures de Delphi il suffit de réaliser cette importation avec l'IDE.
L'unique classe TXMLTransform de XMLTransform facilite la transformation d'un document XML par un document de style XSL en définissant les fonctions de classe suivantes :

// Cette fonction renvoie un document XML chargé avec les données du fichier dont
// le nom est passé en argument. Une exception est lancée si le document est
// mal formé ou invalide.
class function OpenXMLDocument(const FileName: TFileName): IXMLDOMDocument;

// Cette fonction renvoie le résultat de la transformation du document xmldoc
// par le document de style XSL
class function Transform(const xmldoc, stylesheet: IXMLDOMNode): string; overload;

// Cette fonction renvoie le résultat de la transformation du fichier xmldoc
// par le fichier de style XSL ; celle-ci utilise OpenXMLDocument
class function Transform(const xmldoc, stylesheet: TFileName): string; overload;


Deux exemples d'utilisation, S étant de type string, XSLDoc de type IXMLDOMDocument :

S := TXMLTransform.Transform('Contacts.xml', 'Contact.xsl');

XSLDoc := CoDOMDocument.Create;
XSLDoc.loadXML('<CONTACTS>...</CONTACTS>');
S := TXMLTransform.Transform(TXMLTransform.OpenXMLDocument('Contacts.xml'), XSLDoc.documentElement);


Ressources

L'unité XMLTransform
XMLTransform.zip


Retour à la page d'accueil