6 Création du squelette de l`interface utilisateur

publicité
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
Téléchargement