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

DjmSoftware

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

Sources du Programme disponible ici. ♪

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

- Auteur : DjmSoftware

- Parution : 13 mars 2006

- Public visé :

- Licence : 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.

Sommaire

- I. Définition

- I-A. Interface IUnknow

- I-B. IInterface

- I-C. DelphiInterface

- II. Définition d'une Interface

- II-A. Utilisation d'une interface

- II-B. Déclaration d'une Interface

- III. La classe d'implémentation

- III-A. Classe TInterfacedObject

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

- IV. Une application exemple

- IV-A. Marche à suivre

- IV-B. Interface Icalc

- IV-C. Interface Iagenda

- IV-D. La Classe d'implémentation

- V. L'application

- VI. Conclusion

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 lorsqu'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 celles de Delphi, Borland met à 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

1. __interface  INTERFACE_UUID ("{B208D697-3B41-42A2-9129-82F30184BADB}") IMapremiereInterface public :  IInterface

2. {

3. public:

4. virtual void __stdcall DitBonjour(void) = 0 ;// Méthode virtuelle pure

5. };

6. 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 :

1. virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)

2. virtual ULONG __stdcall AddRef()

3. virtual ULONG __stdcallRelease();

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

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

1. class PremiereInterfaceImpl : public TInterfacedObject ,public IMapremiereInterface

2. {

3. public:

4. virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)

5. {

6. return TInterfacedObject::QueryInterface(IID, (void *)Obj);

7. }

8. virtual ULONG __stdcall AddRef()

9. {

10. return TInterfacedObject::_AddRef();

11. }

12. virtual ULONG __stdcall Release()

13. {

14. return TInterfacedObject::_Release();

15. }

16. public:

17. virtual void __stdcall DitBonjour(void);

18. };

Points importants

- 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

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

- Calculatrice basique ;

- Pense-bête.

Notre calculatrice doit pouvoir :

- additionner deux nombres ;

- soustraire deux nombres ;

- multiplier deux nombres ;

- diviser deux nombres ;

- établir la racine carrée d'un nombre ;

- établir 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 deux interfaces :

- Calculatrice ICalc ;

- Pense-Bête Iagenda.

IV-B - Interface Icalc

1. __interface INTERFACE_UUID("{9EB5933E-7556-46FD-A3EB-85163E462809}") ICalc :  public IInterface

2. {

3. public:

4. virtual int    __stdcall   Addition(int a,int b)=0;

5. virtual int    __stdcall   Soustraction(int a,int b)=0;

6. virtual float  __stdcall Division(int a,int b)=0;

7. virtual int __stdcall Multiplication(int a, int b)=0;

8. virtual float  __stdcall Racine(int a)=0;

9. virtual long  __stdcall Carre(int a)=0;

10. };

11. 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

1. __interface INTERFACE_UUID("{0243615E-7298-4CB4-A567-CAE70CEA1DB6}") IAgenda :  public IInterface

2. {

3. public:

4. virtual TDateTime  __stdcall   GetDate()=0;

5. virtual void  __stdcall   SetDate(TDateTime Value)=0;

6. virtual AnsiString  __stdcall   GetNote()=0;

7. virtual void  __stdcall   SetNote(AnsiString Value)=0;

8. __property TDateTime  _Date={read = GetDate,write = SetDate};

9. __property AnsiString  _Note={read =GetNote,write = SetNote};

10. };

11. typedef System::DelphiInterface<IAgenda>  _di_IAgenda;

Il est à noter l'utilisation de property.

IV-D - La Classe d'implémentation

Déclaration

1. class CalcImplementation : public TInterfacedObject ,public ICalc ,public IAgenda

