Implementing the Wizard Interfaces

From RAD Studio
Jump to: navigation, search

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;

See Also