ウィザード インターフェイスの実装
ウィザード クラスの作成 への移動
すべてのウィザード クラスでは、少なくとも IOTAWizard を実装する必要があります(その上位クラス、IOTANotifier と IInterface の実装も必要)。 フォーム ウィザードとプロジェクト ウィザードでは、そのすべての上位インターフェイス、つまり IOTARepositoryWizard、IOTAWizard、IOTANotifier、および IInterface を実装する必要があります。
C++ の場合、NotifierObject を基底クラスとして使用するには、複数の継承を使用する必要があります。 ウィザード クラスは、NotifierObject から、および実装を要するウィザード インターフェイス(IOTAWizard など)から継承する必要があります。 IOTAWizard は IOTANotifier と IInterface から継承するので、派生クラスにはあいまいな要素があります。たとえば、AddRef()
関数は継承の上位からみてすべての下位クラスで宣言されます。 この問題を解決するためには、最上位の基底クラスとして 1 つの基底クラスを選択し、このクラスにすべてのあいまいな関数を割り当てます(デリゲート)。 たとえば、クラスの宣言は次のようになります。
class PACKAGE MyWizard : public NotifierObject, public IOTAMenuWizard {
typedef NotifierObject inherited;
public:
// IOTAWizard
virtual UnicodeString __fastcall GetIDString();
virtual UnicodeString __fastcall GetName();
virtual TWizardState __fastcall GetState();
virtual void __fastcall Execute();
// IOTAMenuWizard
virtual UnicodeString __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();
};
// 実装
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);
}
Delphi では、メニュー ウィザードを作成するために追加で IOTAWizard と IOTAMenuWizard の 2 つのインターフェイスを実装する必要があります。 次に示す例は、メニュー ウィザード用のスケルトン コードです。
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;
implementation
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.
IInterface の実装は、Delphi インターフェイスの標準規則に従う必要があります。これは、COM インターフェイスの規則と同じです。 つまり、QueryInterface で型キャストを行い、_AddRef と _Release で、参照カウントを管理します。共通の基底クラスを使用すると、ウィザード クラスとノーティファイア クラスの記述を単純化できます。 このために、ToolsAPI ユニットは TNotifierObject クラスを定義します。これは、IOTANotifier インターフェイス(メソッド本体はなし)を実装したものです。
C++ で 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;
};
// 実装
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;
}
Delphi で、TNotifierObject は次のように定義されます。
type
TNotifierObject = class(TInterfacedObject, IOTANotifier)
protected
// IUnknown の実装
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// IOTANotifier の実装
procedure AfterSave;
procedure BeforeSave;
procedure Modified;
procedure Destroyed;
end;
implementation
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
// 参照カウントは実装されていない
Result := -1;
end;
function TNotifierObject._Release: Integer;
begin
// 参照カウントは実装されていない
Result := -1;
end;
ウィザードは IOTANotifier から継承するので、そのすべての関数を実装する必要がありますが、IDE は通常これらの関数を利用しないので、実装は空でもかまいません(TNotifierObject と同様)。 このため、独自のウィザード クラスを記述するときは、IOTANotifier の TNotifierObject の実装を受け入れて、ウィザード インターフェイスによって導入されたこれらのインターフェイス メソッドを宣言して実装するだけです。
// C++ の空実装
void __fastcall NotifierObject::AfterSave() {}
void __fastcall NotifierObject::BeforeSave() {}
void __fastcall NotifierObject::Destroyed() {}
void __fastcall NotifierObject::Modified() {}
//Delphi の空実装
procedure TXPEditReaderStream.AfterSave;
begin
// 何もしない
end;
procedure TXPEditReaderStream.BeforeSave;
begin
// 何もしない
end;
procedure TXPEditReaderStream.Modified;
begin
// 何もしない
end;