Die Experten-Schnittstellen implementieren

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Eine Experten-Klasse erstellen

Jede Experten-Klasse muss mindestens IOTAWizard sowie deren Vorgänger implementieren, nämlich IOTANotifier und IInterface. Formular- und Projekt-Experten müssen sämtliche Vorgänger-Schnittstellen implementieren, also IOTARepositoryWizard, IOTAWizard, IOTANotifier und IInterface.

Zur Nutzung von NotifierObject als Basisklasse in C++ ist Mehrfachvererbung erforderlich. Ihre Experten-Klasse muss sowohl von NotifierObject erben als auch von den Experten-Schnittstellen, die Sie implementieren müssen (z.B. IOTAWizard). Da IOTAWizard von IOTANotifier und IInterface erbt, würde es Mehrdeutigkeiten in der abgeleiteten Klasse geben: Funktionen wie AddRef() sind in jedem Vorgängerzweig deklariert. Zur Vermeidung dieses Problems wählen Sie eine Klasse als Primärklasse, an die Sie alle mehrfachen Funktionen delegieren. Die Klassendeklaration könnte wie folgt lauten:

 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();
 };

 // Implementierung
 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);
 }


In Delphi sollen Sie zwei weitere Schnittstellen (IOTAWizard und IOTAMenuWizard) implementieren, um den Menü-Experte zu erstellen. Das folgende Beispiel ist ein Skeleton-Code für Menü-Experten:

 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;

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


Bei der Implementierung von IInterface sind die üblichen Regeln für Delphi-Schnittstellen einzuhalten, die mit denen für COM-Schnittstellen identisch sind. QueryInterface bewirkt somit die Typumwandlung, _AddRef sowie _Release dienen zur Referenzzählung. Eine gemeinsame Basisklasse kann die Erstellung von Experten- und Notifier-Klassen vereinfachen. Zu diesem Zweck definiert die Unit ToolsAPI die Klasse TNotifierObject, durch die die IOTANotifier-Schnittstelle mit leeren Methodendeklarationen implementiert wird.

In C++ können Sie eine TNotifierObject ähnliche Klasse schreiben.

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;
 };

 // Implementierung
 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;
 }


In Delphi kann TNotifierObject wie folgt definiert werden:

 type
 TNotifierObject = class(TInterfacedObject, IOTANotifier)
 protected
    // Implementierung von IUnknown 
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    // Implementierung von IOTANotifier
    procedure AfterSave;
    procedure BeforeSave;
    procedure Modified;
    procedure Destroyed;
 end;

 Implementierung
 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
  // Referenzzählung wird nicht implementiert
  Result := -1;
 end;
 function TNotifierObject._Release: Integer;
 begin
  // Referenzzählung wird nicht implementiert
  Result := -1;
 end;


Experten erben von IOTANotifier und müssen deshalb alle Funktionen dieser Schnittstelle implementieren. Die IDE nutzt diese Funktionen in der Regel jedoch nicht, sodass die Implementierungen leer bleiben können (wie auch in der Klasse TNotifierObject): Wenn Sie also Ihre Expertenklasse erstellen, müssen Sie nur diejenigen Schnittstellenmethoden deklarieren und implementieren, die von den Expertenschnittstellen eingeführt wurden, die die TNotifierObject-Implementierung von IOTANotifier akzeptieren.

 // C++ leere Implementierungen
 void __fastcall NotifierObject::AfterSave()  {}
 void __fastcall NotifierObject::BeforeSave() {}
 void __fastcall NotifierObject::Destroyed()  {}
 void __fastcall NotifierObject::Modified()   {}
 //Delphi leere Implementierung
    procedure TXPEditReaderStream.AfterSave;
  begin
    // Nichts ausführen
  end;
   procedure TXPEditReaderStream.BeforeSave;
 begin
  // Nichts ausführen
 end;
   procedure TXPEditReaderStream.Modified;
 begin
  // Nichts ausführen
 end;


Siehe auch