How to write Delphi Wizards
TCompilerOutputExpert
Now, it's time to write an esAddIn Wizard, just like the two examples above, but this time we respond to the OnClick event by creating and showing the Compiler Output Wizard, like we did earlier in this paper. Since we'll be porting this last Wizard of this paper from Delphi 3 to Delphi 2 and C++Builder, let's include the entire code to look closely at the potential porting issues involved:
library AddIn;
uses
ShareMem, VirtIntf, ExptIntf, ToolIntf, Menus, Forms, Classes, Dcc32Out;
{ EXPERT TYPE DEFINITION }
Type
TBDcc32OutputExpert = class(TIExpert)
public
constructor Create; virtual;
destructor Destroy; override;
function GetStyle: TExpertStyle; override;
function GetIDString: String; override;
function GetName: String; override;
function GetAuthor: String; override;
protected
procedure OnClick(Sender: TIMenuItemIntf); virtual;
private
MenuItem: TIMenuItemIntf;
end {TBDcc32OutputExpert};
{ EXPERT SUPPORT FUNCTION }
procedure HandleException;
begin
if Assigned(ToolServices) then
ToolServices.RaiseException(ReleaseException)
end {HandleException};
{ EXPERT IMPLEMENTATION }
function TBDcc32OutputExpert.GetStyle: TExpertStyle;
begin
Result := esAddIn
end {GetStyle};
function TBDcc32OutputExpert.GetIDString: String;
begin
try
Result := 'DrBob.TCompilerOutputExpert'
except
HandleException
end
end {GetIDString};
function TBDcc32OutputExpert.GetName: String;
begin
try
Result := 'Compiler Output Wizard'
except
HandleException
end
end {GetName};
function TBDcc32OutputExpert.GetAuthor: String;
begin
try
Result := 'Bob Swart (aka Dr.Bob)' { although not needed for esAddIn }
except
HandleException
end
end {GetAuthor};
constructor TBDcc32OutputExpert.Create;
var Main: TIMainMenuIntf;
ProjectBuildItem: TIMenuItemIntf;
Project: TIMenuItemIntf;
begin
inherited Create;
MenuItem := nil;
if ToolServices <> nil then
try
Main := ToolServices.GetMainMenu;
if Main <> nil then { we've got the main menu }
try
ProjectBuildItem := Main.FindMenuItem('ProjectBuildItem');
if ProjectBuildItem <> nil then
try
Project := ProjectBuildItem.GetParent;
if Project <> nil then
try
MenuItem := Project.InsertItem(ProjectBuildItem.GetIndex+1,
'&Dr.Bob''s Compiler Output Wizard...',
'DrBob','',
ShortCut(Ord('D'),[ssCtrl]),0,0,
[mfEnabled, mfVisible], OnClick);
finally
Project.DestroyMenuItem
end
finally
ProjectBuildItem.DestroyMenuItem
end
finally
Main.Free
end
except
HandleException
end
end {Create};
destructor TBDcc32OutputExpert.Destroy;
begin
try
if MenuItem <> nil then MenuItem.DestroyMenuItem;
inherited Destroy
except
HandleException
end
end {Destroy};
procedure TBDcc32OutputExpert.OnClick(Sender: TIMenuItemIntf);
begin
try
with TDCC32OutputForm.Create(Application) do
try
ShowModal
finally
Free
end
except
HandleException
end
end {OnClick};
{ DLL EXPERT INTERFACE }
procedure DoneExpert;
begin
// cleanup
end {DoneExpert};
function InitExpert(ToolServices: TIToolServices;
RegisterProc: TExpertRegisterProc;
var Terminate: TExpertTerminateProc): Boolean; stdcall;
begin
Result := True;
try
ExptIntf.ToolServices := ToolServices; { Save! }
if ToolServices <> nil then
Application.Handle := ToolServices.GetParentHandle;
Terminate := DoneExpert;
Result := RegisterProc(TBDcc32OutputExpert.Create);
except
HandleException
end
end {InitExpert};
exports
InitExpert name 'INITEXPERT0016', { Delphi 2.0x & C++Builder }
InitExpert name 'INITEXPERT0017'; { Delphi 3 }
begin
end.
Notice the fact that InitExpert got exported twice? One as name 'INITEXPERT0016' and once as name 'INITEXPERT0017'. The former is the name that's used by Delphi 2.0x and C++Builder, while the latter is the name used by Delphi 3. If we don't export the InitExpert function by the right name, then Delphi will just disregard the Wizard and tells us that it's the wrong version. So, by exporting InitExpert twice under both names (or maybe even as 'INITEXPERT0018' to prepare for Delphi 4), we've taken the first obstacle. But don't party yet; there are more to come...