Technique : Travailler avec les Interfaces en C++Builder Partie 1

Ce tutorial démontre l'utilisation des Interfaces en C++ Builder avec un exemple d'application.

Sources du Programme disponible ici.

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Définition

Le Pascal Objet n'est pas conçu pour profiter de l'héritage multiple. Pour pallier ce manque, Borland a implémenté à partir de la version 4 de la VCL l'utilisation des interfaces basées sur COM.

I-A. Interface IUnknow

Définition tirée de l'aide Borland

IUnknown est considérée comme l'interface de base pour toutes les autres interfaces, y compris les interfaces définies en Pascal Objet.

IUnknown présente la méthode QueryInterface, qui est utile pour découvrir et utiliser d'autres interfaces implémentées par le même objet.

IUnknown introduit également les méthodes de comptage de références AddRef et Release. Un code qui obtient un pointeur d'interface doit appeler AddRef dès qu''il acquiert le pointeur, et appeler Release lorsque'il en a terminé avec lui.

I-B. IInterface

IInterface est l'interface de base pour les interfaces définies en code Pascal Objet.

IInterface est considérée comme une interface fille de IUnknown, et IUnknown est considérée comme l'interface de base pour toutes les interfaces.

I-C. DelphiInterface

DelphiInterface est l'implémentation C++Builder du type intrinsèque Pascal Objet interface.

II. Définition d'une Interface

Une interface en C++ Builder est une classe abstraite.

Elle contient :

  • uniquement une partie public
  • des méthodes virtuelles pures
  • des property
  • des Events

Elle ne contient pas :

  • de données membres

II-A. Utilisation d'une interface

Appellation :

On précède le nom d'une interface avec la lettre I

Ex. : IDesigner Interface Idesigner

Déclaration:

Une interface se déclare avec la macro __interface

Composition:

Une interface se compose d'un GUID indispensable pour rechercher cette interface simplement. Ce GUID peut être généré automatiquement avec c++ Builder avec la combinaison de touche <CTRL+Shift+G>

La chaîne générée est au format Pascal.

  • ['{B208D697-3B41-42A2-9129-82F30184BADB}']

Pour la rendre compatible avec C++ Builder il faut la modifier de la manière suivante :

  • ("{B208D697-3B41-42A2-9129-82F30184BADB}")

Ce GUID s'utilise avec la macro INTERFACE_UUID.

Une interface descend toujours de la classe IInterface.

Pour les méthodes relatives à la classe IInterface veuillez consulter l'aide de Borland.

Une méthode intéressante s'appelle Supports.

Elle permet de retourner une autre DelphiInterface prise en charge à partir de la classe d'implémentation.

Nous verrons plus tard son utilisation.

Pour obtenir des facilités d'utilisation des interfaces proches de celle de Delphi, Borland met a disposition un Template :

  • <class T> class RTL_DELPHIRETURN DelphiInterface;

II-B. Déclaration d'une Interface

La déclaration d'une interface se fait toujours dans le Header(.h) et nécessite l'inclusion du fichier system.hpp

 
Sélectionnez
1.
2.
3.
4.
5.
6.
__interface  INTERFACE_UUID ("{B208D697-3B41-42A2-9129-82F30184BADB}") IMapremiereInterface public :  IInterface 
{
   public:
      virtual void __stdcall DitBonjour(void) = 0 ;// Méthode virtuelle pure
};
typedef System::DelphiInterface< IMapremiereInterface >  _di_IMapremiereInterface; // DelphiInterface

III. La Classe d'implémentation

Utilité : définir dans cette classe l'implémentation de toutes les méthodes exposées dans l'interface.

III-A. Classe TInterfacedObject

TInterfacedObject implémente l'interface IInterface (IUnknown) et peut être utilisée comme base pour des classes compatibles COM simples bénéficiant de l'implémentation des méthodes Iinterface.

Différence avec Delphi:

  • En Delphi, IInterface et IUnknow sont identiques. En C++ elles diffèrent dans la signature des méthodes : IInterface et IUnknow sont différentes.

