Implémentation des interfaces d'expert

De RAD Studio
Aller à : navigation, rechercher

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;


Voir aussi