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
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 :
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 trois méthodes.
III-B. Définition d'une classe d'implémentation▲
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
);
}
;
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▲
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▲
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
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 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
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
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 deux Delphi interfaces :
- _di_ICalc mCalc;
- _di_IAgenda mAgenda;
ainsi qu'une variable bool.
Implémentation
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 :
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 :
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. :
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 :