Implementing the Wizard Interfaces
Go Up to Writing a Wizard Class
Every wizard class must implement at least IOTAWizard, which requires implementing its ancestors, IOTANotifier and IInterface, too. Form and project wizards must implement all their ancestor interfaces, namely IOTARepositoryWizard, IOTAWizard, IOTANotifier, and IInterface.
In C++, to use NotifierObject as a base class, you must use multiple inheritance. Your wizard class must inherit from NotifierObject and from the wizard interfaces that you need to implement, such as IOTAWizard. Because IOTAWizard inherits from IOTANotifier and IInterface, there is an ambiguity in the derived class: functions such as AddRef()
are declared in every branch of the ancestral inheritance graph. To resolve this problem, pick one base class as the primary base class and delegate all ambiguous functions to that one class. For example, the class declaration might look as follows:
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();
};
// implementation
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, to create the menu wizard, you need to implement two additional interfaces: IOTAWizard and IOTAMenuWizard. The following sample is a skeleton code for menu wizards:
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.
Your implementation of IInterface must follow the normal rules for Delphi interfaces, which are the same as the rules for COM interfaces. That is, QueryInterface performs type casts, while _AddRef and _Release manage reference counting. You might want to use a common base class to simplify writing wizard and notifier classes. For this purpose, the ToolsAPI unit defines a class, TNotifierObject, which implements the IOTANotifier interface with empty method bodies.
You can write a class similar to TNotifierObject in C++:
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;
};
// implementation
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, TNotifierObject can be defined as:
type
TNotifierObject = class(TInterfacedObject, IOTANotifier)
protected
// IUnknown implementation
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// IOTANotifier implementation
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
// Reference counting not implemented
Result := -1;
end;
function TNotifierObject._Release: Integer;
begin
// Reference counting not implemented
Result := -1;
end;
Although wizards inherit from IOTANotifier, and must therefore implement all of its functions, the IDE does not usually make use of those functions, so your implementations can be empty (as they are in TNotifierObject). Thus, when you write your wizard class, you only have to declare and implement those interface methods introduced by the wizard interfaces, accepting the TNotifierObject implementation of IOTANotifier.
// C++ empty implementations
void __fastcall NotifierObject::AfterSave() {}
void __fastcall NotifierObject::BeforeSave() {}
void __fastcall NotifierObject::Destroyed() {}
void __fastcall NotifierObject::Modified() {}
//Delphi empty implementation
procedure TXPEditReaderStream.AfterSave;
begin
// Do nothing
end;
procedure TXPEditReaderStream.BeforeSave;
begin
// Do nothing
end;
procedure TXPEditReaderStream.Modified;
begin
// Do nothing
end;