Inno Setup - Conditional DisableDirPage - installation

Using Inno Setup 5.5.2 I am trying to conditionally skip selection of the installation directory depending on the existence of a path. Specifically, if the 'D:\' drive is available I want installation to it in a predefined location with no prompts, and if it is not available, provide prompts with a reasonable default.
I have code that works for DefaultDirName, but not for DisableDirPage:
[Code]
const
DefaultDrive = 'D:\';
AppFolder = 'SomeDir';
function GetDefaultDir( Param: String ) : String;
begin
if DirExists( DefaultDrive ) then begin
Result := DefaultDrive + AppFolder;
end else begin
Result := ExpandConstant('{pf}\') + AppFolder;
end;
end;
function DefaultDirValid( Param: String ) : Boolean;
begin
Result := DirExists( DefaultDrive );
end;
[Setup]
; Works as expected
DefaultDirName={code:GetDefaultDir}
...
; Compiler Error - Value of [Setup] section directive "DisableDirPage" is invalid.
DisableDirPage={code:DefaultDirValid}
I have tried using functions for DisableDirPage that return Strings of 'yes' and 'no', as well as Integers of 0 and 1. I have also tried inlining the call to DirExists. All have produced the same compiler error.
My best guess is that it has something to do with the fact that DisableDirPage takes a tri-state yes, no, or auto. Is there a specific type associated with the tri-state logic that needs to be returned? The Inno Help on Scripted Constants only says:
The called function must have 1 String parameter named Param, and must return a String or a Boolean value depending on where the constant is used.

Using the ShouldSkipPage event handler you can skip the directory selection page when the DefaultDrive constant path exists with the following script:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={code:GetDefaultDir}
[Code]
const
DefaultDrive = 'D:\';
AppFolder = 'Some Folder';
function GetDefaultDir(Param: string): string;
begin
Result := DefaultDrive + AppFolder;
if not DirExists(DefaultDrive) then
Result := ExpandConstant('{pf}\') + AppFolder;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := (PageID = wpSelectDir) and DirExists(DefaultDrive);
end;

Related

Inno Setup: duplicated InitializeSetup merge [duplicate]

I use this code to ask for a password:
Inno Setup - Move the password page before the welcome page (first page)
And this code for custom language selector:
Inno Setup - Language selector with VCL Styles
When I merge them, it does not work.
I need password before that the language selector, so this is no correct:
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');
Result := AskPassword();
end;
end;
And this way, with an incorrect password the setup continues.
function InitializeSetup(): Boolean;
var
Language: string;
begin
Result := True;
Language := ExpandConstant('{param:LANG}');
if Language = '' then
begin
Result := AskPassword();
Log('No language specified, showing language dialog');
SelectLanguage();
Result := False;
Exit;
end
else
begin
Log('Language specified, proceeding with installation');
end;
end;
Inno Setup 6
Inno Setup 6 has event attributes features that helps solving this problem.
Just make sure that each of your event implementation have an unique name, e.g. appending unique suffix. And add event attribute with the name of the implemented event.
[Code]
function InitializeSetup(): Boolean;
begin
Result := ...
end;
<event('InitializeSetup')>
function InitializeSetup2(): Boolean;
begin
Result := ...
end;
Inno Setup 5
In general, the easiest is to keep both implementations of the event function separate and add one wrapper implementation that call both.
function InitializeSetup1(): 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');
Result := True;
end;
end;
function InitializeSetup2(): Boolean;
begin
Result := AskPassword();
end;
function InitializeSetup(): Boolean;
begin
{ Order the calls the way you want the checks to be performed }
Result :=
InitializeSetup2() and
InitializeSetup1();
end;
For more general discussion of the problem, see
Merging event function (InitializeWizard) implementations from different sources
Though in your specific case, it's more complicated, as you will also need to pass the password from the first instance to the other, similarly to how the language is passed from the first instance to the other.
So actually, the InitializeSetup2 (password) implementation will have to be similar like the InitializeSetup1 (language), not to ask for the password again.
I actually do not really understand, why you complicate the things so much by not asking for language before the password. It would actually make sense. To get a localized password prompt.

Dynamically Disable Welcome Page

I am trying to disable welcome page dynamically :
here are different option i tried
[SETUP]
DisableWelcomePage={code:ShouldSkipAutorun}
...
[CODE]
function ShouldSkipAutorun(Default: string):boolean;
begin
...
// will return true or false on whether to disable it or not
end;
error: "Value of [Setup] section directive "DisableWelcomePage" is invalid"
next I tried using shouldskippage but documentation says
This event function isn't called for the wpWelcome, wpPreparing, and wpInstalling pages, nor for pages that Setup has already determined should be skipped (for example, wpSelectComponents in an install containing no components
any help?
Use ShouldSkipPage function in inno.
Set DisableWelcomePage default value to no in [Setup] section.
[Setup]
DisableWelcomePage=no
Modify your [Code] section as below
[CODE]
function ShouldSkipAutorun():boolean;
begin
Result:=/** add you result here (TRUE or FALSE) **/;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = wpWelcome then
begin
Result := ShouldSkipAutorun;
end;
end;

Recursively searching the registry

I can successfully query for a known Key's value, using the code below. How can I recursively search the subkeys (in my example below, all subkeys within the Uninstall folder) for a particular data's value? My aim is to see if some particular program is installed, and if not, install it.
function
...(omitted)
var
Res : String;
begin
RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{92EA4162-10D1-418A-91E1-5A0453131A38}','DisplayName', Res);
if Res <> 'A Value' then
begin
// Successfully read the value
MsgBox('Success: ' + Res, mbInformation, MB_OK);
end
end;
The principle is easy, with the RegGetSubkeyNames you'll get an array of subkeys of a certain key and then you just iterate this array and query all the subkeys for the DisplayName value and compare the value (if any) with the searched one.
The following function shows the implementation. Note, that I've removed the Wow6432Node node from the path, so if you really need it, modify the UnistallKey constant in the code:
[Code]
const
UnistallKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall';
function IsAppInstalled(const DisplayName: string): Boolean;
var
S: string;
I: Integer;
SubKeys: TArrayOfString;
begin
Result := False;
if RegGetSubkeyNames(HKEY_LOCAL_MACHINE, UnistallKey, SubKeys) then
begin
for I := 0 to GetArrayLength(SubKeys) - 1 do
begin
if RegQueryStringValue(HKEY_LOCAL_MACHINE, UnistallKey + '\' + SubKeys[I],
'DisplayName', S) and (S = DisplayName) then
begin
Result := True;
Exit;
end;
end;
end
else
RaiseException('Opening the uninstall key failed!');
end;

Is it possible to filter/require installation path to be ASCII in InnoSetup?

So I wonder if it is possible to allow user to enter only ASCII as installation path? (warn him and make input path again) (problem is application we install is old and can not work with Cyrillic paths so we need to restrict user on installation stage)
To restrict the user input for the application directory for the Basic Latin character set you may use the following code. The code only checks if any char of the selected directory name doesn't exceed the Basic Latin character set range. If that happens, an error message is shown and the user is forced to stay on the directory selection page. The remaining folder name validation (based on the file system naming conventions) is left on Inno Setup internals, as it already was:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
function IsCharValid(Value: Char): Boolean;
begin
Result := Ord(Value) <= $007F;
end;
function IsDirNameValid(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to Length(Value) do
if not IsCharValid(Value[I]) then
Exit;
Result := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
if (CurPageID = wpSelectDir) and
not IsDirNameValid(WizardForm.DirEdit.Text) then
begin
Result := False;
MsgBox('There is an invalid char in the selected directory name. ' +
'Directory path may contain only chars that are valid for the ' +
'file system naming conventions and only in the range of the ' +
'Basic Latin character set.', mbError, MB_OK);
end;
end;

Inno Setup: Disable components page on upgrade

Is there a way to disable the Components Page for Upgrades? I would like to enable upgrades of my software but I don't want to allow the users to change the selection of components in case of an upgrade.
Instead the installer you upgrade all existing components from the first installation.
I am worried that it the user selects less components during the upgrade those missing components will stay installed as the old version and you get a mess.
I added the following to my script:
[Setup]
DisableDirPage=auto
DisableProgramGroupPage=auto
DirExistsWarning=auto
I just need a way to disable the components page and use the selection of the previous install (full install) for the upgrade. Is that possible?
I have found a related directive:
[Setup]
UsePreviousTasks=true
UsePreviousTasks is reading the existing section out of the registry which is good. Now I need to find a way to hide the selection window.
Thanks,
Wolfgang
To hide a page from user use the ShouldSkipPage event method. If you return True in this method, the page won't be shown to user. If False, the page will be displayed as usually. Here 's an example of how to check if the installation is an upgrade and if so, skip the Select Components wizard page:
[Setup]
AppId=B75E4823-1BC9-4AC6-A645-94027A16F5A5
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
; here is the place for your [Components] section and the rest of your script
[Code]
const
UninstallKey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
function IsUpgrade: Boolean;
var
Value: string;
begin
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := (PageID = wpSelectComponents) and IsUpgrade;
end;
Another option you mentioned might be to disable all the controls of the page. The next script shows as the previous one how to check if the installation is an upgrade and if so, disables all the controls on the Select Components wizard page:
[Setup]
AppId=B75E4823-1BC9-4AC6-A645-94027A16F5A5
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
; here is the place for your [Components] section and the rest of your script
[Code]
const
UninstallKey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
function IsUpgrade: Boolean;
var
Value: string;
begin
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
procedure DisablePageControls(Page: TNewNotebookPage);
var
I: Integer;
begin
Page.Enabled := False;
for I := 0 to Page.ControlCount - 1 do
Page.Controls[I].Enabled := False;
end;
procedure InitializeWizard;
begin
if IsUpgrade then
DisablePageControls(WizardForm.SelectComponentsPage);
end;
The IsUpgrade function mentioned in TLama's answer has a bug. If AppId starts with a "{" which must be doubled, this isn't resolved and they registry key will not be found. Here's a corrected function that works for me:
function IsUpgrade: Boolean;
var
Value: string;
UninstallKey: string;
begin
UninstallKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' +
ExpandConstant('{#SetupSetting("AppId")}') + '_is1';
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
Leave the separate const away for this function, it won't work with that extra function call.
Apart from that, 64-bit systems don't seem to cause any issues. If InnoSetup runs in 32-bit mode, the registry virtualisation is in effect and redirects you to the correct key already.
Something like that:
if CurPageID=wpSelectComponents then
begin
if ExtraOptionAvailable() then
begin
Wizardform.ComponentsList.Checked[6] := true;
Wizardform.ComponentsList.ItemEnabled[6] := true;
end else begin
Wizardform.ComponentsList.Checked[6] := false;
Wizardform.ComponentsList.ItemEnabled[6] := false;
end;
end;

Resources