Inno Setup - Define music button and error with language selector? - inno-setup

This is the code with the error:
#include "Music\botva2.iss"
#include "Music\BASS_Module.iss"
[Code]
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
external 'ShellExecuteW#shell32.dll stdcall';
var
LanguageForm: TSetupForm;
SelectLabel: TNewStaticText;
CancelButton: TNewButton;
procedure LangChange(Sender : TObject);
begin
case TNewComboBox(Sender).ItemIndex of
0: { English }
begin
SelectLabel.Caption := 'Select the language to the installation:';
CancelButton.Caption := 'Cancel';
LanguageForm.Caption := 'PH';
end;
1: { Español }
begin
SelectLabel.Caption := 'Selecciona el idioma de la instalación:';
CancelButton.Caption := 'Cancelar';
LanguageForm.Caption := 'PH';
end;
end;
end;
procedure SelectLanguage();
var
OKButton: TNewButton;
LangCombo: TNewComboBox;
Languages: TStrings;
Params: string;
Instance: THandle;
P, I: Integer;
S, L: string;
begin
Languages := TStringList.Create();
Languages.Add('eng=English');
Languages.Add('spa=Español');
LanguageForm := CreateCustomForm;
LanguageForm.Caption := SetupMessage(msgSelectLanguageTitle);
LanguageForm.ClientWidth := ScaleX(240);
LanguageForm.ClientHeight := ScaleY(125);
LanguageForm.BorderStyle := bsDialog;
LanguageForm.Center;
CancelButton := TNewButton.Create(LanguageForm);
CancelButton.Parent := LanguageForm;
CancelButton.Left := ScaleX(140);
CancelButton.Top := ScaleY(93);
CancelButton.Width := ScaleY(90);
CancelButton.Height := ScaleY(23);
CancelButton.TabOrder := 3;
CancelButton.ModalResult := mrCancel;
CancelButton.Caption := SetupMessage(msgButtonCancel);
OKButton := TNewButton.Create(LanguageForm);
OKButton.Parent := LanguageForm;
OKButton.Left := ScaleX(10);
OKButton.Top := ScaleY(93);
OKButton.Width := ScaleX(90);
OKButton.Height := ScaleY(23);
OKButton.Caption := SetupMessage(msgButtonOK);
OKButton.Default := True
OKButton.ModalResult := mrOK;
OKButton.TabOrder := 2;
LangCombo := TNewComboBox.Create(LanguageForm);
LangCombo.Parent := LanguageForm;
LangCombo.Left := ScaleX(16);
LangCombo.Top := ScaleY(56);
LangCombo.Width := ScaleX(206);
LangCombo.Height := ScaleY(21);
LangCombo.Style := csDropDownList;
LangCombo.DropDownCount := 16;
LangCombo.TabOrder := 1;
SelectLabel := TNewStaticText.Create(LanguageForm);
SelectLabel.Parent := LanguageForm;
SelectLabel.Left := ScaleX(16);
SelectLabel.Top := ScaleY(15);
SelectLabel.Width := ScaleX(273);
SelectLabel.Height := ScaleY(39);
SelectLabel.AutoSize := False
SelectLabel.Caption := SetupMessage(msgSelectLanguageLabel);
SelectLabel.TabOrder := 0;
SelectLabel.WordWrap := True;
for I := 0 to Languages.Count - 1 do
begin
P := Pos('=', Languages.Strings[I]);
L := Copy(Languages.Strings[I], 0, P - 1);
S := Copy(Languages.Strings[I], P + 1, Length(Languages.Strings[I]) - P);
LangCombo.Items.Add(S);
if L = ActiveLanguage then
LangCombo.ItemIndex := I;
LangCombo.OnChange := #LangChange;
end;
if LanguageForm.ShowModal = mrOK then
begin
// Collect current instance parameters
for I := 1 to ParamCount do
begin
S := ParamStr(I);
// Unique log file name for the elevated instance
if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
begin
S := S + '-localized';
end;
// Do not pass our /SL5 switch
if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
begin
Params := Params + AddQuotes(S) + ' ';
end;
end;
L := Languages.Strings[LangCombo.ItemIndex];
P := Pos('=', L);
L := Copy(L, 0, P-1);
// ... and add selected language
Params := Params + '/LANG=' + L;
Instance := ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
if Instance <= 32 then
begin
MsgBox(
Format('Running installer with selected language failed. Code: %d', [Instance]),
mbError, MB_OK);
end;
end;
end;
function InitializeSetup(): Boolean;
var
Language: string;
begin
Result := True;
Language := ExpandConstant('{param:LANG}');
if Language = '' then
begin
Log('No language specified, showing language dialog');
SelectLanguage();
Result := False;
Exit;
end
else
begin
Log('Language specified, proceeding with installation');
end;
end;
procedure RedesignWizardForm;
begin
with WizardForm do
begin
BorderIcons:=[];
Bevel1.Hide;
AutoScroll := False;
ClientHeight := ScaleY(349);
end;
with WizardForm.CancelButton do
begin
Top := ScaleY(319);
end;
with WizardForm.NextButton do
begin
Top := ScaleY(319);
end;
with WizardForm.BackButton do
begin
Top := ScaleY(319);
end;
with WizardForm.WizardBitmapImage do
begin
Width := ScaleX(500);
end;
with WizardForm.WelcomeLabel2 do
begin
Visible := False;
end;
with WizardForm.WelcomeLabel1 do
begin
Visible := False;
end;
with WizardForm.WizardSmallBitmapImage do
begin
Left := ScaleX(0);
Width := ScaleX(500);
Height := ScaleY(60);
end;
with WizardForm.PageDescriptionLabel do
begin
Visible := False;
end;
with WizardForm.PageNameLabel do
begin
Visible := False;
end;
with WizardForm.WizardBitmapImage2 do
begin
Width := ScaleX(500);
ExtractTemporaryFile('WizardForm.WizardBitmapImage2.bmp');
Bitmap.LoadFromFile(ExpandConstant('{tmp}\WizardForm.WizardBitmapImage2.bmp'));
end;
with WizardForm.FinishedLabel do
begin
Visible := False;
end;
with WizardForm.FinishedHeadingLabel do
begin
Visible := False;
end;
end;
procedure InitializeWizard1();
begin
RedesignWizardForm;
WizardForm.DiskSpaceLabel.Visible := False;
end;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4)
end;
procedure InitializeWizard();
begin
InitializeWizard1();
InitializeWizard2();
end;
procedure DeinitializeSetup();
begin
BASS_DeInit; //Îñâîáîæäàåì ïðîöåññ
gdipShutdown
end;
This code includes the language selector Inno Setup - How to change a label caption [or other controls in general], when selected value in combox box changes and a code that define music and music button. If i delete all about language selector, the code works fine. What is the problem?
The code of music and button includes: botva2.iss, BASS_Module.iss, botva2.dll, CallbackCtrl.dll.
This error appears when you select accept or cancel on the language selector.