2. {

3. public:

4. // implementation de ICalc

5. virtual int __stdcall  Addition(int a, int b);

6. virtual int __stdcall  Soustraction(int a, int b);

7. virtual float __stdcall Division(int a, int b);

8. virtual int __stdcall Multiplication(int a, int b);

9. virtual float __stdcall Racine(int a);

10. virtual long __stdcall Carre(int a);

11. // implementation de IAgenda

12. public:  // utilisé pour clarifié la déclaration de l'interface IAgenda

13. virtual TDateTime __stdcall GetDate();

14. virtual void __stdcall SetDate(TDateTime Value);

15. virtual AnsiString __stdcall GetNote();

16. virtual void __stdcall SetNote(AnsiString Value);

17. // implementation de TInterfacedObject

18. public:

19. virtual HRESULT __stdcall QueryInterface(const GUID& IID, void ** Obj);

20. virtual ULONG __stdcall AddRef();

21. virtual ULONG __stdcall Release();

22. private:

23. TDateTime Fdate;

24. AnsiString Fnote;

25. };

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

Il est a noter que cette classe dispose de deux 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

1. __stdcall CalcImplementation::CalcImplementation()

2. : Fdate(NULL),

3. Fnote("")

4. {

5. }

6. //---------------------------------------------------------------------------

8. int __stdcall CalcImplementation::Addition(int a, int b)

9. {

10. return a+b;

11. }

12. //---------------------------------------------------------------------------

13. int __stdcall CalcImplementation::Soustraction(int a, int b)

14. {

15. return a-b;

16. }

17. //---------------------------------------------------------------------------

18. float __stdcall CalcImplementation::Division(int a, int b)

19. {

20. return static_cast<float>(a)/static_cast<float>(b);

21. }

22. //---------------------------------------------------------------------------

23. int __stdcall CalcImplementation::Multiplication(int a, int b)

24. {

25. return a*b;

26. }

27. //---------------------------------------------------------------------------

28. float __stdcall CalcImplementation::Racine(int a)

29. {

30. return sqrt(a);

31. }

32. //---------------------------------------------------------------------------

33. long __stdcall CalcImplementation::Carre(int a)

34. {

35. return a*a;

36. }

37. //---------------------------------------------------------------------------

39. HRESULT __stdcall CalcImplementation::QueryInterface(const GUID& IID, void ** Obj)

40. {

41. return TInterfacedObject::QueryInterface(IID, (void *)Obj);

42. }

43. //---------------------------------------------------------------------------

44. ULONG __stdcall CalcImplementation::AddRef()

45. {

46. return TInterfacedObject::_AddRef();

47. }

49. //---------------------------------------------------------------------------

51. ULONG __stdcall CalcImplementation::Release()

52. {

53. return TInterfacedObject::_Release();

54. }

55. //---------------------------------------------------------------------------

56. TDateTime __stdcall CalcImplementation::GetDate()

57. {

58. if (static_cast<int>(Fdate)) return Fdate;

59. else

60. return Now();

61. }

62. //---------------------------------------------------------------------------

63. void __stdcall CalcImplementation::SetDate(TDateTime Value)

64. {

65. Fdate=Value;

66. }

67. //---------------------------------------------------------------------------

68. AnsiString __stdcall CalcImplementation::GetNote()

69. {

70. return Fnote;

71. }

72. //---------------------------------------------------------------------------

73. void __stdcall CalcImplementation::SetNote(AnsiString Value)

74. {

75. Fnote=Value;

76. }

77. //---------------------------------------------------------------------------

V - L'application

Déclaration

1. class TForm1 : public TForm

2. {

3. __published: // Composants gérés par l'EDI

4. TPanel *Panel1;

5. TLabeledEdit *lbledtB;

6. TStatusBar *Stat1;

7. TGroupBox *grp1;

8. TLabeledEdit *lbledtResult;

9. TLabeledEdit *lbledtA;

10. TPanel *pnl1;

11. TButton *btnbtminus;

12. TButton *btnPlus;

13. TButton *btnX;

14. TButton *btndiv;

15. TButton *btnRacine;

16. TButton *btnCarre;

17. TGroupBox *grp2;

18. TLabel *lbl2;

19. TEdit *edtReadNote;

20. TButton *btnLireNote;

21. TLabel *lbl3;

22. TLabel *lbl4;

23. TEdit *edtReadHeure;

24. TButton *btnReadHeure;

25. TEdit *edtWriteNote;

26. void __fastcall btnbtminusClick(TObject *Sender);

27. void __fastcall btnCarreClick(TObject *Sender);

28. void __fastcall btndivClick(TObject *Sender);

29. void __fastcall btnPlusClick(TObject *Sender);

30. void __fastcall btnRacineClick(TObject *Sender);

31. void __fastcall btnXClick(TObject *Sender);

32. void __fastcall btnLireNoteClick(TObject *Sender);

33. void __fastcall btnReadHeureClick(TObject *Sender);

34. void __fastcall btnWriteNoteClick(TObject *Sender);

35. private:     // Déclarations de l'utilisateur

36. _di_ICalc mCalc;

37. _di_IAgenda mAgenda;

38. bool Fagenda;

39. public:

40. __fastcall TForm1(TComponent* Owner);

41. };

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

