TNewCheckListBox Checked not being updated - inno-setup

I'm trying to write an installer in Inno Setup that includes a page where a TNewCheckListBox is created and then used to populate a selection of choices on the next page:
var
ListBox: TNewCheckListBox;
SelectedConfigs: array of Integer;
function ConfigurationPage(): Integer;
var
Page: TWizardPage;
I: Integer;
begin
Page := CreateCustomPage(wpSelectComponents, 'Select Configuration',
'Please select the configuration you would like to install to.');
ListBox := TNewCheckListBox.Create(Page);
ListBox.Parent := Page.Surface;
ListBox.Left := ScaleX(0);
ListBox.Top := ScaleY(0);
ListBox.Width := Page.SurfaceWidth;
ListBox.Height := Page.SurfaceHeight;
ListBox.BorderStyle := bsNone;
for I := 0 to GetArrayLength(ConfigurationNames) - 1 do
begin
ListBox.AddCheckBox(ConfigurationNames[I], '', 0, False, True, False, False, nil);
end;
ListBox.ItemIndex := 0;
Result := Page.ID;
end;
procedure PortSelectionPage(PageID: Integer);
var
Page: TWizardPage;
ArrayLength: Integer;
I: Integer;
begin
Page := CreateCustomPage(PageID, 'Select Port',
'Please select the port you want the configurations to receive on.');
for I := 0 to ListBox.Items.Count - 1 do
begin
Log(Format('ListBox[%d], %s is checked? %d', [I, ListBox.Items[I], ListBox.Checked[I]]));
if ListBox.Checked[I] then
begin
ArrayLength := GetArrayLength(SelectedConfigs);
SetArrayLength(SelectedConfigs, ArrayLength + 1);
SelectedConfigs[ArrayLength] := I;
Log(Format('Config %d was selected...', [I]));
end;
end;
end;
procedure InitializeWizard;
var
PageID: Integer;
begin
PageID := ConfigurationPage;
PortSelectionPage(PageID);
end;
The problem I'm having is that, regardless of the selections I make in the ConfigurationPage procedure, the SelectedConfigs array is not being updated and my debugging messages are showing that none of the options are selected. Before creating the PortSelectionPage procedure, the code lived on the CurStepChanged event handler so I'm not sure if the issue is with my having moved the code to a different page or if there's something else going on here. Do I need to force an update to the component or should I be using event handlers instead? If so, how would I implement one for this usecase?

Your whole code is executed from InitializeWizard event function. Hence even before the installer wizard is shown.
You have to query the checkbox states only after the user changes them. For example, from some of these events:
NextButtonClick
CurStepChanged
CurPageChanged

Related

Custom page with radio buttons for one define