The DeinitializeSetup is called, even when the setup is aborted by returning False from the InitializeSetup.
So I guess the BASS_DeInit (or the gdipShutdown) fails, because an equivalent BASS_Init was never called.
You have to avoid calling the code in the DeinitializeSetup, when the BASS_Init was never called.
var
BASS_Initialized: Boolean;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4);
BASS_Initialized := True;
end;
procedure DeinitializeSetup();
begin
if BASS_Initialized then
begin
BASS_DeInit;
gdipShutdown;
end;
end;

Related

Cover a check box in the image to require a code for its selection

I'm trying to create something like this:
I want the check box to be hidden behind the lock image, and clicking on it will open a form requesting code entry, and if the code is correct the image will be removed and the check box will be available.
Here's what I tried:
[files]
Source: "Lock.bmp"; Flags: dontcopy
{...}
[code]
var
CustomFuncPage: TWizardPage;
CBAdvFunctions: TNewCheckBox;
Locked: TBitmapImage;
procedure OnClickLock(Sender: TObject);
var
PassForm: TSetupForm;
Edit: TNewEdit;
OKButton, CancelButton: TNewButton;
begin
PassForm := CreateCustomForm();
PassForm.ClientWidth := ScaleX(256);
PassForm.ClientHeight := ScaleY(128);
PassForm.Caption := 'Password required';
Edit := TNewEdit.Create(PassForm);
Edit.Top := ScaleY(10);
Edit.Left := ScaleX(10);
Edit.Width := PassForm.ClientWidth - ScaleX(20);
Edit.Height := ScaleY(23);
Edit.Anchors := [akLeft, akTop, akRight];
Edit.Parent := PassForm;
OKButton := TNewButton.Create(PassForm);
OKButton.Parent := PassForm;
OKButton.Caption := 'OK';
OKButton.Left := PassForm.ClientWidth - ScaleX(166);
OKButton.Top := PassForm.ClientHeight - ScaleY(33);
OKButton.Height := ScaleY(23);
OKButton.Anchors := [akRight, akBottom]
OKButton.ModalResult := mrOk;
OKButton.Default := True;
CancelButton := TNewButton.Create(PassForm);
CancelButton.Parent := PassForm;
CancelButton.Caption := 'Cancel';
CancelButton.Left := PassForm.ClientWidth - ScaleX(85);
CancelButton.Top := PassForm.ClientHeight - ScaleY(33);
CancelButton.Height := ScaleY(23);
CancelButton.Anchors := [akRight, akBottom]
CancelButton.ModalResult := mrCancel;
CancelButton.Cancel := True;
PassForm.ActiveControl := Edit;
if PassForm.ShowModal() = mrOk then
begin
if Edit.Text = '1234' then
begin
Locked.Free;
PassForm.Free;
end else
begin
MsgBox('Password incorrect', mbError, MB_OK);
PassForm.Free;
end;
end else
begin
PassForm.Free;
end;
end;
Procedure InitializeWizard;
begin
CustomFuncPage := CreateCustomPage(wpSelectProgramGroup, {...}, {...});
CBAdvFunctions := TNewCheckBox.Create(CustomFuncPage);
CBAdvFunctions.Parent := CustomFuncPage.Surface;
CBAdvFunctions.Top := 0;
CBAdvFunctions.Left := 0;
CBAdvFunctions.Caption := 'Advanced Functions'
CBAdvFunctions.Width := ScaleX(130);
CBAdvFunctions.Cursor := 1;
CBAdvFunctions.SendToBack;
ExtractTemporaryFile('Lock.bmp');
Locked := TBitmapImage.Create(CustomFuncPage);
Locked.Parent := CustomFuncPage.Surface;
Locked.Bitmap.LoadFromFile(ExpandConstant('{tmp}\Lock.bmp'));
Locked.Bitmap.AlphaFormat := afPremultiplied;
Locked.SetBounds(0, 0, CBAdvFunctions.Width, CBAdvFunctions.Height);
Locked.Stretch := True;
Locked.BringToFront;
Locked.OnClick := #OnClickLock;
{...}
And this is the result:
This is the image I used:
I tried a few ways, but was unable to get the image to cover the checkbox.
Maybe someone has an idea?
By the way, maybe someone has an explanation for why the background of the image is not transparent?