- _di_ICalc mCalc;

- _di_IAgenda mAgenda;

ainsi qu'une variable bool.

Implémentation

1. __fastcall TForm1::TForm1(TComponent* Owner)

2. : TForm(Owner)

3. {

4. mCalc = new CalcImplementation();    // instanciation de l'interface

5. Fagenda= mCalc->Supports(mAgenda);   // instanciation et test si interface existante

6. }

7. //---------------------------------------------------------------------------

8. void __fastcall TForm1::btnbtminusClick(TObject *Sender)

9. {

10. try

11. {

12. lbledtResult->Text=mCalc->Soustraction(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));

13. }

14. catch(Exception &E)

15. {

16. Stat1->SimpleText=E.Message;

17. }

18. }

19. //---------------------------------------------------------------------------

20. void __fastcall TForm1::btnCarreClick(TObject *Sender)

21. {

22. try

23. {

24. lbledtResult->Text=mCalc->Carre(StrToInt(lbledtA->Text));

25. }

26. catch(Exception &E)

27. {

28. Stat1->SimpleText=E.Message;

29. }

30. }

31. //---------------------------------------------------------------------------

32. void __fastcall TForm1::btndivClick(TObject *Sender)

33. {

34. try

35. {

36. lbledtResult->Text=mCalc->Division(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));

37. }

38. catch(Exception &E)

39. {

40. Stat1->SimpleText=E.Message;

41. }

42. }

43. //---------------------------------------------------------------------------

44. void __fastcall TForm1::btnPlusClick(TObject *Sender)

45. {

46. try

47. {

48. lbledtResult->Text=mCalc->Addition(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));

49. }

50. catch(Exception &E)

51. {

52. Stat1->SimpleText=E.Message;

53. }

54. }

55. //---------------------------------------------------------------------------

56. void __fastcall TForm1::btnRacineClick(TObject *Sender)

57. {

58. try

59. {

60. lbledtResult->Text=mCalc->Racine(StrToInt(lbledtA->Text));

61. }

62. catch(Exception &E)

63. {

64. Stat1->SimpleText=E.Message;

65. }

66. }

67. //---------------------------------------------------------------------------

68. void __fastcall TForm1::btnXClick(TObject *Sender)

69. {

70. try

71. {

72. lbledtResult->Text=mCalc->Multiplication(StrToInt(lbledtA->Text),StrToInt(lbledtB->Text));

73. }

74. catch(Exception &E)

75. {

76. Stat1->SimpleText=E.Message;

77. }

78. }

79. //---------------------------------------------------------------------------

80. void __fastcall TForm1::btnLireNoteClick(TObject *Sender)

81. {

82. if(Fagenda)

83. edtReadNote->Text=mAgenda->_Note;

84. }

85. //---------------------------------------------------------------------------

86. void __fastcall TForm1::btnReadHeureClick(TObject *Sender)

87. {

88. if(Fagenda)

89. edtReadHeure->Text=TimeToStr(mAgenda->_Date);

90. }

91. //---------------------------------------------------------------------------

92. void __fastcall TForm1::btnWriteNoteClick(TObject *Sender)

93. {

94. if(Fagenda)

95. mAgenda->_Note=edtWriteNote->Text;

96. }

97. //---------------------------------------------------------------------------

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 :

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

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

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. :

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 2 d'utilisation des interfaces (en préparation) je vous proposerai la création de plugin basée sur un tutoriel 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 :

- la FAQ C++ Builder par Geronimo ;

- la FAQ C ;

- la FAQ C++.