La conséquence est que la classe TInterfaceObject n'implémente pas les méthodes suivantes :

 
Sélectionnez
1.
2.
3.
virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
virtual ULONG __stdcall AddRef()
virtual ULONG __stdcallRelease();

Il est du devoir du programmeur d'implémenter ces 3 méthodes.

III-B. Définition d'une classe d'implémentation

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
class PremiereInterfaceImpl : public TInterfacedObject ,public IMapremiereInterface
{
   public:
      virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
      {
         return TInterfacedObject::QueryInterface(IID, (void *)Obj);
      }
      virtual ULONG __stdcall AddRef()
      {
         return TInterfacedObject::_AddRef();
      }
      virtual ULONG __stdcall Release()
      {
         return TInterfacedObject::_Release();
      }
   public:
      virtual void __stdcall DitBonjour(void);
};

Point important:

  • La classe TInterfaceObject dispose d'un compteur de références.
  • Dés que ce compteur est à 0, le destructeur de la classe d'implémentation est automatiquement appelé.
  • La gestion mémoire est ainsi facilitée.

IV. Une Application Exemple

Image non disponible

Nous allons programmer sur la base des interfaces une petite application basique stimulant des outils de bureau

  • Calculatrice basique
  • Pense bête

Notre calculatrice doit pouvoir :

  • Additionner 2 nombres
  • Soustraire 2 Nombres
  • Multiplier 2 Nombres
  • Diviser 2 Nombres
  • Etablir la racine Carrée d'un nombre
  • Etablir le carré d'un nombre

Notre pense-bête doit pouvoir :

  • Mémoriser une note
  • Lire une note mémorisée
  • Donner la date et l'heure

IV-A. Marche à suivre

Nous allons déclarer 2 interfaces :

  • Calculatrice ICalc
  • Pense Bête IAgenda

IV-B. Interface Icalc

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
__interface INTERFACE_UUID("{9EB5933E-7556-46FD-A3EB-85163E462809}") ICalc :  public IInterface
{
   public:
   virtual int    __stdcall   Addition(int a,int b)=0;
   virtual int    __stdcall   Soustraction(int a,int b)=0;
   virtual float  __stdcall Division(int a,int b)=0;
   virtual int __stdcall Multiplication(int a, int b)=0;
   virtual float  __stdcall Racine(int a)=0;
   virtual long  __stdcall Carre(int a)=0;
};
typedef System::DelphiInterface <ICalc>  _di_Calc;

Toutes les fonctions désirées de notre calculatrice sont exposées en tant que méthodes virtuelles pures.

IV-C. Interface Iagenda

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
__interface INTERFACE_UUID("{0243615E-7298-4CB4-A567-CAE70CEA1DB6}") IAgenda :  public IInterface
{
   public:
   virtual TDateTime  __stdcall   GetDate()=0;
   virtual void  __stdcall   SetDate(TDateTime Value)=0;
   virtual AnsiString  __stdcall   GetNote()=0;
   virtual void  __stdcall   SetNote(AnsiString Value)=0;
   __property TDateTime  _Date={read = GetDate,write = SetDate};
   __property AnsiString  _Note={read =GetNote,write = SetNote};
};
typedef System::DelphiInterface<IAgenda>  _di_IAgenda;

Il est à noter l'utilisation de property.

IV-D. La Classe d'implémentation

Déclaration

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
class CalcImplementation : public TInterfacedObject ,public ICalc ,public IAgenda
{
   public:
   // implementation de ICalc
   virtual int __stdcall  Addition(int a, int b);
   virtual int __stdcall  Soustraction(int a, int b);
   virtual float __stdcall Division(int a, int b);
   virtual int __stdcall Multiplication(int a, int b);
   virtual float __stdcall Racine(int a);
   virtual long __stdcall Carre(int a);
   // implementation de IAgenda
   public:  // utilisé pour clarifié la déclaration de l'interface IAgenda
   virtual TDateTime __stdcall GetDate();
   virtual void __stdcall SetDate(TDateTime Value);
   virtual AnsiString __stdcall GetNote();
   virtual void __stdcall SetNote(AnsiString Value);
   // implementation de TInterfacedObject
   public:
   virtual HRESULT __stdcall QueryInterface(const GUID& IID, void ** Obj);
   virtual ULONG __stdcall AddRef();
   virtual ULONG __stdcall Release();
   private:
   TDateTime Fdate;
   AnsiString Fnote;
};