Inno Setup - Dynamic positioning of controls/checkboxes

I have a bunch of checkboxes on a page, and all of those are conditionally visible, and the Top position is defined relative to the previous checkbox, e.g.
CheckBox4.Top := CheckBox3.Top + CheckBox3.Height + 5;
When at least one of the components is set to be invisible, the result looks like this:
What I'd like the checkboxes to do, is to shift upwards, if the previous one is set to invisible. Prog3 should be right underneath Prog1, or, if both Prog2 and Prog3 are hidden, Prog4 should be right underneath Prog1.
EDIT: My code after the answer:
var
PageNameLabel,PageDescriptionLabel: TLabel;
TypesComboOnChangePrev: TNotifyEvent;
UninstallConfigssPage: TNewNotebookPage;
UninstallNextButton: TNewButton;
CheckListBox: TNewCheckListBox;
Dirs: TStringList;
procedure UpdateUninstallWizard;
begin
UninstallProgressForm.PageNameLabel.Caption := CustomMessage('UninstPNL');
UninstallProgressForm.PageDescriptionLabel.Caption := CustomMessage('UninstPDL');
UninstallNextButton.Caption := CustomMessage('UninstBtn');
UninstallNextButton.ModalResult := mrOK;
end;
procedure UninstallNextButtonClick(Sender: TObject);
begin
UninstallNextButton.Visible := False;
end;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
end;
end;
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
PageNameLabel,PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
begin
if not UninstallSilent then
begin
UninstallProgressForm.Caption := CustomMessage('Uninst');
UninstallConfigssPage:= TNewNotebookPage.Create(UninstallProgressForm);
UninstallConfigssPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallConfigssPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallConfigssPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallConfigssPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left - ScaleX(20);
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := True;
PageText.ShowAccelChar := False;
PageText.Caption := CustomMessage('UninstTxt');
Dirs := TStringList.Create();
CheckListBox := TNewCheckListBox.Create(UninstallConfigssPage);
CheckListBox.Parent := UninstallConfigssPage;
CheckListBox.SetBounds(PageText.Left,ScaleY(30),PageText.Width,ScaleY(220));
CheckListBox.BorderStyle := bsNone;
CheckListBox.Color := clBtnFace;
CheckListBox.MinItemHeight := ScaleY(20);
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter'), 'Prog1');
AddDirCheckbox(ExpandConstant('{app}\Theseus'), 'Prog2');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter Revisited'), 'Prog3');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2'), 'Prog4');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Reloaded'), 'Prog5');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Conscription'), 'Prog6');
AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter'), 'Prog7');
AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter 2'), 'Prog8');
UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfigssPage;
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(35);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width + ScaleX(30);
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
UninstallNextButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder - 1;
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
end;
Use TInputOptionWizardPage, which is designed for this kind of tasks/layouts. Create it using CreateInputOptionPage.
Use TStringList (or array of string) to maintain association between the created checkboxes and the paths.
var
Page: TInputOptionWizardPage;
Dirs: TStringList;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
Page.Add(Caption);
end;
end;
procedure InitializeWizard();
begin
Page :=
CreateInputOptionPage(
wpWelcome, 'Configuration files found', 'Choose an action for configuration files',
'Choose the configuration files you''d like to be deleted.', False, False);
Dirs := TStringList.Create();
AddDirCheckbox('C:\dir1', 'Prog 1');
AddDirCheckbox('C:\dir2', 'Prog 2');
AddDirCheckbox('C:\dir3', 'Prog 3');
end;
To process the selected checkboxes/paths, use a code like this:
procedure CurStepChanged(CurStep: TSetupStep);
var
Index: Integer;
begin
if CurStep = ssInstall then
begin
for Index := 0 to Dirs.Count - 1 do
begin
if Page.Values[Index] then
begin
MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
end;
end;
end;
end;
Assuming C:\dir1 and C:\dir3 exist and C:\dir2 does not, you will get:
If you cannot use TInputOptionWizardPage, e.g. because you need the checkboxes on an uninstaller form or on a custom form, just create the TNewCheckListBox (what TInputOptionWizardPage uses internally).
The following example places the TNewCheckListBox on a blank custom TWizardPage, but you can of course place it, wherever you want.
var
Page: TWizardPage;
CheckListBox: TNewCheckListBox;
Dirs: TStringList;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
end;
end;
procedure InitializeWizard();
begin
Page :=
CreateCustomPage(
wpWelcome, 'Configuration files found',
'Choose an action for configuration files');
Dirs := TStringList.Create();
CheckListBox := TNewCheckListBox.Create(Page);
CheckListBox.Parent := Page.Surface;
CheckListBox.Width := Page.SurfaceWidth;
CheckListBox.Height := Page.SurfaceHeight;
{ The same styling as used by TInputOptionWizardPage }
CheckListBox.BorderStyle := bsNone;
CheckListBox.Color := clBtnFace;
CheckListBox.WantTabs := True;
CheckListBox.MinItemHeight := ScaleY(22);
AddDirCheckbox('C:\dir1', 'Prog 1');
AddDirCheckbox('C:\dir2', 'Prog 2');
AddDirCheckbox('C:\dir3', 'Prog 3');
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
Index: Integer;
begin
if CurStep = ssInstall then
begin
for Index := 0 to Dirs.Count - 1 do
begin
if CheckListBox.Checked[Index] then
begin
MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
end;
end;
end;
end;

