Implémentation des interfaces d'expert
Remonter à Ecriture d'une classe d'expert
Chaque classe expert doit implémenter au minimum IOTAWizard, ce qui nécessite également d'implémenter ses ancêtres : IOTANotifier et aussi IInterface. Les experts fiche et projet doivent implémenter toutes leurs interfaces ancêtres, soit, IOTARepositoryWizard, IOTAWizard, IOTANotifier et IInterface.
Pour C++, pour employer NotifierObject comme classe de base, vous devez utiliser l'héritage multiple. Votre classe expert doit hériter de NotifierObject et des interfaces expert que vous devez implémenter, comme IOTAWizard. Comme IOTAWizard hérite de IOTANotifier et de IInterface, il y a une ambiguïté dans la classe dérivée : des fonctions comme AddRef()
sont déclarées dans chaque branche du graphe d'héritage ancestral. Pour résoudre ce problème, choisissez l'une des classes de base comme classe de base primaire et déléguez tous les appels de fonction ambigus à cette classe. Vous pouvez, par exemple, utiliser la déclaration de classe suivante :
class PACKAGE MyWizard : public NotifierObject, public IOTAMenuWizard {
typedef NotifierObject inherited;
public:
// IOTAWizard
virtual AnsiString __fastcall GetIDString();
virtual AnsiString __fastcall GetName();
virtual TWizardState __fastcall GetState();
virtual void __fastcall Execute();
// IOTAMenuWizard
virtual AnsiString __fastcall GetMenuText();
void __fastcall AfterSave();
void __fastcall BeforeSave();
void __fastcall Destroyed();
void __fastcall Modified();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
};
// implémentation
ULONG __stdcall MyWizard::AddRef() { return inherited::AddRef(); }
ULONG __stdcall MyWizard::Release() { return inherited::Release(); }
HRESULT __stdcall MyWizard::QueryInterface(const GUID& iid, void** obj)
{
if (iid == __uuidof(IOTAMenuWizard)) {
*obj = static_cast<IOTAMenuWizard*>(this);
static_cast<IOTAMenuWizard*>(*obj)->AddRef();
return S_OK;
}
if (iid == __uuidof(IOTAWizard)) {
*obj = static_cast<IOTAWizard*>(this);
static_cast<IOTAWizard*>(*obj)->AddRef();
return S_OK;
}
return inherited::QueryInterface(iid, obj);
}
En Delphi, pour créer menu de l'expert vous devez implémenter deux interfaces supplémentaires : IOTAWizard et IOTAMenuWizard. L'exemple suivant est un code squelette pour les menus de l'expert :
interface
uses
ToolsAPI,...;
Type
MyWizard = class(TNotifierObject, IOTAWizard, IOTAMenuWizard)
public
{ IOTAWizard }
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
{ IOTAMenuWizard }
function GetMenuText: string;
end;
procedure Register;
//implémentation
const
SADPMenuIdString ='ADP.Headline';
SADPMenu ='About...';
.....
procedure MyWizard.Execute;
begin
if ADPHeadlinesForm = nil then ADPHeadlinesForm := TADPHeadlinesForm.Create(Application) ;
ADPHeadlinesForm.Show;
ADPHeadlinesForm.BringToFront;
end;
function MyWizard.GetIDString: string;
begin
Result := SADPMenuIdString;
end;
function MyWizard.GetMenuText: string;
begin
Result := SADPMenu;
end;
function MyWizard.GetName: string;
begin
Result := SADPMenuIdString;
end;
function MyWizard.GetState: TWizardState;
begin
Result := [wsEnabled];
end;
end.
Votre implémentation de IInterface suit les mêmes règles que celles des interfaces Delphi, qui sont les mêmes que celles des interfaces COM. C'est-à-dire que QueryInterface effectue les transtypages et que _AddRef et _Release gèrent le comptage de références. Vous pouvez utiliser une classe de base commune pour simplifier l'écriture des classes d'experts et de notificateurs. A cette fin, l'unité ToolsAPI définit une classe, TNotifierObject, qui implémente l'interface IOTANotifier avec des corps de méthode vides.
Vous pouvez écrire en C++ une classe similaire à la classe TNotifierObject.
class PACKAGE NotifierObject : public IOTANotifier {
public:
__fastcall NotifierObject() : ref_count(0) {}
virtual __fastcall ~NotifierObject();
void __fastcall AfterSave();
void __fastcall BeforeSave();
void __fastcall Destroyed();
void __fastcall Modified();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
private:
long ref_count;
};
// implémentation
ULONG __stdcall NotifierObject::AddRef()
{
return InterlockedIncrement(&ref_count);
}
ULONG __stdcall NotifierObject::Release()
{
ULONG result = InterlockedDecrement(&ref_count);
if (ref_count == 0)
delete this;
return result;
}
HRESULT __stdcall NotifierObject::QueryInterface(const GUID& iid, void** obj)
{
if (iid == __uuidof(IInterface)) {
*obj = static_cast<IInterface*>(this);
static_cast<IInterface*>(*obj)->AddRef();
return S_OK;
}
if (iid == __uuidof(IOTANotifier)) {
*obj = static_cast<IOTANotifier*>(this);
static_cast<IOTANotifier*>(*obj)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
En Delphi, TNotifierObject peut être définie comme :
type
TNotifierObject = class(TInterfacedObject, IOTANotifier)
protected
// implémentation de IUnknown
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// implémentation de IOTANotifier
procedure AfterSave;
procedure BeforeSave;
procedure Modified;
procedure Destroyed;
end;
//implémentation
function TNotifierObject.QueryInterface(const IID: TGUID; out Obj):HResult;
const
E_NOINTERFACE = HResult($80004002);
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
function TNotifierObject._AddRef: Integer;
begin
// Le comptage des références n'est pas implémenté
Result := -1;
end;
function TNotifierObject._Release: Integer;
begin
// Le comptage des références n'est pas implémenté
Result := -1;
end;
Bien que les experts héritent de IOTANotifier et doivent donc implémenter toutes ses fonctions, l'EDI n'utilise généralement pas ces fonctions et votre implémentation peut rester vide (comme c'est le cas dans TNotifierObject). Par conséquent, lorsque vous écrivez votre classe expert, vous devez uniquement déclarer et implémenter les méthodes d'interface introduites par les interfaces expert, en acceptant l'implémentation TNotifierObject de IOTANotifier.
// Implémentations C++ vides
void __fastcall NotifierObject::AfterSave() {}
void __fastcall NotifierObject::BeforeSave() {}
void __fastcall NotifierObject::Destroyed() {}
void __fastcall NotifierObject::Modified() {}
//Implémentation Delphi vide
procedure TXPEditReaderStream.AfterSave;
begin
// Ne fait rien
end;
procedure TXPEditReaderStream.BeforeSave;
begin
// Ne fait rien
end;
procedure TXPEditReaderStream.Modified;
begin
// Ne fait rien
end;