J'ai séparé dans un but de clarté l'implémentation de chaque interface.

Il est a noter que cette classe dispose de 2 membres privés permettant de stocker les valeurs Date Heure et Note.

Cette classe dispose d'un constructeur pour initialiser les données membres.

Implémentation

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
__stdcall CalcImplementation::CalcImplementation()
   : Fdate(NULL),
   Fnote("")
   {
   }
   //---------------------------------------------------------------------------
 
   int __stdcall CalcImplementation::Addition(int a, int b)
   {
      return a+b;
   }
   //---------------------------------------------------------------------------
   int __stdcall CalcImplementation::Soustraction(int a, int b)
   {
      return a-b;
   }
   //---------------------------------------------------------------------------
   float __stdcall CalcImplementation::Division(int a, int b)
   {
      return static_cast<float>(a)/static_cast<float>(b);
   }
   //---------------------------------------------------------------------------
   int __stdcall CalcImplementation::Multiplication(int a, int b)
   {
      return a*b;
   }
   //---------------------------------------------------------------------------
   float __stdcall CalcImplementation::Racine(int a)
   {
      return sqrt(a);
   }
   //---------------------------------------------------------------------------
   long __stdcall CalcImplementation::Carre(int a)
   {
      return a*a;
   }
   //---------------------------------------------------------------------------
 
   HRESULT __stdcall CalcImplementation::QueryInterface(const GUID& IID, void ** Obj)
   {
      return TInterfacedObject::QueryInterface(IID, (void *)Obj);
   }
   //---------------------------------------------------------------------------
   ULONG __stdcall CalcImplementation::AddRef()
   {
      return TInterfacedObject::_AddRef();
   }
 
   //---------------------------------------------------------------------------
 
   ULONG __stdcall CalcImplementation::Release()
   {
      return TInterfacedObject::_Release();
   }
   //---------------------------------------------------------------------------
   TDateTime __stdcall CalcImplementation::GetDate()
   {
      if (static_cast<int>(Fdate)) return Fdate;
      else
         return Now();
   }
   //---------------------------------------------------------------------------
   void __stdcall CalcImplementation::SetDate(TDateTime Value)
   {
      Fdate=Value;
   }
   //---------------------------------------------------------------------------
   AnsiString __stdcall CalcImplementation::GetNote()
   {
      return Fnote;
   }
   //---------------------------------------------------------------------------
   void __stdcall CalcImplementation::SetNote(AnsiString Value)
   {
      Fnote=Value;
   }
   //---------------------------------------------------------------------------

V. L'application

Déclaration

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
class TForm1 : public TForm
{
   __published: // Composants gérés par l'EDI
      TPanel *Panel1;
      TLabeledEdit *lbledtB;
      TStatusBar *Stat1;
      TGroupBox *grp1;
      TLabeledEdit *lbledtResult;
      TLabeledEdit *lbledtA;
      TPanel *pnl1;
      TButton *btnbtminus;
      TButton *btnPlus;
      TButton *btnX;
      TButton *btndiv;
      TButton *btnRacine;
      TButton *btnCarre;
      TGroupBox *grp2;
      TLabel *lbl2;
      TEdit *edtReadNote;
      TButton *btnLireNote;
      TLabel *lbl3;
      TLabel *lbl4;
      TEdit *edtReadHeure;
      TButton *btnReadHeure;
      TEdit *edtWriteNote;
      void __fastcall btnbtminusClick(TObject *Sender);
      void __fastcall btnCarreClick(TObject *Sender);
      void __fastcall btndivClick(TObject *Sender);
      void __fastcall btnPlusClick(TObject *Sender);
      void __fastcall btnRacineClick(TObject *Sender);
      void __fastcall btnXClick(TObject *Sender);
      void __fastcall btnLireNoteClick(TObject *Sender);
      void __fastcall btnReadHeureClick(TObject *Sender);
      void __fastcall btnWriteNoteClick(TObject *Sender);
   private:     // Déclarations de l'utilisateur
      _di_ICalc mCalc;
      _di_IAgenda mAgenda;
      bool Fagenda;
   public:
      __fastcall TForm1(TComponent* Owner);
};