I would like to create a custom Inno Setup page that will be displayed at the very beginning of installation, with 3 radio buttons to select from. That selection should set the value in #define BinVer. Currently I'm forced to make 3 separate installers, but I prefer to have just one with such feature. I'm setting it up like that for each exe:
#define BinVer "111111", in another compilation I change it to #define BinVer "222222", and finaly I have #define BinVer "333333" for the third compilation. I'm using this define in such situations li ke that:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
case CurUninstallStep of
usPostUninstall:
begin
CreateDir(ExpandConstant('{app}\bin\{#BinVer}\files'));
end;
end;
end;
or
Source: "{#CompPath}\Folder\sed.exe"; DestDir: "{app}\bin\{#BinVer}\files\gui\unbound\"; Flags: deleteafterinstall
So far I have tried something like this, but it's just very beginning, not sure what to do next, I'm not that good with programming it:
[Code]
procedure InitializeWizard();
var
Page: TInputOptionWizardPage;
begin
Page := CreateInputOptionPage(wpWelcome, '', '', '', False, False);
Page.AddEx('Radio button 1', 0, True);
Page.AddEx('Radio button 2', 0, True);
Page.AddEx('Radio button 3', 0, True);
end;
Also tried something like that (but I don't know if this is correct way to achieve this, it's not even compiling without errors):
[Code]
var
BinVer: string;
function InitializeSetup(): Boolean;
var
Page: TInputOptionWizardPage;
begin
Page := CreateInputOptionPage(wpWelcome, 'Select BinVer', 'Select BinVer', '', False, False);
Page.Add('BinVer 1', '111111');
Page.Add('BinVer 2', '222222');
Page.Add('BinVer 3', '333333');
Page.DefaultValueIndex := 0; // initially select the first radio button
Result := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
Page: TInputOptionWizardPage;
begin
Result := True;
if CurPageID = wpWelcome then
begin
Page := GetInputOptionPage(wpWelcome);
case Page.SelectedValueIndex of
0: BinVer := '111111';
1: BinVer := '222222';
2: BinVer := '333333';
end;
end;
end;
You cannot change a value of a preprocessor variable (used on compile-time) on runtime.
But you do not have to. You want to use the value on runtime from Pascal Code. And you already have the value stored to runtime Pascal variable (BinVer: string). So use that:
CreateDir(ExpandConstant('{app}\bin\' + BinVer + '\files'));
Though you do not really need to store the value to the global variable either. You can query the "InputOptionPage" when needed.
Example of full code, linking the selection to the [Files] section entry destination:
[Files]
Source: "{#CompPath}\Folder\sed.exe"; \
DestDir: "{app}\bin\{code:GetBinVer}\files\gui\unbound\"; \
Flags: deleteafterinstall
[Code]
var
InputOptionPage: TInputOptionWizardPage;
procedure InitializeWizard();
begin
InputOptionPage := CreateInputOptionPage(wpWelcome, '', '', '', False, False);
InputOptionPage.AddEx('Radio button 1', 0, True);
InputOptionPage.AddEx('Radio button 2', 0, True);
InputOptionPage.AddEx('Radio button 3', 0, True);
// initially select the first radio button
InputOptionPage.SelectedValueIndex := 0;
end;
function GetBinVer(Param: string): string;
begin
case InputOptionPage.SelectedValueIndex of
0: BinVer := '111111';
1: BinVer := '222222';
2: BinVer := '333333';
else RaiseException('Unexpected selection');
end;
end;
(not tested)
Though linking it to a code in CurUninstallStepChanged is more complicated, that happens in another (uninstaller) process. So you will have to store the selection/BinVer somewhere (e.g. Windows Registry), where you will load it from in the uninstaller. You might by able to use SetPreviousData/GetPreviousData.

Inno Setup: ensure that the user have read the Information page

I have an Information page, the page that that is activated with infobefore file:
[Setup]
InfoBeforeFile=infobefore.txt
I want to:
Add a checkbox (or a couple of checkboxes), that the user has to check to show that he has paid attention to the information.
The user should only be allowed to proceed when he checks the checkboxes right. I think of disabling the Next button or showing a messagebox, whatever is easier.
Is there an easy way to do this?
Just add a new checkbox on the InfoBeforePage page. And update the NextButton state based on the check box state.
[Setup]
InfoBeforeFile=infobefore.txt
[Code]
var
InfoBeforeCheck: TNewCheckBox;
procedure CheckInfoBeforeRead;
begin
{ Enable the NextButton only if InfoBeforeCheck is checked or }
{ installer is running in the silent mode }
WizardForm.NextButton.Enabled := InfoBeforeCheck.Checked or WizardSilent;
end;
procedure InfoBeforeCheckClick(Sender: TObject);
begin
{ Update state of the Next button, whenever the InfoBeforeCheck is toggled }
CheckInfoBeforeRead;
end;
procedure InitializeWizard();
begin
InfoBeforeCheck := TNewCheckBox.Create(WizardForm);
InfoBeforeCheck.Parent := WizardForm.InfoBeforePage;
{ Follow the License page layout }
InfoBeforeCheck.Top := WizardForm.LicenseNotAcceptedRadio.Top;
InfoBeforeCheck.Left := WizardForm.LicenseNotAcceptedRadio.Left;
InfoBeforeCheck.Width := WizardForm.LicenseNotAcceptedRadio.Width;
InfoBeforeCheck.Height := WizardForm.LicenseNotAcceptedRadio.Height;
InfoBeforeCheck.Caption := 'I swear I read this';
InfoBeforeCheck.OnClick := #InfoBeforeCheckClick;
{ Make the gap between the InfoBeforeMemo and the InfoBeforeCheck the same }
{ as the gap between LicenseMemo and LicenseAcceptedRadio }
WizardForm.InfoBeforeMemo.Height :=
((WizardForm.LicenseMemo.Top + WizardForm.LicenseMemo.Height) -
WizardForm.InfoBeforeMemo.Top) +
(InfoBeforeCheck.Top - WizardForm.LicenseAcceptedRadio.Top);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpInfoBefore then
begin
{ Initial state of the Next button }
CheckInfoBeforeRead;
end;
end;

Inno Setup: CreateInputQueryPage isn't returning any value

Here is the script I'm using on Inno Setup. It is my first script, please understand if I'm asking something obvious.
It seems that the variable ServerAddress never has a value, even if I fill the input field. It looks like Page.Values[0] always returns an empty result. What is wrong with my code?
As you can see, I have made a test with a testvar variable to exclude it was a matter of variable scope, but it's not the case.
[Code]
var
Page: TInputQueryWizardPage;
ServerAddress: String;
testvar: String;
procedure InitializeWizard();
begin
Page := CreateInputQueryPage(wpWelcome,
'Server Informations', '',
'Please specify the IP address, then click Next.');
{ Add items (False means it's not a password edit) }
Page.Add('IP Address:', False);
ServerAddress := Page.Values[0];
testvar := 'testvalue';
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then begin
MsgBox(ExpandConstant('{app} '+testvar+' : '+ServerAddress),mbInformation,MB_OK);
SaveStringToFile(ExpandConstant('{app}')+'\config.txt', 'test'+ServerAddress, True);
end;
end;
The InitializeWizard event function is called (and finishes) before the wizard window is even shown.
So a value (that the user will enter in the future) can hardly be known at that point. You have to read the value only after the custom page is shown. Like in your CurStepChanged(ssPostInstall):
procedure CurStepChanged(CurStep: TSetupStep);
var
ServerAddress: string;
begin
if CurStep = ssPostInstall then
begin
{ This is the right time to read the value }
ServerAddress := Page.Values[0];
SaveStringToFile(ExpandConstant('{app}') + '\config.txt', ServerAddress, True);
end;
end;

Correct code for opening a file with an external editor

Is the following code it right or not and, if it is wrong, please correct it.
Note: I want to open the file with "WordPad.exe" not with "Microsoft Office Word" until if "Microsoft Office Word" is the default program.
My code:
function InitializeSetup: Boolean;
var
S: AnsiString;
begin
// Show the contents of Readme.txt (non Unicode) in a message box
ExtractTemporaryFile('Info.rtf');
Result := True;
end;
procedure AboutButtonOnClick(Sender: TObject);
var
ErrorCode: Integer;
begin
ShellExec('open', ExpandConstant('{tmp}\Info.rtf'), '', '', SW_SHOWNORMAL, ewNoWait,
ErrorCode);
end;
ShellExec('open','Documentname'....); will open with the program associated with the extension of the file. If there is no program associated it will prompt you to select which program you want to view it with.
You could look for WordPad.exe and if it's found you could call ShellExec using the WordPad.EXE directly. Then pass the documentName as a parameter.
Updated with function to do this
procedure OpenDocumentInWordPad(Document : String);
var
WordPad : String;
ErrorCode : Integer;
begin
// Typical Location on XP and later.
WordPad := ExpandConstant('{pf}') + '\Windows NT\Accessories\WordPad.exe'
// Find word pad
if Not FileExists(WordPad) then
begin
// Location in Windows 95/98
WordPad := ExpandConstant('{pf}') + '\Accessories\WordPad.exe'
if Not FileExists(WordPad) then
begin
// Fall back to anything associated with document.
WordPad := Document;
Document := '';
end;
end;
if not ShellExec('open',WordPad,Document,'',SW_SHOW,ewNoWait,ErrorCode) then
begin
MsgBox(SysErrorMessage(ErrorCode),mbError,MB_OK);
end;
end;

InnoSetup: need to add validate URL by adding test button on CreateInputQueryPage

I have on CreateInputQueryPage, in that box user will type URL...Now i want to have Test button next to that text box so i can ping and validate that URL...is that possible ?
Using the Standard Support Functions, you are unable to do this.
You can call an external dll, which would allow you to do this.
You can also call COM interfaces, which would also allow you do to this.
Specifically you could use WinHTTPRequest COM object to perform this check.
You have a couple of options to deal with the validation. Both are demonstrated in the following script. The first is to create a button just for validation, the 2nd is to hook the Next Button and have it occur automatically before moving to the next page.
[Setup]
AppName='Test Script'
AppVerName='Test Script'
DefaultDirName={pf}\test
[Code]
const
InputQueryPageID = 100; //Determined by watching in Debugger.
var
Page : TInputQueryWizardPage;
procedure ClickEvent(Sender : TObject);
begin
MsgBox('Could Validate Here',mbInformation,MB_OK);
end;
procedure InitializeWizard();
var
Button : TButton;
begin
Page := CreateInputQueryPage(wpWelcome,
'Add URL', 'Test2','Test3');
Page.Add('URL:', False);
Button := TButton.Create(Page);
Button.Parent := Page.Surface;
Button.Caption := 'Button Text';
Button.Top := 100;
Button.Left := 10;
Button.OnClick := #ClickEvent;
end;
function NextButtonClick(CurrPageID: Integer) : Boolean;
begin
case CurrPageID of
InputQueryPageID : begin
MsgBox('Could Validate Here',mbInformation,MB_OK);
result := true; // Results of EXE Validation
end;
else result := true;
end;
end;

Resources