5

I need to create a complex form with my own components (kinda OneClick installer), and use it as the replacement of the standard InnoSetup wizard. Is it possible?

My form is placed into DLL, and this DLL will be available for InnoSetup process.

This is how I tried to do that:

Delphi DLL code

library OneClickWizard;

uses
  SysUtils,
  Classes,
  Wizard in 'Wizard.pas' {FormWizard};

{$R *.res}

exports
  CreateWizardForm,
  DestroyWizardForm;

begin
end.

Delphi form

unit Wizard;

interface

type
  TFormWizard = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormWizard: TFormWizard;

procedure CreateWizardForm(AppHandle: THandle); stdcall;
procedure DestroyWizardForm; stdcall;

implementation

{$R *.dfm}

procedure CreateWizardForm(AppHandle: THandle);
begin
  Application.Handle := AppHandle;
  FormWizard := TFormWizard.Create(Application);
  FormWizard.Show;
  FormWizard.Refresh;
end;

procedure DestroyWizardForm;
begin
  FormWizard.Free;
end;

InnoSetup script (iss)

[Setup]
;Disable all of the default wizard pages
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyMemo=true
DisableReadyPage=true
DisableStartupPrompt=true
DisableWelcomePage=true
DisableFinishedPage=true

[Files]
Source:"OneClickWizard.dll"; Flags: dontcopy

[Code]
procedure CreateWizardForm(AppHandle: Cardinal); 
  external 'CreateWizardForm@files:OneClickWizard.dll stdcall';
procedure DestroyWizardForm;
  external 'DestroyWizardForm@files:OneClickWizard.dll stdcall';


procedure InitializeWizard();
begin
  CreateWizardForm(MainForm.Handle);
end;

The form appearing on the screen, but it doesn't react on my input. Seems it is out of main message cycle. How to do this correctly?

menjaraz
  • 7,551
  • 4
  • 41
  • 81
Andrew
  • 3,696
  • 3
  • 40
  • 71

3 Answers3

6

In my setup I do something similar. InnoSetup code I pass the handle as StrToInt(ExpandConstant('{wizardhwnd}')) (my guess is that MainForm.Handle is zero)

in the DLL:

OldAppHandle := Application.Handle;
try
  Application.Handle := hAppHandle; // hAppHandle the handle from InnoSetup
  F := TfmZForm.Create(Application);
  try
    F.Caption := lpTitle;
    F.ShowModal;
    Result := F.ErrorCode;
  finally
    F.Free;
  end;
finally
  Application.Handle := OldAppHandle;
end;
kobik
  • 21,001
  • 4
  • 61
  • 121
  • The big difference here is ShowModal I suspect – David Heffernan Dec 21 '11 at 10:33
  • I modified your solution a little, because I want to hide the standard InnoSetup Wizard: in the InitializeWizard() procedure I added WizardForm.BorderStyle := bsNone; WizardForm.Width := 0; WizardForm.Height := 0; CreateWizardForm(WizardForm.Handle); – Andrew Dec 21 '11 at 11:55
  • @kobik, I'm sorry, but I have to check the David's answer as correct, because he was the first who proposed to use ShowModal() – Andrew Dec 21 '11 at 11:58
5

I know precisely nothing about InnoSetup but surely you need to use ShowModal rather than Show here. Installation UI is invariably modal and what you want here is to wait until the user has finished iteracting with the form before you return to Inno. Otherwise, how would Inno know when to proceed? ShowModal runs a message loop to service the form so there will be no problems receiving input.

You would also change your DLL to remove DestroyWizardForm since the function that calls ShowModal can both create and destroy the form.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
1

If you want to entirely replace the UI, it will probably be easier to create a stub application that presents the form then runs the normal setup in silent mode passing various command line parameters.

Either that or at the very least, using Inno's native form and wizard page functions/logic.

Deanna
  • 23,876
  • 7
  • 71
  • 156