Die Experten-Schnittstellen implementieren
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;