TP: DCOM/COM+ Une architecture distribuée 3-tiers transactionnelle Patrick Smacchia 2002 1 2 3 4 5 6 Définitions.............................................................................................................. 2 Objectifs du TP ...................................................................................................... 3 Technologies à utiliser ........................................................................................... 3 Création de la base de données .............................................................................. 4 Création du DSN (Data Source Name) .................................................................. 6 Création du squelette de l’interface utilisateur ...................................................... 8 6.1 Création du projet C++ de l’interface utilisateur ........................................... 8 6.2 Edition du dialogue de l’interface utilisateur ................................................. 9 6.3 Méthodes et Attributs du dialogue interface utilisateur ............................... 10 7 Création du squelette du serveur .......................................................................... 12 7.1 Création du projet C++ du serveur .............................................................. 12 7.2 Création de l’objet COM CompteObj avec le wizard ATL ......................... 13 7.3 Analyse du fichier idl ................................................................................... 15 7.4 Définition du type de transaction supporté .................................................. 15 7.5 Analyse de la Class C++ de l’objet COM ComptObj .................................. 16 7.6 Compilation et Enregistrement des objets COM ......................................... 17 8 Remplissage des fonctions du serveur ................................................................. 18 8.1 Introduction à ADO (ActiveX Data Object) ................................................ 18 8.2 Import de la librairie de type ADO .............................................................. 18 8.3 Ajout d’une fonction pour ouvrir une connexion avec la DB ...................... 19 8.4 Ajout de fonctions pour manipuler des long signés avec ADO ................... 20 8.5 Le code de la fonction Ajoutez() ................................................................. 21 8.6 Le code de la fonction Fermez() .................................................................. 22 8.7 Le code de la fonction Consultez() .............................................................. 23 8.8 Le code de la fonction Transfererez() .......................................................... 24 9 Remplissage des fonctions de l’interface utilisateur ............................................ 26 9.1 Appel a CoInitialize() .................................................................................. 26 9.2 Import de la librairie de type de CompteServeur ......................................... 26 9.3 Macro pour les ClassID ............................................................................... 26 9.4 Le code de la fonction OnButtonAjoute() : ................................................. 27 9.5 Le code de la fonction OnButtonFerme() : .................................................. 27 9.6 Le code de la fonction OnButtonConsulte() : .............................................. 28 9.7 Le code de la fonction OnButtonTransfere() ............................................... 28 10 Faire de CompteServeur une COM+ Application transactionnelle ................. 29 10.1 Définitions d’un snap-in .............................................................................. 29 10.2 Ouverture du snap-in Components Services ................................................ 29 10.3 Ajout d’une nouvelle application COM+ .................................................... 31 10.4 Ajout de l’objet COM CompteObj dans notre COM+Application .............. 33 10.5 Vérification du mode transactionnel supporté ............................................. 34 11 Exécution du TP............................................................................................... 35 11.1 Déboguer un objet COM+ ........................................................................... 35 12 Bibliographie.................................................................................................... 36 Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 1/36 1 Définitions Architecture distribuée : Un logiciel est dit à ‘Architecture distribuée’ s’il est formé de plusieurs unités exécutables (appelés aussi composants) (.exe .dll sous NT) qui peuvent éventuellement être exécutées sur plusieurs machines en communiquant entre elles. 3-tiers : Une architecture distribuée est dite ‘3-tiers’ si elle est constituée des trois composants suivant : Interface Utilisateur Serveur Base de données L’interface utilisateur est utilisée pour manipuler des données. En général plusieurs instances de ce composant tournent sur plusieurs machines différentes pour permettre à plusieurs utilisateurs de travailler en même temps. Ce composant peut, par exemple, se présenter sous la forme d’une page web (ASP) ou d’un exécutable ou sous d’autres formes. Le serveur est utilisé pour : o fournir les données aux utilisateurs o enregistrer les modifications sur les données faites par les utilisateurs o faire des opérations sur les données (business rule en anglais) o garantir l’intégrité des données (peux être aussi gérer entièrement ou partiellement par la base donnée) o gérer la sécurité pour l’accès aux données (peux être aussi gérer entièrement ou partiellement par la base donnée) Plusieurs instances du composant serveur peuvent éventuellement être exécutées sur plusieurs machines en même temps, pour répartir la charge de calcul sur plusieurs machines. Dans ce cas il y a un composant entre l’interface utilisateur et le serveur pour répartir les appels des utilisateurs sur les machines. On appelle cette étape le ‘load balancing’ (balance la plus équitable possible entre les machines pour répartir la charge). La propriété centrale d’un serveur et sa ‘scalabilité’ (traduction non officielle du mot anglais scalable). On dit d’une application qu’elle est scalable, si, lorsque vous ajoutez du hardware pour l’exécuter (processeur, RAM, PCs etc) vous obtenez un gain de performance commensurable à la quantité de hardware ajoutée. En pratique, on observe que l’ajout de hardware pour exécuter une application ne provoque pas forcément un gain de performance. En effet, de nombreux goulets d’étranglement subsistent toujours à différents niveaux (locking des données, middleware…). Seule l’architecture de l’application peut minimiser les effets néfastes de ces goulets d’étranglement. La base de données est utilisée pour stocker les données. En général ce composant est codé par une autre entreprise (SQL server/Microsoft, Oracle…). En effet beaucoup de services sont requis pour manipuler efficacement et de manière sure des données (transaction, sécurité, réplication, répartition du stockage sur plusieurs disque durs, support du langage SQL, changement de version…). Néanmoins il arrive que la base de données soit propriétaire pour des raisons de performance ou de compatibilité ascendante. On appelle aussi ce type d’architecture une architecture client/serveur. Dans ce cas le serveur représente l’amalgame serveur/base de données. transactionnelle : Une succession d’opérations sur des données est dite ‘transactionnelle’, si à la fin de ces modifications tous les changement sont validés (commit), ou aucun des changement n’est validé (roll back). En général, le milieu transactionnel n’a (heureusement) pas à être codé. Le développeur utilise une API d’un milieu transactionnel existant (ADO/MTS/COM+ chez Microsoft…). La théorie nous dit que quatre propriétés doivent être supportées par un milieu transactionnel (les propriétés ACID) : Atomicity : Toutes les actions s’effectuent ou aucune. Consistency : Aucune action ne viole aucune loi d’intégrité des données. Isolation : Les transactions s’effectuent d’une manière isolée. Durable : Les résultats sont stockés de manière permanente. On remarque qu’une transaction peut s’effectuer sur plusieurs serveurs, et sur plusieurs bases de données différentes. (Exemple : Débit d’un compte de la banque X pour créditer un compte de la banque Y). Toutes ces contraintes font que la théorie des transactions distribuées est très compliquée. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 2/36 2 Objectifs du TP 3 Créer une architecture distribuée 3-tiers transactionnelle sur Windows 2000. Le logiciel développé sera de type logiciel bancaire/ gestion de comptes. Un compte sera indexé par un numéro et aura une somme positive ou nulle. Les opérations possibles à partir de l’interface utilisateur seront : Ouvrir un compte avec une somme de départ. Consulter un compte. Fermer un compte. Transférer une somme d’un compte à un autre. Concrètement on code : L’interface, qui permet : i. d’entrer les paramètres des 4 opérations bancaires. ii. d’appeler les 4 fonctions du serveur (correspondantes aux 4 opérations bancaires) iii. de visualiser les résultats. L’interface se présente sous la forme d’un .exe. Le serveur qui est un composant COM+. Il a une interface qui présentes les 4 fonctions correspondantes aux 4 opérations bancaires. Le code compilé du serveur est stocké dans un dll. Technologies à utiliser VC++ Le client et le serveur seront tous les deux codés sous la forme d’un projet Visual C++. DCOM Les clients communiqueront avec le serveur en utilisant la technologie Distributed Component Object Model. COM+ Le milieu transactionnel sera COM+. En fait avant Windows 2000 (NT), un service spécial était offert par Microsoft pour gérer des transactions. C’était MTS (Microsoft Transaction Server). Depuis MTS à été intégré à COM+. Concrètement il faudra utiliser une API Microsoft dans notre code, et utiliser le snap-in Component Services pour vérifier et/ou modifier les propriétés transactionnelles de notre serveur. SQL Server Notre base de données sera stockée dans SQL server. C’est un produit Microsoft qui permet de gérer les base de données au moyen du langage SQL. ADO Pour communiquer avec une base de données, Microsoft à développé un ensemble (une collection) d’objet COM appelé ADO (ActiveX Data Object). Ces composants s’appuient sur une couche appelée ODBC (Open Database Connectivity) qui permet de communiquer à partir d’une API avec différentes implémentation de bases de données (SQL server, Oracle…). Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 3/36 4 Création de la base de données Nous allons créer notre base de données au moyen des 2 scripts en langage SQL suivant : Script1: CREATE DATABASE TP_DCOM GO Script2: CREATE TABLE [dbo].[COMPTES] ( [ID] [int] IDENTITY (1, 1) NOT NULL, [Somme] [int] NOT NULL) GO La base de données s’appelle TP_DCOM. Elle contient une seule table COMPTES. La table COMPTES est constituée de 2 champs : Le champ ID : C’est le numéro du compte. Le mot clé IDENTITY nous apprend que les éléments de cette colonne sont tous différents. Ainsi on identifiera une ligne de cette table par ce numéro. C’est donc la clé primaire de notre table. On aurait pu renforcer ceci en utilisant la syntaxe PRIMARY KEY du langage SQL. SQL server calcule automatiquement ce nouveau numéro lorsque l’on insère une nouvelle ligne. L’algorithme utilisé par SQL server est le suivant. Premier numéro de compte :1 (premier paramètre après IDENTITY) Incrémentation du numéro de compte lorsque l’on ajoute un élément : 1 (deuxième paramètre apres IDENTITY) Naturellement pour effectuer cet algorithme SQL server stocke quelque part le numéro du dernier compte, mais ceci est complètement masqué pour l’utilisateur. Le type de ce champs est ‘int’. En SQL server cela signifie que c’est un entier signé sur 4 bytes. Cela signifie que l’on a potentiellement un peu plus de 2 milliards de numéros de compte différents possible. Le champ Somme : C’est encore un champ de type ‘int‘ mais ce n’est pas une clé primaire. En effet il n’y a pas d’objection à ce que 2 comptes différents aient la même somme. Une de nos business rule est que l’on autorise que les valeurs positives ou nulles. Le mot clé NOT NULL dans la définition d’un champ signifie que ce champ est obligatoirement initialisé avec une valeur correcte du type du champ. En effet SQL server autorise de ne pas initialiser certains champs dans une ligne. Lancer l’outil : MenuDémarrer/Programmes/Microsoft SQL server 7.0/Query Analyzer Choisir avec quelle machine on veut travailler, sous quel utilisateur SQL server. (voir lors du TP) Taper le script1 pour créer la base de données TP_DCOM, puis le lancer avec le bouton ► vert. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 4/36 Dans la Combo Box DB : choisir la base de données TP_DCOM. Taper le script2 pour créer la table COMPTES, puis le lancer avec le bouton ► vert. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 5/36 5 Création du DSN (Data Source Name) Un Data Source Name (DSN) est un enregistrement sur une machine. Il permet d’associer a un nom (i.e une chaîne de caractères) un accès à une base de données. Un programme s’exécutant sur la même machine peut ainsi avoir accès à la base très simplement à partir du nom du DSN (voir 8.3). Le DSN encapsule les problématiques suivantes : Accès à une DB distante (i.e une autre machine) Accès à la DB sécurisé (login password) Administration/Maintenance d’un système distribué simplifiées. Lancer l’outil : MenuDémarrer/Setting/Panneau de contrôle/Administrative Tools/Data Sources ODBC Cliquer Add et choisir SQL Server. Appeler le nouveau DSN : TP_DCOM_DSN. Choisisser quelle est la machine qui supportera la base de données. Cliquez Next > Choisir une authentification à partir de SQL server avec le login sa et sans password. Cliquez Next > Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 6/36 Changer la base de données par défaut et mettre la base TP_DCOM. Cliquez Next > Rien à faire de spécial sur cet écran. Cliquez Finish Cliquer tester la base de données. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 7/36 6 Création du squelette de l’interface utilisateur 6.1 Création du projet C++ de l’interface utilisateur Nous allons créer un projet MFC Dialog based pour notre interface. 1) Ouvrir Visual C++. 2) Ctrl N (Add New) 3) Choisir New Project MFC App Wizard (NE PAS SE TROMPER SUR LE TYPE DU PROJET). Appeler ce nouveau projet CompteManager. Mettre le projet dans un répertoire TP_DCOM ( NE PAS SE TROMPER SUR LE REPERTOIRE NI DE NOM DE PROJET). Cliquer OK. 4) Choisir l’option Dialog Based. Cliquer Finish . Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 8/36 6.2 Edition du dialogue de l’interface utilisateur Vous devez arriver directement sur la fenêtre d’édition d’un dialogue. Fabriquer un dialogue similaire à celui ci (vous utiliserez les contrôles de type : Button ; StaticText ; EditBox ; GroupBox) Button GroupBox StaticText EditBox ● Sur les 6 EditBox blancs cocher la propriété Number (i.e ils n’acceptent que des nombres) Appeler les (de haut en bas) IDC_EDIT_SOMME_INITIALE IDC_EDIT_COMPTEID_CONSULTEZ IDC_EDIT_COMPTEID_FERMEZ IDC_EDIT_COMPTEID_SRC IDC_EDIT_COMPTEID_DEST IDC_EDIT_SOMME_TRANSFERT ● Sur l’EditBox result cocher les propriétés ReadOnly et MultiLine. Appelez le IDC_EDIT_RESULT. ● Appeler les quatre boutons (de haut en bas) : IDC_BUTTON_AJOUTE IDC_BUTTON_CONSULTE IDC_BUTTON_FERME IDC_BUTTON_TRANSFERE A ce stade vous pouvez compiler et exécuter le programme (Ctrl F5). Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 9/36 6.3 Méthodes et Attributs du dialogue interface utilisateur Le dialogue créer par Visual C++ est en fait une classe qui s’appelle CCompteManagerDlg. Nous allons ajouter à cette classe : 7 attributs : 4 méthodes : un pour chaque EditBox. Un attribut représente l’élément minimal de dialogue. Par appel de la fonction CDialog ::UpdateData(TRUE) la valeur saisie par l’utilisateur est affectée à l’attribut. Par appel de la fonction CDialog ::UpdateData(FALSE) la valeur contenue dans l’attribut est affichée dans le contrôle graphique. une pour chaque Bouton. Une méthode est la fonction de notre ‘classe dialogue’ qui est appelée automatiquement sur un événement. Ici, l’événement est le bouton simplement clické gauche. Là encore le wizard va nous aider. Cliquer droit dans la fenêtre d’édition des dialogues et choisir ClassWizard. Sélectionner l’onglet Member Variables et ajouter 7 variables comme suit (Il y a 6 long et 1 CString) Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 10/36 Sélectionner l’onglet MessageMaps et ajouter les quatre fonctions comme suit qui vont être appelées sur l’évènement BN_CLICKED pour chacun des quatre boutons : A la fin du fichier CompteManagerDlg.cpp vous devez maintenant avoir les quatre fonctions suivantes : void CCompteManagerDlg::OnButtonAjoute() { // TODO: Add your control notification } void CCompteManagerDlg::OnButtonConsulte() { // TODO: Add your control notification } void CCompteManagerDlg::OnButtonFerme() { // TODO: Add your control notification } void CCompteManagerDlg::OnButtonTransfere() { // TODO: Add your control notification } handler code here handler code here handler code here handler code here Le remplissage de ces quatre fonctions fait l’objet de la partie 9 du TP. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 11/36 7 Création du squelette du serveur 7.1 Création du projet C++ du serveur Nous allons ajouter un projet ATL COM AppWizard au workspace CompteManager. ATL (ActiveX Template Library) est une aide (avec un wizard) fournie par Microsoft pour fabriquer des objets COM. Comme on va le voir par la suite, à l’aide du ‘wizard ATL’ on peut très simplement ajouter des composants COM, des interfaces COM sur ces composants, et des fonctions dans les interfaces. 1) Ouvrir Visual C++. 2) Ouvrir le WorkSpace CompteManager. 3) Cliquer droit sur le Workspace. 4) Add Projet To Workspace. ATL COM AppWizard (NE PAS SE TROMPER SUR LE TYPE DU PROJET). Cliquer Add to current Workspace. Appelez ce nouveau projet CompteServeur. Mettre le projet dans le répertoire TP_DCOM ( NE PAS SE TROMPER SUR LE REPERTOIRE NI DE NOM DE PROJET) (le même que CompteManager) Cliquez OK. 5) Sélectionner : ♦ Dynamic Link Library (le code de notre serveur va être stocké dans une dll) ♦ Support MFC (pratique) ♦ Support MTS (notre serveur va supporter les transactions MTS : Microsoft Transactional Server) Cliquer Finish. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 12/36 7.2 Création de l’objet COM CompteObj avec le wizard ATL Nous avons maintenant 2 projets dans notre workspace : CompteManager CompteServeur Pour travailler avec un projet, dans la fenêtre FileView cliquer droit sur le projet à sélectionner, cliquer set as active projet. On va travailler avec CompteServeur. Nous allons ajouter notre serveur. Dans la fenêtre ClassView cliquer droit sur CompteServeur classes. New ATL Object. Notre objet (notre serveur) va être de type MS Transaction Server. Cliquer Next Appelons notre objet Compte CompteObj. Sélectionnez Support IObjectControl (IObjectControl pour pouvoir être contrôlé par le code de Microsoft dans un serveur COM) Sélectionnez interface Dual (Pour accéder à cette objet par un langage comme VB, pas vu dans ce TP). Concrétement l’objet supportera auusi l’interface Microsoft IDispatch. Cliquer OK. L’interface COM ICompteObj est insérée dans les classes du CompteServeur. C’est la seule interface construite par nous même que l’objet COM CompteObj supporte. Il en supporte deux autres : IObjectControl et IDispatch . Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 13/36 Nous allons ajouter les quatre fonctions qui seront les quatre fonctionnalités de notre serveur à savoir : Ouvrir un compte avec une somme de départ. Consulter un compte. Fermer un compte Transférer une somme d’un compte à un autre. Cliquer droit sur IComptObj et cliquer Add Method. Ajouter les quatre méthodes comme suit : On remarque que dans la fonction (resp Ajoutez ,Consultez ) on a un paramètre de retour (resp pNewCompteID , pSomme) de type long. C’est grâce à la syntaxe [out] + pointeur. On utilise rarement la valeur de retour pour retourner des information du serveur. En effet cette valeur est très classiquement réservée pour retourner l’information, fonction exécutée avec succès, ou problème a l’exécution de la fonction avec un code d’erreur. (le tout sous forme d’un HRESULT) Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 14/36 7.3 Analyse du fichier idl 2) Aller dans le fichier CompteurServeur.idl (fenêtre FileView, répertoire Source Files),et constater que le code suivant à été ajoutez par le wizard: … // définition de l’interface ICompteObj dans le fichier idl // hérite de IDispatch car on avait choisi une interface duale. [ object, uuid(27E781AD-D5F7-4BEC-BC90-1776AEDB446D), dual, helpstring("ICompteObj Interface"), pointer_default(unique) ] interface ICompteObj : IDispatch { [id(1), helpstring("method Ajoutez")] HRESULT Ajoutez( long SommeInitiale, [out] long * pNewCompteID ); [id(2), helpstring("method Consultez")] HRESULT Consultez( long CompteID , [out] long * pSomme ); [id(3), helpstring("method Fermez")] HRESULT Fermez( long CompteID); [id(4), helpstring("method Transferez")] HRESULT Transferez( long CompteIDSrc , long CompteIDDest , long Somme ); }; … // définition de l’objet COM CompteObj dans le fichier idl [ uuid(530B9F72-A7EF-4A33-9AC2-B86951F57D49), helpstring("CompteObj Class") ] coclass CompteObj { [default] interface ICompteObj; }; 7.4 Définition du type de transaction supporté On souhaite qu’à chaque appel d’une fonction d‘une des interfaces de l’objet COM CompteObj une nouvelle transaction soit lancée. Pour cela dans le fichier CompteurServeur.idl rajouter : . . . import "oaidl.idl"; import "ocidl.idl"; #include "Mtxattr.h" [ object, . . . et . . . [ uuid(530B9F72-A7EF-4A33-9AC2-B86951F57D49), helpstring("CompteObj Class"), TRANSACTION_REQUIRES_NEW ] . . . Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 15/36 7.5 Analyse de la Class C++ de l’objet COM ComptObj Aller dans le fichier CompteObj.h et constater que le code suivant à été ajouté par le wizard : // définition de la classe C++ CCompteObj qui implémente le code de l’object COM CompteObj class ATL_NO_VTABLE CCompteObj : // hérite de CComObjectRootEx< CcomSingleThreadModel> car CompteObj ne s’exécutera que sur un seul thread public CComObjectRootEx<CComSingleThreadModel>, // hérite de CComCoClass car c’est une class C++ qui implémente un object COM, crée avec l’ATL. public CComCoClass<CCompteObj, &CLSID_CompteObj>, // hérite de IObjectControl pour pouvoir être contrôlé par le code de Microsoft dans un serveur COM (dllhost.exe) public IObjectControl, // hérite de IDispatch car on avait choit une interface duale (i .e accessible par un langage de script) public IDispatchImpl<ICompteObj, &IID_ICompteObj, &LIBID_COMPTESERVEURLib> { public: CCompteObj() { } DECLARE_REGISTRY_RESOURCEID(IDR_COMPTEOBJ) DECLARE_PROTECT_FINAL_CONSTRUCT() // on ne peut agréger cet objet COM DECLARE_NOT_AGGREGATABLE(CCompteObj) // cet objet COM supporte ces 3 interfaces BEGIN_COM_MAP(CCompteObj) COM_INTERFACE_ENTRY(ICompteObj) COM_INTERFACE_ENTRY(IObjectControl) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // Les méthodes pour l’interface IObjectControl // IObjectControl public: // La macro STDMETHOD déclare une fonction d’une interface COM qui retourne un HRESULT // Notez la syntaxe maladroite du passage du nom en paramètre de la macro STDMETHOD(Activate)(); STDMETHOD_(BOOL, CanBePooled)(); STDMETHOD_(void, Deactivate)(); CComPtr<IObjectContext> m_spObjectContext; // Les méthodes pour l’interface ICompteObj // ICompteObj public: STDMETHOD(Ajoutez) ( long SommeInitiale, /*[out]*/ long * pNewCompteID ); STDMETHOD(Consultez) (long CompteID , /*[out]*/ long * pSomme ); STDMETHOD(Fermez) (long CompteID); STDMETHOD(Transferez) ( long CompteIDSrc , long CompteIDDest , long Somme ); }; // Le code des méthodes pour l’interface IDispatch n’est pas accessible dans ce cas. Aller dans le fichier CompteObj.cpp et constater que le corps de nos quatre fonctions est : STDMETHODIMP CCompteObj::Consultez(long compteID, long *pSomme) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // TODO: Add your implementation code here return S_OK; } La macro STDMETHODIMP déclare une fonction d’une interface COM qui retourne un HRESULT La macro AFX_MANAGE_STATE appelle du code Microsoft pour assurer que l’objet que l’on appelle en ce moment est dans un état valide. Ne pas enlever cette macro. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 16/36 7.6 Compilation et Enregistrement des objets COM 3) A ce stade compiler (F7) le projet CompteServeur pour assurer qu’il compile. Notez que les informations surlignées dans la fenêtre ci-dessous nous montre que l’objet COM s’enregistre directement sur votre ordinateur. Pour s’en persuader vous pouvez lancer l’outils MenuDémarer/run/regedit, et ouvrir la clé de registre : My Computer/HKEY_CLASSES_ROOT /CompteServeur.CompteObj Dans HKEY_CLASSES_ROOT il y a tous les objets COM enregistrés (registered) sur votre machine. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 17/36 8 Remplissage des fonctions du serveur 8.1 Introduction à ADO (ActiveX Data Object) Comme vu précédemment nous allons utiliser la collection d’objets COM ADO (ActiveX Data Objet) pour accéder à la base de données. Nous allons utiliser seulement les deux principaux objets de cette collection : La connexion : Cet objet COM/ADO permet de créer une connexion avec la base de données. L’énorme bienfait de cet objet est qu’il est compatible avec les transactions COM+, c’est à dire que tous le travail effectué sur les données au sein d’une connexion lors d’une transaction est soit validés en entier, soit perdu en entier. La base de données n’est jamais dans un état inconsistant. Ceci est essentiel à notre projet bancaire. En effet, dans le cas d’un transfert d’une somme d’un compte vers un autre, on ne peut faire les choses à moitié. Soit un compte est débité et l’autre crédité soit l’opération échoue dans sa totalité et les deux comptes ne changent pas de valeur. Le recordset : Cet objet COM/ADO permet d’exploiter très simplement les données. Pour simplifier, il permet de charger un nombre restreint de colonnes et de lignes d’une table pour travailler avec, et enfin sauver les changements. Une ligne dans un recordset s’appelle un record (enregistrement), d’où le nom recordset (ensemble d’enregistrements). Pour être connecté à la base de données, un recordset à besoin d’un objet COM/ADO connexion. Signalons enfin qu’on utilise aussi beaucoup les recordset non connectés pour échanger des données entre objet COM (on ne le verra pas dans ce TP). 8.2 Import de la librairie de type ADO Nous allons utiliser la collection d’objets COM ADO dans CompteServeur. Il faut que ce projet connaisse au moins la syntaxe des noms d’objets utilisés et les méthodes sur ces objets (ne serait ce que pour que le programme puisse compiler). Cette syntaxe est contenue dans la librairie de type (type library) de ADO.On peux comparer une librairie de type ‘.tlh’en COM au fichiers ‘.h’ en C. De même qu’on inclut un fichier ‘.h’ avec la directive préprocesseur ‘#include’ on inclu une librairie de type avec la directive préprocesseur Microsoft ‘#import’. La librairie de type n’est en fait que du code binaire illisible pour un humain. Elle est obtenu après compilation du fichier ‘.idl’. Elle peux être physiquement inclue dans la DLL qui contient les objets COM ou dans un fichier ‘.tlh’. Dans le cas d’ADO la DLL msado15.dll contient à la fois le code de la collection d’objets, et la librairie de type. Il faut donc taper dans le fichier stdafx.h de CompteServeur : . . . #include <atlcom.h> #import "C:\program files\common files\system\ado\msado15.dll" rename("EOF","adoEOF") no_namespace //{{AFX_INSERT_LOCATION}} . . . Notez que msado15.dll n’est pas forcément sur le drive C. Localisez la avant de taper ce code. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 18/36 8.3 Ajout d’une fonction pour ouvrir une connexion avec la DB Les quatre opérations (Ajoutez(), Consultez(), Fermez(), Transferez() ) ont toutes besoin d’accéder à la base de données. Notez qu’à part Consultez() qui n’y accède qu’en lecture, les trois autres opérations accèdent à la base de données en lecture et en écriture. On va mettre le code pour ouvrir une connexion avec notre base de données dans une fonction privée de la classe C++ CCompteObj. Taper dans le fichier CompteObj.h de CompteServeur : . . . END_COM_MAP() private: _Connection * OpenCnx(); // IObjectControl public: . . . Taper à la fin du fichier CompteObj.cpp de CompteServeur : _Connection * CCompteObj::OpenCnx() { // Création d’un pointeur vers un objet COM ADO Connection _ConnectionPtr pCnx(__uuidof( Connection )); // Nom du DSN (qu’on a construit précédemment) // le type bstr_t sert à manipuler le type BSTR (BASIC STRING). // C’est le type de chaîne de caractères utilisé avec les objets COM. // Il supporte notamment UNICODE. // On ne détaillera pas cette partie de COM lors de ce TP. _bstr_t source("TP_DCOM_DSN"); // Login pour la base de données _bstr_t user("sa"); // Password pour la base de données _bstr_t pwd(""); // Création et ouverture de la connexion if (pCnx != NULL) pCnx->Open(source, user, pwd,adConnectUnspecified); // Retour de la connexion // La fonction _ConnectionPtr::Detach() renvoie l’objet de type // _Connection* et invalide l’objet _ConnectionPtr // sur lequel elle est appelée. return pCnx.Detach(); } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 19/36 8.4 Ajout de fonctions pour manipuler des long signés avec ADO Nous allons devoir manipuler des données à l’intérieur de recordsets. Le type de nos données est exclusivement le type ‘long signé’ puisque les ID des comptes sont de ce type et les sommes dans les comptes sont de ce type. On va donc avoir accès à des ‘long signés’ en lecture (par exemple pour consulter une somme) et en écriture (par exemple pour transférer une somme), sur des recordsets. Pour simplifier on code 2 fonctions, une pour l’accès en lecture RS_GetSI4(), une pour l‘accès en écriture RS_SetSI4(). Note : SI4 comme signed integer 4 bytes Taper dans le fichier CompteObj.h de CompteServeur : . . . private: _Connection * OpenCnx(); void RS_GetSI4( _Recordset * pRS , short pos , signed long & ival ); void RS_SetSI4( _Recordset * pRS , short pos , signed long ival ); // IObjectControl public: . . . Taper à la fin du fichier CompteObj.cpp de CompteServeur : void CCompteObj::RS_GetSI4( // un pointer sur le recordset sur lequel on va lire le long // le recordset doit être positionné sur le bon enregistrement _Recordset * pRS , // numéro de la colonne dans le record qui contient le long // 0 based (i.e 0 -> première colonne) short pos , // référence sur le long de retour signed long & ival ) { _variant_t var; VariantInit(&var); FieldsPtr pFields=NULL; FieldPtr pField=NULL; pRS->get_Fields(&pFields); pFields->get_Item(_variant_t(pos),&pField); pField->get_Value(&var); if( var.vt == VT_I4 ) ival = (signed int) var.intVal; } void CCompteObj::RS_SetSI4( // un pointer sur le recordset sur lequel on va écrire le long // le recordset doit être positionné sur le bon enregistrement _Recordset * pRS , // numéro de la colonne dans le record qui contient le long // 0 based (i.e 0 -> première colonne) short pos , // la valeur du long signed long ival ) { _variant_t var; VariantInit(&var); var = _variant_t( (signed long) ival ); FieldsPtr pFields=NULL; FieldPtr pField=NULL; pRS->get_Fields(&pFields); pFields->get_Item(_variant_t(pos),&pField); pField->put_Value(var); } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 20/36 8.5 Le code de la fonction Ajoutez() STDMETHODIMP CCompteObj::Ajoutez( long SommeInitiale, /*[out]*/ long * pNewCompteID ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) bool bObjectContextInitialized = false; CComPtr<IObjectContext> spObjectContext; try { // Initialisation du paramètre de retour if( pNewCompteID == NULL )throw _com_error(E_FAIL); *pNewCompteID = 0; // N'autorise pas de somme initiale négative if( SommeInitiale < 0 ) throw _com_error(E_FAIL); // Notre ObjetContext, utilisé pour la transaction GetObjectContext(&spObjectContext); if( spObjectContext == NULL ) throw _com_error(E_FAIL); bObjectContextInitialized = true; // ouverture de la connexion _ConnectionPtr pCnx = OpenCnx(); // ouverture d'un recordset sur toute la table COMPTES (adCmdTable) // en read/write (adLockBatchOptimistic) _RecordsetPtr pRS = NULL; pRS.CreateInstance(__uuidof(Recordset)); pRS->put_CursorLocation(adUseClient); pRS->Open( _variant_t( _bstr_t( (char*) "COMPTES" ) ), _variant_t( (_Connection*) pCnx ), adOpenStatic, adLockBatchOptimistic, adCmdTable); // Ajoute un enregistrement à la table COMPTES // Après AddNew() l’enregistrement du recordset courant est le nouveau pRS->AddNew(); // Ecriture de la somme dans le nouvel enregistrement // 1 est la position de la colonne Somme dans la table COMPTES RS_SetSI4( pRS , 1 , SommeInitiale ); // Sauve les changements sur l'enregistrement courant pRS->UpdateBatch(adAffectCurrent); // Obtient le nouveau compteID pRS->put_Filter( _variant_t( (long) adFilterAffectedRecords) ); // 0 est la position de la colonne ID dans la table COMPTES RS_GetSI4( pRS , 0 , *pNewCompteID ); pRS->put_Filter( _variant_t( (long) adFilterNone) ); // Ferme le recordset pRS->Close(); // Ferme la connexion pCnx->Close(); } catch(...) { if( bObjectContextInitialized && spObjectContext != NULL ) spObjectContext->SetAbort(); return E_FAIL; } spObjectContext->SetComplete(); return S_OK; } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 21/36 8.6 Le code de la fonction Fermez() STDMETHODIMP CCompteObj::Fermez(long CompteID) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) bool bObjectContextInitialized = false; CComPtr<IObjectContext> spObjectContext; try { // Notre ObjetContext, utilisé pour la transaction GetObjectContext(&spObjectContext); if( spObjectContext == NULL ) throw _com_error(E_FAIL); bObjectContextInitialized = true; // ouverture de la connexion _ConnectionPtr pCnx = OpenCnx(); // ouverture d'un recordset // en read/write (adLockBatchOptimistic) // et à partir d'une requête SQL (adCmdText) CString sSelect; sSelect.Format("SELECT * FROM COMPTES WHERE ID = %d ",CompteID ); _RecordsetPtr pRS = NULL; pRS.CreateInstance(__uuidof(Recordset)); pRS->put_CursorLocation(adUseClient); pRS->Open( _variant_t( _bstr_t( (const char*) sSelect) ), _variant_t((_Connection*) pCnx ), adOpenStatic, adLockBatchOptimistic, adCmdText); // Si pas de compte trouvé avec cet ID // alors la fonction MoveFirst() va lancer une exception // car le recordset est vide!! pRS->MoveFirst(); // delete la ligne courante pRS->Delete(adAffectCurrent); // Sauve les changements sur l'enregistrement courant pRS->UpdateBatch(adAffectCurrent); // Ferme le recordset pRS->Close(); // Ferme la connexion pCnx->Close(); } catch(...) { if( bObjectContextInitialized && spObjectContext != NULL ) spObjectContext->SetAbort(); return E_FAIL; } spObjectContext->SetComplete(); return S_OK; } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 22/36 8.7 Le code de la fonction Consultez() STDMETHODIMP CCompteObj::Consultez(long CompteID, long *pSomme) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) bool bObjectContextInitialized = false; CComPtr<IObjectContext> spObjectContext; try { // Initialisation du paramètre de retour if( pSomme == NULL )throw _com_error(E_FAIL); *pSomme = 0; // Notre ObjetContext, utilisé pour la transaction GetObjectContext(&spObjectContext); if( spObjectContext == NULL ) throw _com_error(E_FAIL); bObjectContextInitialized = true; // ouverture de la connexion _ConnectionPtr pCnx = OpenCnx(); // ouverture d'un recordset // en read only (adLockReadOnly) // et à partir d'une requête SQL (adCmdText) CString sSelect; sSelect.Format("SELECT Somme FROM COMPTES WHERE ID = %d ",CompteID ); _RecordsetPtr pRS = NULL; pRS.CreateInstance(__uuidof(Recordset)); pRS->put_CursorLocation(adUseClient); pRS->Open( _variant_t( _bstr_t( (const char*) sSelect ) ), _variant_t((_Connection*) pCnx ), adOpenStatic, adLockReadOnly, adCmdText); // Si pas de compte trouvé avec cet ID // alors la fonction MoveFirst() va lancer une exception // car le recordset est vide!! pRS->MoveFirst(); // 0 est la position de la colonne Somme ici // comme on a sélectionné qu'une colonne RS_GetSI4( pRS , 0 , *pSomme ); // Ferme le recordset pRS->Close(); // Ferme la connexion pCnx->Close(); } catch(...) { if( bObjectContextInitialized && spObjectContext != NULL ) spObjectContext->SetAbort(); return E_FAIL; } spObjectContext->SetComplete(); return S_OK; } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 23/36 8.8 Le code de la fonction Transfererez() STDMETHODIMP CCompteObj::Transferez( long CompteIDSrc , long CompteIDDest , long Somme ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // pas besoin de transferer sur le meme compte if( CompteIDSrc == CompteIDDest ) return S_OK; bool bObjectContextInitialized = false; CComPtr<IObjectContext> spObjectContext; try { // Notre ObjetContext, utilisé pour la transaction GetObjectContext(&spObjectContext); if( spObjectContext == NULL ) throw _com_error(E_FAIL); bObjectContextInitialized = true; // ouverture de la connexion _ConnectionPtr pCnx = OpenCnx(); // ouverture d'un recordset // en read/write (adLockBatchOptimistic) // et à partir d'une requête SQL (adCmdText) CString sSelect; sSelect.Format("SELECT * FROM COMPTES WHERE ID = %d OR ID =%d" ,CompteIDSrc,CompteIDDest ); _RecordsetPtr pRS = NULL; pRS.CreateInstance(__uuidof(Recordset)); pRS->put_CursorLocation(adUseClient); pRS->Open( _variant_t( _bstr_t( (const char*) sSelect ) ), _variant_t((_Connection*) pCnx ), adOpenStatic, adLockBatchOptimistic, adCmdText); //////////////////////////////////// // 1ere partie de la transaction // Débite le compte source de somme { // ne filtre que le compte source CString sFilter; sFilter.Format("ID = %d",CompteIDSrc); pRS->put_Filter( _variant_t( _bstr_t( (const char*)sFilter) )); // Si pas de compte trouvé avec ID source // alors la fonction MoveFirst va lancer une exception // car le recordset est vide!! pRS->MoveFirst(); // prend la somme courante long SommeCourante = 0; RS_GetSI4( pRS , 1 , SommeCourante ); // décrémente la somme courante // ne l'autorise pas à être négative SommeCourante -= Somme; if( SommeCourante < 0 ) throw _com_error(E_FAIL); // sauve la somme courante dans l'enregistrement RS_SetSI4( pRS , 1 , SommeCourante ); // Sauve les changements sur l'enregistrement courant pRS->UpdateBatch(adAffectCurrent); // enlève le filtre pRS->put_Filter(_variant_t( _bstr_t( "" ) ) ); } . . . Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 24/36 . . . //////////////////////////////////// // 2eme partie de la transaction // Crédite le compte destination de somme { // ne filtre que le compte destination CString sFilter; sFilter.Format("ID = %d",CompteIDDest); pRS->put_Filter( _variant_t( _bstr_t( (const char*)sFilter) )); // Si pas de compte trouvé avec ID destination // alors la fonction MoveFirst va lancer une exception // car le recordset est vide!! pRS->MoveFirst(); // prend la somme courante long SommeCourante = 0; RS_GetSI4( pRS , 1 , SommeCourante ); // incrémente la somme courante SommeCourante += Somme; // sauve la somme courante dans l'enregistrement RS_SetSI4( pRS , 1 , SommeCourante ); // Sauve les changements sur l'enregistrement courant pRS->UpdateBatch(adAffectCurrent); // enlève le filtre pRS->put_Filter(_variant_t( _bstr_t( "" ) ) ); } // Ferme le recordset pRS->Close(); // Ferme la connexion pCnx->Close(); } catch(...) { if( bObjectContextInitialized && spObjectContext != NULL ) spObjectContext->SetAbort(); return E_FAIL; } spObjectContext->SetComplete(); return S_OK; } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 25/36 9 Remplissage des fonctions de l’interface utilisateur 9.1 Appel a CoInitialize() Quand on fait un client d’objet COM en C+, il est indispensable d’initialiser COM dans chaque thread susceptible d’appeler un objet COM. Il suffit juste d’appeler la fonction CoInitialize() au début du thread. Dans le CompteManagerDlg.cpp ajouter dans la fonction BOOL CcompteManagerDlg ::OnInitDialog() : . . . // TODO: Add extra initialization here CoInitialize(NULL); return TRUE; // return TRUE unless you set the focus to a control . . . Note: Microsoft préconise l’appel à CoUnInitialize() dans chaque thread avant de quitter. Pour simplifier nous ne le feront pas dans ce TP. 9.2 Import de la librairie de type de CompteServeur De même que l’on à utilisé la directive préprocesseur Microsoft ‘#import’ pour pouvoir utiliser la collection d’objet COM ADO dans le serveur (i.e inclure la librairie de type de ADO), il faut utiliser cette même directive pour pouvoir utiliser notre objet COM serveur dans l’interface utilisateur (i.e inclure la librairie de type de CompteServeur). Au préalable il faut compiler CompteServeur pour fabriquer la librairie de type de CompteServeur (dans le fichier CompteServeur.tlh). Dans le fichier stdafx.h de CompteManager ajouter : . . . #endif // _AFX_NO_AFXCMN_SUPPORT #import "..\CompteServeur\CompteServeur.tlb" no_namespace raw_interfaces_only //{{AFX_INSERT_LOCATION}} . . . Note : Si vous n’avez pas respecté les répertoires pour les projets il faut placer le bon répertoire. 9.3 Macro pour les ClassID Pour créer un objet COM de type CompteObj on va devoir avoir accès à son ClassID (identificateur unique de classe CLSID). Ces IDs de 128 bits sont difficiles à manipuler dans du code du fait de leur taille. Pour palier ce problème, des macros sont définies pour ces Ids. Ces macros se trouvent dans le fichier CompteServeur_i.c, généré lors de la compilation de CompteServeur. Dans le fichier CompteManager.h de CompteManager ajouter : . . . #endif #include "..\CompteServeur\CompteServeur_i.c" ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About . . . Note : Si vous n’avez pas respecté les répertoires pour les projets il faut placer le bon répertoire. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 26/36 9.4 Le code de la fonction OnButtonAjoute() : void CCompteManagerDlg::OnButtonAjoute() { UpdateData(TRUE); ICompteObjPtr pI; // Création d'un objet CompteObj (en local) HRESULT hr = pI.CreateInstance( CLSID_CompteObj ); if( SUCCEEDED( hr ) ) { long NewID = 0; hr = pI->Ajoutez( m_SommeInitiale , &NewID ); if( SUCCEEDED( hr ) ) m_sResult.Format("Identificateur du nouveau compte %d",NewID); else m_sResult = CString("Echec de l'appel à CompteObj::Ajoutez()"); } else m_sResult = CString("Echec dans la création de l'objet CompteObj !!"); UpdateData(FALSE); } 9.5 Le code de la fonction OnButtonFerme() : void CCompteManagerDlg::OnButtonFerme() { UpdateData(TRUE); ICompteObjPtr pI; // Création d'un objet CompteObj (en local) HRESULT hr = pI.CreateInstance( CLSID_CompteObj ); if( SUCCEEDED( hr ) ) { long NewID = 0; hr = pI->Fermez( m_CompteIDFermez ); if( SUCCEEDED( hr ) ) m_sResult.Format("Compte %d fermé.",m_CompteIDFermez); else m_sResult = CString("Echec de l'appel à CompteObj::Fermez()"); } else m_sResult = CString("Echec dans la création de l'objet CompteObj !!"); UpdateData(FALSE); } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 27/36 9.6 Le code de la fonction OnButtonConsulte() : void CCompteManagerDlg::OnButtonConsulte() { UpdateData(TRUE); ICompteObjPtr pI; // Création d'un objet CompteObj (en local) HRESULT hr = pI.CreateInstance( CLSID_CompteObj ); if( SUCCEEDED( hr ) ) { long Somme = 0; hr = pI->Consultez( m_CompteIDConsultez , &Somme ); if( SUCCEEDED( hr ) ) m_sResult.Format("Le compte %d à la somme %d", m_CompteIDConsultez, Somme); else m_sResult = CString("Echec de l'appel à CompteObj::Consultez()"); } else m_sResult = CString("Echec dans la création de l'objet CompteObj !!"); UpdateData(FALSE); } 9.7 Le code de la fonction OnButtonTransfere() void CCompteManagerDlg::OnButtonTransfere() { UpdateData(TRUE); ICompteObjPtr pI; // Création d'un objet CompteObj (en local) HRESULT hr = pI.CreateInstance( CLSID_CompteObj ); if( SUCCEEDED( hr ) ) { long NewID = 0; hr = pI->Transferez( m_CompteIDSrc , m_CompteIDDest, m_SommeTransfert); if( SUCCEEDED( hr ) ) m_sResult.Format("Transfert de la somme %d du compte %d au compte %d", m_SommeTransfert, m_CompteIDSrc, m_CompteIDDest); else m_sResult = CString("Echec de l'appel à CompteObj::Transferez()"); } else m_sResult = CString("Echec dans la création de l'objet CompteObj !!"); UpdateData(FALSE); } Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 28/36 10 Faire de CompteServeur une COM+ Application transactionnelle 10.1 Définitions d’un snap-in Microsoft a décidé dans Windows NT d’unifier le management des composants soft. Pour cela a été crée le MMC (Microsoft Management Console). Le MMC est un exécutable qui peut charger des snap-in. Un snap-in est une collection d’objet COM qui permette de gérer graphiquement des composants logiciels. Une Application COM+ est une collection d’objets COM qui ensemble, vont être un serveur transactionnel. Il existe un snap-in dédié a la gestion des Application COM+. C’est le snap-in Components services. 10.2 Ouverture du snap-in Components Services Aller dans MenuDémarer/Run tapez mmc. Faire Ctrl M (Add/Remove SnapIn) Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 29/36 Cliquer le bouton Add Sélectionner Component Services et cliquer le bouton Add puis le bouton Close puis le bouton OK Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 30/36 10.3 Ajout d’une nouvelle application COM+ Cliquer droit sur le répertoire COM+Applications New Application Cliquer Next Cliquer Create an empty application Appelons notre nouvelle COM+ Application CompteApplication. C’est une serveur application c’est à dire que nos objets COM transactionnels sont dans une DLL, qui va être chargée dans un process surrogate (subrogé en français). Un processs surrogate est un process spécial pour charger une DLL avec des objets COM transactionnels. Ce process s’occupe de tout (i.e charger la DLL, fabriquer les transactions, commit ou rollback sur les transactions…). Sur Windows 2000 ce process s’appelle dllhost.exe. Cliquer Next Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 31/36 Cliquer le bouton Next puis le bouton finish Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 32/36 10.4 Ajout de l’objet COM CompteObj dans notre COM+Application Cliquer droit sur le répertoire Components New Component Cliquer Next Cliquer Install new components Browser jusqu’au répertoire Debug ou il y a CompteServeur.dll Click Open Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 33/36 Cliquer le bouton Next puis le bouton finish Le composant (i.e objet COM) CompteObj est installé sur votre machine. Vous pouvez lancer le client et le tester. 10.5 Vérification du mode transactionnel supporté Cliquer droit sur le composants CompteServeur Properties Onglet Transactions Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 34/36 11 Exécution du TP Si toutes les étapes du TP ont été respectées, vous pouvez lancer l’interface et tester les 4 opérations. Malheureusement l’expérience montre qu’un projet ne marche rarement du premier coup. 11.1 Déboguer un objet COM+ La meilleure solution consiste à faire tourner l’objet en mode debug. Dans le snap in component services : Cliquez droit sur CompteApplication. Stop. Ceci aura pour effet de stopper l’exécution de l’objet COM+. On peut ainsi modifier ses paramètres d’exécution. Aller sur les propriétés de CompteApplication. Dans le menu advanced mettre : o ‘leave running when idle’. Le même objet COM+ pourra ainsi être utilisé pour plusieurs débogage. o ‘Open in a debugger’. Le process subrogés DllHost.exe sera ainsi ouvert en mode debogage. o Cliquez OK. Lancez l’interface. Exécuté une opération. Si l’objet COM+ est exécuté une instance de visual C++ est automatiquement lancée. Si l’instance ne se lance pas, l’objet COM+ n’est pas bien enregistrés sur la machine. L’avez vous bien compilé ? L’instance de visual C++ lancée attend que vous pressez F5 (run in mode debug) pour exécuter dllhost.exe. Presser F5. A ce stade vous n’avez pas posé de breakpoint dans le code de l’objet COM+. Après avoir pressé F5 le code ne va donc pas s’arrêter et la méthode appelée s’exécutera une fois. Après cette exécution vous pouvez ouvrir votre fichier source (.cpp) et poser vos breakpoint (F9). Les breakpoint ne pouvez être posé avant d’avoir pressé F5. En effet, à cet instant la dll n’était pas encore chargée dans le process, le debogger ne pouvait donc pas utiliser vos fichiers sources. Il peut arriver que vous souhaiter debogger votre méthode dés sa première exécution. Dans ce cas ajouter la ligne suivante au début de votre méthode : _asm int 3 Stoppez l’objet COM puis recompilez le. Cette ligne est un breakpoint. Le debugger s’y arrêtera dés sa première rencontre. ATTENTION : Le code compilé ne pourra pas s’exécuter en mode non débogué. Il faut impérativement recompiler la dll en enlevant ‘_asm int 3’. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 35/36 12 Bibliographie Une introduction intéressante est dans : Wnd200 Systems programming BlackBox Al Williams Coriolis ISBN 1-57610-280-7 Livre intéressant pour utiliser Windows 2000 (XP), pas seulement pour l’introduction à COM Pour ceux qui veulent approfondir : Inside Distributed COM Guy Eddon ; Henry Eddon Microsoft Press ISBN ??? Présente tous les aspects de DCOM sans allez dans tous les détails. Pour ceux qui veulent se spécialiser dans le 3-tiers sous Microsoft : DCOM Programming Professional Richard Grimes WROX ISBN 1-861000-60-X Indispensable pour comprendre l’architecture de DCOM DCOM Visual C++6 MTS Programming Richard Grimes WROX ISBN 1-861002-39-X Indispensable pour travailler dans un milieu transactionnel sous VC++. Transactional COM+ Building Scalable Applications Tim Ewald ADDISON-WESLEY ISBN 0-201-61594-0 Indispensable pour comprendre la théorie des serveurs transactionnelle et l’utilisez au mieux en COM+ pour faire des serveur efficaces. Effective COM: 50 Ways to improve your COM and MTS-based Applications Don Box ; Keith Brown ; Tim Ewald ; Chris Sells ADDISON-WESLEY ISBN 0-201-37968-6 Indispensable pour éviter les erreurs fréquente dans l’utilisation de COM/COM+. SAMS teach yourself database programming with VC++ Lyn Robison SAMS ISBN 0 -672-31350-2 Présente une vue complète des technologie à utiliser dans la programmation 3-tiers sous Microsoft. Une architecture distribuée 3-tiers transactionnelle sous COM+ Patrick Smacchia 2002 36/36