On notera comme membres privés les 2 Delphi interfaces :

  • _di_ICalc mCalc;
  • _di_IAgenda mAgenda;

ainsi qu'une variable bool.

Implémentation

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
   mCalc = new CalcImplementation();    // instanciation de l'interface
   Fagenda= mCalc->Supports(mAgenda);   // instanciation et test si interface existante
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnbtminusClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Soustraction(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
      Stat1->SimpleText=E.Message;
   }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCarreClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Carre(StrToInt(lbledtA->Text));
   }
   catch(Exception &E)
   {
      Stat1->SimpleText=E.Message;
   }
}
//--------------------------------------------------------------------------- 
void __fastcall TForm1::btndivClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Division(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
      Stat1->SimpleText=E.Message;
   }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnPlusClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Addition(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
      Stat1->SimpleText=E.Message;
   }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnRacineClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Racine(StrToInt(lbledtA->Text));
   }
   catch(Exception &E)
   {
   Stat1->SimpleText=E.Message;
   }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnXClick(TObject *Sender)
{
   try
   {
      lbledtResult->Text=mCalc->Multiplication(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));
   }
   catch(Exception &E)
   {
      Stat1->SimpleText=E.Message;
   }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnLireNoteClick(TObject *Sender)
{
   if(Fagenda)
      edtReadNote->Text=mAgenda->_Note;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnReadHeureClick(TObject *Sender)
{
   if(Fagenda)
      edtReadHeure->Text=TimeToStr(mAgenda->_Date);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnWriteNoteClick(TObject *Sender)
{
   if(Fagenda)
      mAgenda->_Note=edtWriteNote->Text;
}
//---------------------------------------------------------------------------

Dans le constructeur de la Form on instancie une des interfaces en appelant le constructeur de la classe d'implémentation.

L'interface ICalc est instanciée de la manière suivante :

 
Sélectionnez
1.
mCalc = new CalcImplementation();    // instanciation de l'interface

A partir de cette Delphinterface on peut retrouver toutes les interfaces implémentés par la classe d'implémentation :

 
Sélectionnez
1.
Fagenda= mCalc->Supports(mAgenda);   // instanciation et test si interface existante

Si l'interface passée en paramètre de la méthode Supports est bien implémentée, elle est instanciée et la valeur 0 est retournée.

Pour travailler ensuite avec ces delphiInterface il suffit d'appeler directement les méthodes implémentées.

Ex. :

 
Sélectionnez
1.
lbledtResult->Text = mCalc->Soustraction(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));

La classe d'implémentation des DelphiInterfaces est automatiquement libérée à la fin de l'application.

J'ai joint avec le source un fichier d'aide réalisé avec Doxygen à la fin de l'application

VI. Conclusion

Dans la partie deux d'utilisation des interfaces (en préparation) je vous propose la création de Plugin basée sur un tutorial Delphi de sjrd.

Voir également mes autres articles :

  • Utilisation des Files Mapping sous C++Builder C++ Builder
  • La technique des PatchFiles C++ Builder
  • Maîtrisez les files d'impression sous windows C++ Builder
  • Travailler avec les Interfaces en C++Builder Partie 1 C++ Builder
  • Composant de gestion des ports d'imprimantes C++ Builder
  • Programme de Test Version ActiveX du composant TDLIoport Delphi

Sans oublier :

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Voir également mes autres articles :
Utilisation des Files Mapping sous C++Builder C++ Builder
La technique des PatchFiles C++ Builder
Maîtrisez les files d'impression sous windows C++ Builder
Composant de gestion des ports d'imprimantes C++ Builder
Programme de Test Version ActiveX du composant TDLIoport Delphi
Sans oublier :
la FAQ C++ Builder par Geronimo
la FAQ C
la FAQ C++

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 DjmSoftware. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.