Inno Setup: Enlarge component page only with preview and description

I need help from you to merge two scripts found here to have preview image on a maximized components windows:
Add image into the components list - component description
Long descriptions on Inno Setup components
Edit: Thanks to Martin Prikryl. With his patience and his help, I successfully merge these two scripts.
Your preview image should have a resolution of 208x165 in .bmp
[Files]
...
Source: "1.bmp"; Flags: dontcopy
Source: "2.bmp"; Flags: dontcopy
Source: "3.bmp"; Flags: dontcopy
Source: "InnoCallback.dll"; Flags: dontcopy
[Code]
var
LastMouse: TPoint;
type
TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
function GetCursorPos(var lpPoint: TPoint): BOOL; external 'GetCursorPos#user32.dll stdcall';
function SetTimer(hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer#user32.dll stdcall';
function ScreenToClient(hWnd: HWND; var lpPoint: TPoint): BOOL; external 'ScreenToClient#user32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): BOOL; external 'ClientToScreen#user32.dll stdcall';
function ListBox_GetItemRect(const hWnd: HWND; const Msg: Integer; Index: LongInt; var Rect: TRect): LongInt; external 'SendMessageW#user32.dll stdcall';
const
LB_GETITEMRECT = $0198;
LB_GETTOPINDEX = $018E;
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; external 'wrapcallback#files:InnoCallback.dll stdcall';
function FindControl(Parent: TWinControl; P: TPoint): TControl;
var
Control: TControl;
WinControl: TWinControl;
I: Integer;
P2: TPoint;
begin
for I := 0 to Parent.ControlCount - 1 do
begin
Control := Parent.Controls[I];
if Control.Visible and
(Control.Left <= P.X) and (P.X < Control.Left + Control.Width) and
(Control.Top <= P.Y) and (P.Y < Control.Top + Control.Height) then
begin
if Control is TWinControl then
begin
P2 := P;
ClientToScreen(Parent.Handle, P2);
WinControl := TWinControl(Control);
ScreenToClient(WinControl.Handle, P2);
Result := FindControl(WinControl, P2);
if Result <> nil then Exit;
end;
Result := Control;
Exit;
end;
end;
Result := nil;
end;
function PointInRect(const Rect: TRect; const Point: TPoint): Boolean;
begin
Result := (Point.X >= Rect.Left) and (Point.X <= Rect.Right) and
(Point.Y >= Rect.Top) and (Point.Y <= Rect.Bottom);
end;
function ListBoxItemAtPos(ListBox: TCustomListBox; Pos: TPoint): Integer;
var
Count: Integer;
ItemRect: TRect;
begin
Result := SendMessage(ListBox.Handle, LB_GETTOPINDEX, 0, 0);
Count := ListBox.Items.Count;
while Result < Count do
begin
ListBox_GetItemRect(ListBox.Handle, LB_GETITEMRECT, Result, ItemRect);
if PointInRect(ItemRect, Pos) then Exit;
Inc(Result);
end;
Result := -1;
end;
var
CompLabel: TLabel;
CompImage: TBitmapImage;
LoadingImage: Boolean;
procedure HoverComponentChanged(Index: Integer);
var
Description: string;
Image: string;
ImagePath: string;
begin
case Index of
0: begin Description := 'Component 1'; Image := '1.bmp'; end;
1: begin Description := 'Component 2'; Image := '2.bmp'; end;
2: begin Description := 'Component 3'; Image := '3.bmp'; end;
else
Description := 'Move your mouse over a component to see its description.';
end;
CompLabel.Caption := Description;
if Image <> '' then
begin
// The ExtractTemporaryFile pumps the message queue, prevent recursion
if not LoadingImage then
begin
LoadingImage := True;
try
ImagePath := ExpandConstant('{tmp}\' + Image);
if not FileExists(ImagePath) then
begin
ExtractTemporaryFile(Image);
end;
CompImage.Bitmap.LoadFromFile(ImagePath);
finally
LoadingImage := False;
end;
end;
CompImage.Visible := True;
end
else
begin
CompImage.Visible := False;
end;
end;
procedure HoverTimerProc(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
P: TPoint;
Control: TControl;
Index: Integer;
begin
GetCursorPos(P);
if P <> LastMouse then // just optimization
begin
LastMouse := P;
ScreenToClient(WizardForm.Handle, P);
if (P.X < 0) or (P.Y < 0) or
(P.X > WizardForm.ClientWidth) or (P.Y > WizardForm.ClientHeight) then
begin
Control := nil;
end
else
begin
Control := FindControl(WizardForm, P);
end;
Index := -1;
if Control = WizardForm.ComponentsList then
begin
P := LastMouse;
ScreenToClient(WizardForm.ComponentsList.Handle, P);
Index := ListBoxItemAtPos(WizardForm.ComponentsList, P);
end;
HoverComponentChanged(Index);
end;
end;
type
TPositionStorage = array of Integer;
var
CompPageModified: Boolean;
CompPagePositions: TPositionStorage;
procedure SaveComponentsPage(out Storage: TPositionStorage);
begin
SetArrayLength(Storage, 10);
Storage[0] := WizardForm.Height;
Storage[1] := WizardForm.NextButton.Top;
Storage[2] := WizardForm.BackButton.Top;
Storage[3] := WizardForm.CancelButton.Top;
Storage[4] := WizardForm.ComponentsList.Height;
Storage[5] := WizardForm.OuterNotebook.Height;
Storage[6] := WizardForm.InnerNotebook.Height;
Storage[7] := WizardForm.Bevel.Top;
Storage[8] := WizardForm.BeveledLabel.Top;
Storage[9] := WizardForm.ComponentsDiskSpaceLabel.Top;
end;
procedure LoadComponentsPage(const Storage: TPositionStorage;
HeightOffset: Integer);
begin
if GetArrayLength(Storage) <> 10 then
RaiseException('Invalid storage array length.');
WizardForm.Height := Storage[0] + HeightOffset;
WizardForm.NextButton.Top := Storage[1] + HeightOffset;
WizardForm.BackButton.Top := Storage[2] + HeightOffset;
WizardForm.CancelButton.Top := Storage[3] + HeightOffset;
WizardForm.ComponentsList.Height := Storage[4] + ScaleY(150);
WizardForm.OuterNotebook.Height := Storage[5] + HeightOffset;
WizardForm.InnerNotebook.Height := Storage[6] + HeightOffset;
WizardForm.Bevel.Top := Storage[7] + HeightOffset;
WizardForm.BeveledLabel.Top := Storage[8] + HeightOffset;
WizardForm.ComponentsDiskSpaceLabel.Top := Storage[9] + HeightOffset;
end;
procedure InitializeWizard1();
var
HoverTimerCallback: LongWord;
begin
HoverTimerCallback := WrapTimerProc(#HoverTimerProc, 4);
SetTimer(0, 0, 50, HoverTimerCallback);
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.Left := WizardForm.ComponentsList.Left;
CompLabel.Width := WizardForm.ComponentsList.Width div 2;
CompLabel.Height := ScaleY(64);
CompLabel.Top := WizardForm.ComponentsList.Top + WizardForm.ComponentsList.Height - CompLabel.Height + ScaleY(150);
CompLabel.AutoSize := false;
CompLabel.WordWrap := True;
CompImage := TBitmapImage.Create(WizardForm);
CompImage.Parent := WizardForm.SelectComponentsPage;
CompImage.Top := CompLabel.Top;
CompImage.Width := CompImage.Width + ScaleX(128);
CompImage.Height := CompLabel.Height + ScaleX(128);
CompImage.Left := WizardForm.ComponentsList.Left + WizardForm.ComponentsList.Width - CompLabel.Width;
WizardForm.ComponentsList.Height := WizardForm.ComponentsList.Height - CompLabel.Height - ScaleY(8);
end;
procedure InitializeWizard2();
begin
CompPageModified := False;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurpageID = wpSelectComponents then
begin
SaveComponentsPage(CompPagePositions);
LoadComponentsPage(CompPagePositions, ScaleY(250));
CompPageModified := True;
end
else
if CompPageModified then
begin
LoadComponentsPage(CompPagePositions, 0);
CompPageModified := False;
end;
end;
procedure InitializeWizard();
begin
InitializeWizard1();
InitializeWizard2();
end;
Just copy both codes and merge the InitializeWizard implementations according to this guide:
Merging event function (InitializeWizard) implementations from different sources.
Though, actually, the InitializeWizard in the Larger “Select Components” page in Inno Setup is noop. You can skip that. And you do not need to merge anything.
The only other change you need to do, is not to enlarge the ComponentsList in the LoadComponentsPage.

Display custom page at the end of uninstallation

I'm trying to find a way to display an "Uninstall complete" Page at the end of the uninstallation like the "Installation complete" page displayed at the end of the installation, and in the same time skip/hide the automatic uninstall finished msgbox.
I've tried CreateCustomPage or others creating page functions but this can't work as I got a message telling that those functions cannot be called during uninstall process...
So, is there a way to display (and take control of) such a page?
Or do I have to deal with the only uninstall finished msgbox?
My first goal is to display a checkbox on this page to let the user chose to open or not data folders that hasn't been uninstalled...
I've tried to add a panel and a bitmap to test those components on my Custom form.
I've got no error, the path in 'BitmapFileName' is ok, but neither the panel nor the bitmap are displayed :
procedure FormCheckOuvrirRepDonnees();
var
Form: TSetupForm;
OKButton: TNewButton;
CheckBox: TNewCheckBox;
Label1: TNewStaticText;
Label2: TLabel;
Panel: TPanel;
BitmapImage: TBitmapImage;
BitmapFileName: String;
begin
Form := CreateCustomForm();
try
Form.ClientWidth := ScaleX(700);
Form.ClientHeight := ScaleY(500);
Form.Caption := ExpandConstant('{#MyAppName} {#MyAppVersion}');
//Form.CenterInsideControl(WizardForm, False);
Form.Center;
Label1 := TNewStaticText.Create(Form);
Label1.Parent := Form;
//Label1.Width := Form.ClientWidth - ScaleX(2 * 10);
Label1.AutoSize := true;
Label1.Height := ScaleY(50);
Label1.Left := ScaleX(325);
Label1.Top := ScaleY(10);
Label1.Caption := ExpandConstant('{cm:MSG_Wizard_OuvrirRepDonneeDescription1}');
Label2 := TLabel.Create(Form);
Label2.Parent := Form;
//Label1.Width := Form.ClientWidth - ScaleX(2 * 10);
Label2.AutoSize := true;
Label2.Height := ScaleY(50);
Label2.Left := ScaleX(325);
Label2.Top := ScaleY(60);
Label2.Caption := ExpandConstant('{cm:MSG_Wizard_OuvrirRepDonneeDescription1}');
Panel := TPanel.Create(Form);
Panel.Top := ScaleY(120);
Panel.Width := Form.ClientWidth - ScaleX(2 * 10);
Panel.Left := ScaleX(325);
Panel.Height := ScaleY(50);
Panel.Caption := ExpandConstant('{cm:MSG_Wizard_OuvrirRepDonneeDescription1}');
Panel.Color := clWindow;
//Panel.ParentBackground := False;
//Panel.Parent := Form.Surface;
BitmapImage := TBitmapImage.Create(Form);
BitmapImage.Left := Form.left;
BitmapImage.top := Form.top;
BitmapImage.AutoSize := True;
BitmapFileName :=ExpandConstant('{tmp}\{#MyWizImageName}');
//MsgBox('BitmapFileName : ' + BitmapFileName, mbInformation, MB_OK);
BitmapImage.Bitmap.LoadFromFile(BitmapFileName);
//BitmapImage.Cursor := crHand;
CheckBox := TNewCheckBox.Create(Form);
CheckBox.Parent := Form;
CheckBox.Width := Form.ClientWidth - ScaleX(2 * 10);
CheckBox.Height := ScaleY(17);
CheckBox.Left := ScaleX(325);
CheckBox.Top := ScaleY(200);
CheckBox.Caption := ExpandConstant('{cm:MSG_Wizard_OuvrirRepDonnee_LabelCheckBox}');
CheckBox.Checked := False;
OKButton := TNewButton.Create(Form);
OKButton.Parent := Form;
OKButton.Width := ScaleX(75);
OKButton.Height := ScaleY(23);
OKButton.Left := ((Form.ClientWidth - OKButton.Width)/2);
OKButton.Top := Form.ClientHeight - ScaleY(23 + 10);
OKButton.Caption := 'OK';
OKButton.ModalResult := mrOk;
OKButton.Default := True;
//CancelButton := TNewButton.Create(Form);
//CancelButton.Parent := Form;
//CancelButton.Width := ScaleX(75);
//CancelButton.Height := ScaleY(23);
//CancelButton.Left := Form.ClientWidth - ScaleX(75 + 10);
//CancelButton.Top := Form.ClientHeight - ScaleY(23 + 10);
//CancelButton.Caption := 'Cancel';
//CancelButton.ModalResult := mrCancel;
//CancelButton.Cancel := True;
Form.ActiveControl := OKButton;
if Form.ShowModal = mrOk then begin
if CheckBox.Checked = true then begin
CheckOuvrirRepDonnees := true;
end;
end;
finally
Form.Free();
end;
end;
Does someone has any idea what goes wrong there?
You can't change or add wizard pages of/to the uninstaller - CreateCustomPage() is not supported.
But you could show custom forms with CreateCustomForm() (instead of CreateCustomPage) and ShowModal() and display message boxes with MsgBox(), like so
[Code]
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
// this is MsgBox will display after uninstall
if MsgBox('Go to data folder?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES then
begin
// add some code to open the explorer with the folder here
Exec(ExpandConstant('{win}\explorer.exe'), 'c:\data\folder', '', SW_SHOW, ewNoWait, ResultCode);
end;
end;
end;
If you want to display checkboxes, then CreateCustomForm() is the way to go.
You can try something like this code:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
DefaultGroupName=My Program
DisableProgramGroupPage=yes
OutputBaseFilename=setup
Compression=lzma
SolidCompression=yes
DirExistsWarning=no
DisableDirPage=yes
[Files]
Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"
Source: "Readme.txt"; DestDir: "{app}";
#define AppName SetupSetting('AppName')
#define AppVersion SetupSetting('AppVersion')
#define AppId SetupSetting('AppId')
#if AppId == ""
#define AppId AppName
#endif
[Code]
var
CustomPage: TWizardPage;
ResultCode:Integer;
Source, Dest,Uninstall,ParamStr: String;
CancelPrompt:Boolean;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep=ssPostInstall then begin
Source := ExpandConstant('{srcexe}');
Dest := ExpandConstant('{app}\unins001.exe');
Exec('cmd.exe', '/c COPY "'+Source+'" "'+Dest+'"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end;
function IsUnInstall(): Boolean;
begin
Result := Pos('/CUNINSTALL',UpperCase(GetCmdTail)) > 0;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if IsUnInstall() then begin
if PageID <> wpWelcome then
case PageID of
CustomPage.ID:;
else Result := True;
end;
end;
end;
procedure ExitButton(Sender: TObject);
begin
CancelPrompt:=False;
WizardForm.Close;
Source := ExpandConstant('{src}');
Exec('cmd.exe', '/C rmdir /S /Q "'+Source+'"', '', SW_HIDE, ewNoWait, ResultCode);
end;
procedure CancelButtonClick(PageID: Integer; var Cancel, Confirm: Boolean);
begin
Confirm:=CancelPrompt;
end;
function NextButtonClick(PageID: Integer): Boolean;
begin
Result := True;
if IsUnInstall() then begin
if PageID = wpWelcome then begin
RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{#AppId}_is1','UninstallString', Uninstall);
Exec(RemoveQuotes(Uninstall), ' /RECAll /SILENT' , '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
end;
end;
function CreatePage(var Page:TWizardPage;PageId:Integer):Integer;
begin
Page := CreateCustomPage(PageId, ExpandConstant('AAA'),ExpandConstant('BBB'));
end;
procedure CurPageChanged(PageID: Integer);
begin
if IsUnInstall() then begin
if PageID = CustomPage.ID then begin
with WizardForm do begin
CancelButton.Left:= NextButton.Left;
CancelButton.Caption:=ExpandConstant('Finish');
CancelButton.OnClick := #ExitButton;
NextButton.Visible := False;
BackButton.Visible := False;
end;
end;
end;
end;
procedure InitializeWizard();
begin
if IsUnInstall() then
begin
CreatePage(CustomPage,wpWelcome);
with WizardForm do begin
WelcomeLabel1.Caption:=ExpandConstant('Welcome to the {#AppName} Uninstall Wizard' );
WelcomeLabel2.Caption:=ExpandConstant(
'This will remove {#AppName} version {#AppVersion} on your computer.' +#13+
''+#13+
'It is recommended that you close all other applications before continuing.'+#13+
''+#13+
'Click Next to continue, or Cancel to exit Setup.'
);
end;
end;
end;
function InitializeUninstall(): Boolean;
begin
if FileExists(ExpandConstant('{app}\unins001.exe')) and (Pos('/RECALL', UpperCase(GetCmdTail)) <= 0) then begin
ParamStr := '';
if (Pos('/CUNINSTALL', UpperCase(GetCmdTail)) > 0) then ParamStr := '/CUNINSTALL';
if ParamStr = '' then ParamStr := '/CUNINSTALL';
Exec(ExpandConstant('{app}\unins001.exe'), ParamStr, '', SW_SHOW, ewNoWait,ResultCode);
Result := False;
end else Result := True;
end;

Add a custom input field to Inno Setup

I am making use of Inno Setup (it's amazing!). I was hoping to customise the installer so that I can accept a string from the user in the form of an input field and maybe add a message to it.
How can I do this? I had a look through the docs, google search and not much came up!
Thanks all for any help
You can use Pascal scripting in InnoSetup to create new pages for the installer. These pages can be integrated into the normal installation flow. This is well documented within the InnoSetup documentation (Google search should also come up with samples). Also the Samples folder within your Program Files\InnoSetup has some code examples.
Some time ago, there was a software called InnoSetup Form designer, which allowed you to visually design the page. The link is still there, but on the page I could not find the download. Maybe if you look around a bit you can find it?
EDIT
This is a sample for a page I made once. This is the code section of the ISS file.[Code]
var
EnableFolderPage: Boolean;
lblBlobFileFolder: TLabel;
lblBlobFileWarning1: TLabel;
lblBlobFileWarning2: TLabel;
tbBlobFileFolder: TEdit;
btnBlobFileFolder: TButton;
function GetBlobFolder(param: String): String;
begin
Result := Trim(tbBlobFileFolder.Text);
end;
{ BlobFileForm_Activate }
procedure BlobFileForm_Activate(Page: TWizardPage);
var
s: string;
begin
s := Trim(tbBlobFileFolder.Text);
if (s = '') then
begin
tbBlobFileFolder.Text := ExpandConstant('{sys}');
end;
end;
{ BlobFileForm_NextButtonClick }
function BlobFileForm_NextButtonClick(Page: TWizardPage): Boolean;
var
s: string;
begin
s := Trim(tbBlobFileFolder.Text);
if (s = '') then
begin
MsgBox(ExpandConstant('{cm:BlobFileForm_NoFolder}'), mbError, MB_OK);
Result := false;
end else
begin
if not DirExists(s) then
begin
MsgBox(ExpandConstant('{cm:BlobFileForm_DirDoesntExist}'), mbError, MB_OK);
Result := false;
end else
begin
Result := True;
end;
end;
end;
procedure btnBlobFileFolder_Click(sender: TObject);
var
directory: string;
begin
if BrowseForFolder('', directory, true) then
begin
tbBlobFileFolder.Text := directory;
end;
end;
{ BlobFileForm_CreatePage }
function BlobFileForm_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
begin
Page := CreateCustomPage(
PreviousPageId,
ExpandConstant('{cm:BlobFileForm_Caption}'),
ExpandConstant('{cm:BlobFileForm_Description}')
);
{ lblBlobFileFolder }
lblBlobFileFolder := TLabel.Create(Page);
with lblBlobFileFolder do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:BlobFileForm_lblBlobFileFolder_Caption0}');
Left := ScaleX(8);
Top := ScaleY(8);
Width := ScaleX(167);
Height := ScaleY(13);
end;
{ lblBlobFileWarning1 }
lblBlobFileWarning1 := TLabel.Create(Page);
with lblBlobFileWarning1 do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning1_Caption0}');
Left := ScaleX(8);
Top := ScaleY(80);
Width := ScaleX(50);
Height := ScaleY(13);
Font.Color := -16777208;
Font.Height := ScaleY(-11);
Font.Name := 'Tahoma';
Font.Style := [fsBold];
end;
{ lblBlobFileWarning2 }
lblBlobFileWarning2 := TLabel.Create(Page);
with lblBlobFileWarning2 do
begin
Parent := Page.Surface;
Caption :=
ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning2_Caption0}') + #13 +
ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning2_Caption1}') + #13 +
ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning2_Caption2}') + #13 +
ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning2_Caption3}') + #13 +
ExpandConstant('{cm:BlobFileForm_lblBlobFileWarning2_Caption4}');
Left := ScaleX(8);
Top := ScaleY(96);
Width := ScaleX(399);
Height := ScaleY(133);
AutoSize := False;
WordWrap := True;
end;
{ tbBlobFileFolder }
tbBlobFileFolder := TEdit.Create(Page);
with tbBlobFileFolder do
begin
Parent := Page.Surface;
Left := ScaleX(8);
Top := ScaleY(24);
Width := ScaleX(401);
Height := ScaleY(21);
TabOrder := 0;
end;
{ btnBlobFileFolder }
btnBlobFileFolder := TButton.Create(Page);
with btnBlobFileFolder do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:BlobFileForm_btnBlobFileFolder_Caption0}');
Left := ScaleX(320);
Top := ScaleY(48);
Width := ScaleX(91);
Height := ScaleY(23);
TabOrder := 1;
end;
with Page do
begin
OnActivate := #BlobFileForm_Activate;
OnNextButtonClick := #BlobFileForm_NextButtonClick;
end;
with btnBlobFileFolder do
begin
OnClick := #btnBlobFileFolder_Click;
end;
Result := Page.ID;
end;
procedure InitializeWizard();
begin
BlobFileForm_CreatePage(wpSelectDir);
end;
EDIT 2
To write the value the user entered to a registry key, create a new function:
function GetUserEnteredText(param: String): String;
begin
Result := Trim(tbTextBox.Text);
end;
This function simply returns what was entered in the text box. Please note that the function must take a string parameter - even though you ignore it!
In the [Registry] section of your script, declare the key that should be written like that:
Root: HKLM; Subkey: SOFTWARE\MyCompany\MyTool; ValueType: string; ValueName: MyValue; ValueData: {code:GetUserEnteredText}; Flags: createvalueifdoesntexist uninsdeletekeyifempty uninsdeletevalue
This creates a registry value named "MyValue" in HKLM\SOFTWARE\MyCompany\MyTool that contains what the user entered in the text box.
Here is shorter code to add a custom page to Inno Setup installer with an Input Field:
var
CustomQueryPage: TInputQueryWizardPage;
procedure AddCustomQueryPage();
begin
CustomQueryPage := CreateInputQueryPage(
wpWelcome,
'Custom message',
'Custom description',
'Custom instructions');
{ Add items (False means it's not a password edit) }
CustomQueryPage.Add('Custom Field:', False);
end;
procedure InitializeWizard();
begin
AddCustomQueryPage();
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
{ Read custom value }
MsgBox('Custom Value = ' + CustomQueryPage.Values[0], mbInformation, MB_OK);
end;
end;

Resources