Decide which files to install in Inno Setup based on registry value - inno-setup

I am preparing a setup file. What I want to do is auto select a component by comparing a registry key version and the user should not be able to modify these auto selected components.
Is there anyone who can help me on this issue, it would be greatly appreciated.
There will be five components and first two components should be auto-selected by the setup file comparing a registry version (I mean the setup file will decide which of the first two components will be choose and install.) and the other three components will be selected by the user, if the user want to install the additional files.
Or Is there any other way to install some files by comparing a registry key? So the setup file can automatically decide which files needs to be installed.
Let's say;
[Components]
Name: "FeatureA"; Description: "Feature A"
Name: "FeatureB"; Description: "Feature B"
Name: "FeatureC"; Description: "Feature C"
if RegKeyExists(HKEY_LOCAL_MACHINE,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B8D22EE6-1S55-48BD-8B6F-B8961847F657}_is1') then
begin<br />
RegQueryStringValue(HKEY_LOCAL_MACHINE,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B8D22EE6-1S55-48BD-8B6F-B8961847F657}_is1', 'DisplayVersion', Version); then
if the version > 2.5.0.0 then
auto select "FeatureA"
else
if the version < 2.4.0.0 then
auto select "FeatureB"

To install files under certain conditions only, use the Check parameter:
[Files]
Source: FeatureA.dll; DestDir: {app}; Check: ShouldInstallFeatureA
Source: FeatureB.dll; DestDir: {app}; Check: ShouldInstallFeatureB
[Code]
function GetVersion(var Version: string): Boolean;
begin
Result :=
RegQueryStringValue(
HKEY_LOCAL_MACHINE,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{B8D22EE6-1S55-48BD-8B6F-B8961847F657}_is1',
'DisplayVersion', Version);
end;
function ShouldInstallFeatureA: Boolean;
var
Version: string;
begin
Result :=
GetVersion(Version) and
(CompareVersion(Version, '2.5.0.0') > 0);
end;
function ShouldInstallFeatureB: Boolean;
var
Version: string;
begin
Result :=
GetVersion(Version) and
(CompareVersion(Version, '2.4.0.0') < 0);
end;
For the CompareVersion function, see Compare version strings in Inno Setup.

Related

Inno Setup: Force component to have always same check status as it's parent

My component section:
[Components]
Name: "client"; Description: "Client"; Types: full default monitor;
Name: "server"; Description: "Server"; Types: full default server;
Name: "server/feature"; Description: "Optional feature";
Name: "server/Db"; Description: "Always supported server feature"; Types: full default server; Flags: fixed
Name: "server/Db/latest"; Description: "Latest"; Types: full default server; Flags: exclusive
Name: "server/Db/2_10"; Description: "2.10"; Flags: exclusive
I would like to have component server/Db that is always selected when its parent server is selected. I was able to acomplish it with fixed flag, but then I'm not able to select server/Db/2_10. If I tried to select second exclusive component, server/Db is unsellected altogether.
I can only select server/Db/2_10 if server/Db isn't flaged as fixed, but then I'm able to unselect it.
Edit:
I added procedures like in Inno Setup: how to auto select a component if another component is selected?. Just changed ComponentsListCheckChanges procedure like this, (because I don't want to allow user to uncheck it):
procedure ComponentsListCheckChanges;
begin
// No metter what you chceck/uncheck force component if needed.
if WizardIsComponentSelected('server') then
begin
WizardSelectComponents('server/Db');
end
else
begin
WizardSelectComponents('!server/Db');
end
end;
I just found out, that If I try to uncheck server/Db, its checked back, that's good, but Type change to Custom even if it match other predefined type. Did I made some mistakes?
Old:
I also tried to mark it as Disabled in code. I was able to select both exclusive components, but not able to Uncheck component server anymore.
[code]
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
WizardForm.ComponentsList.ItemEnabled[3] := False;
end;
I'm able to create same behavior in component section as with code above by adding custom to Types list. I'm able to select both exclusive components, but can't uncheck server
[Components]
Name: "client"; Description: "Client"; Types: full default monitor;
Name: "server"; Description: "Server"; Types: full default server;
Name: "server/feature"; Description: "Optional feature";
Name: "server/Db"; Description: "Always supported server feature"; Types: full default server custom; Flags: fixed
Name: "server/Db/latest"; Description: "Latest"; Types: full default server; Flags: exclusive
Name: "server/Db/2_10"; Description: "2.10"; Types: custom; Flags: exclusive
Last thing that I tried was setting it to True in code if server is checked. I Tried using code from: https://stackoverflow.com/a/36989894/2416941 , it forced server/Db to behave like I wanted but, in dropdown menu above components, Selected Type stopped changing, It always showed as Default instalation and not Custom or any other type, so I don't think that's a valid solution.
How can I force server/Db to be always checked with server and able to choose one of server/Db's children, and still possible to uncheck server?
Or is there other way to group exclusive components?
I made it works corectly by editing answers for Inno Setup: how to auto select a component if another component is selected? but had to add ComponentsListClickCheckPrev(nil); to Update Type selection corectly after calling WizardSelectComponents.
[Code]
var
TypesComboOnChangePrev: TNotifyEvent;
ComponentsListClickCheckPrev: TNotifyEvent;
procedure ComponentsListCheckChanges;
begin
{ If Check status of components is different }
{ Update child's status with parent's status. }
if WizardIsComponentSelected('server') <> WizardIsComponentSelected('server/Db') then
begin
if WizardIsComponentSelected('server') then { If checked parent }
begin
WizardSelectComponents('server/Db'); { Check component }
end
else
begin
WizardSelectComponents('!server/Db'); { Uncheck component }
end;
ComponentsListClickCheckPrev(nil); { Update Type selection after my edits }
end;
end;
procedure ComponentsListClickCheck(Sender: TObject);
begin
{ First let Inno Setup update the components selection }
ComponentsListClickCheckPrev(Sender);
{ And then check for changes }
ComponentsListCheckChanges;
end;
procedure TypesComboOnChange(Sender: TObject);
begin
{ First let Inno Setup update the components selection }
TypesComboOnChangePrev(Sender);
{ And then check for changes }
ComponentsListCheckChanges;
end;
procedure InitializeWizard();
begin
{ The Inno Setup itself relies on the TypesCombo.OnChange and OnClickCheck. }
{ so we have to preserve their handlers. }
ComponentsListClickCheckPrev := WizardForm.ComponentsList.OnClickCheck;
WizardForm.ComponentsList.OnClickCheck := #ComponentsListClickCheck;
TypesComboOnChangePrev := WizardForm.TypesCombo.OnChange;
WizardForm.TypesCombo.OnChange := #TypesComboOnChange;
end;

Inno Setup "Select directory" page disabled/skipped when uninstalling an older version first

I have a Inno Setup that runs the uninstaller of the older version if it was installed. In case that the uninstall process takes place the directory page is disabled what leads to an internal error because constant app is not initialized.
The new version (1.3) is no more uninstallable, but I need to run the uninstaller if versions 1.1 or 1.2 where installed previous.
Since this is a addon for an existing program i need to find it's directory. I try to extract this information from the registry and save information about the addon installation in the registry, too. This is where i find the uninstaller path in case that the previous version was installed and run it before i continue with the install process
[Setup]
…
DefaultDirName={commonpf32}\LucasArts\Star Wars Battlefront II\GameData\
AppendDefaultDirName=no
…
Uninstallable=no
DisableWelcomePage=no
DirExistsWarning=no
DisableProgramGroupPage=yes
…
[Registry]
Root: HKCU; Subkey: "Software\GTAnakin"; Flags: uninsdeletekeyifempty
Root: HKCU; Subkey: "Software\GTAnakin\SWBF2REMASTER"; Flags: uninsdeletekey
…
[Code]
…
procedure InitializeWizard;
var
UrlLabel : TNewStaticText;
CancelBtn : TButton;
UninstPath : string;
iResult : integer;
InstallDir : string;
begin
CancelBtn := WizardForm.CancelButton;
UrlLabel := TNewStaticText.Create(WizardForm);
UrlLabel.Top := CancelBtn.Top + (CancelBtn.Height div 2) - (UrlLabel.Height div 2);
UrlLabel.Left := WizardForm.ClientWidth - CancelBtn.Left - CancelBtn.Width;
UrlLabel.Caption := ExpandConstant('{cm:txtHomePageLink}');
UrlLabel.Font.Style := UrlLabel.Font.Style + [fsUnderline];
UrlLabel.Cursor := crHand;
UrlLabel.Font.Color := clHighlight;
UrlLabel.OnClick := #UrlLabelClick;
UrlLabel.Parent := WizardForm;
WizardForm.DirEdit.Text := '';
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\LucasArts\Star Wars Battlefront II\1.0','ExePath', InstallDir) then begin
if FileExists(InstallDir) then begin
InstallDir := ExtractFilePath(InstallDir);
delete(InstallDir,length(InstallDir),1);
WizardForm.DirEdit.Text := InstallDir;
end;
end;
if RegQueryStringValue(HKEY_CURRENT_USER, 'Software\GTAnakin\SWBF2REMASTER', 'UninstPath', UninstPath) then begin
Exec(UninstPath, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResult)
end;
end;
…
I expect that the setup tries to auto detect the install path from the registry and predefines the app constant. The directory page should be displayed always, so the user can check and change the path. But the directory page is only shown if there was no previous version to uninstall. Otherwise the page is skipped and lead to the internal error if the path couldn't be autodetected.
Move your uninstall code to InitializeSetup event function.
InitializeSetup is triggered before the DisableDirPage is directive evaluated (contrary to InitializeWizard that you use currently). And your code belongs there anyway.

Disable controls based on components selection in Inno Setup

I'd like to disable controls on my custom page (VST2DirPage) based on selected components. I have tried the condition:
if IsComponentSelected('VST64') then
begin
VST2DirPage.Buttons[0].Enabled := False;
VST2DirPage.PromptLabels[0].Enabled := False;
VST2DirPage.Edits[0].Enabled := False;
end
But the elements seems to be always disabled, so it looks like it is not getting the correct values to work properly. Script below:
[Types]
Name: "full"; Description: "{code:FullInstall}";
Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom
[Components]
Name: "VST64"; Description: "64-bit VST2"; Types: full; Check: Is64BitInstallMode
Name: "VST"; Description: "32-bit VST2"; Types: full; Check: Is64BitInstallMode
Name: "VST"; Description: "32-bit VST2"; Types: full; Check: not Is64BitInstallMode
[Code]
var VST2DirPage: TInputDirWizardPage;
procedure InitializeWizard;
begin
VST2DirPage := CreateInputDirPage(wpSelectComponents,
'Confirm VST2 Plugin Directory', '',
'Select the folder in which setup should install the VST2 Plugin, then click Next.',
False, '');
VST2DirPage.Add('64-bit folder');
VST2DirPage.Values[0] := ExpandConstant('{reg:HKLM\SOFTWARE\VST,VSTPluginsPath|{pf}\Steinberg\VSTPlugins}');
VST2DirPage.Add('32-bit folder');
VST2DirPage.Values[1] := ExpandConstant('{reg:HKLM\SOFTWARE\WOW6432NODE\VST,VSTPluginsPath|{pf32}\Steinberg\VSTPlugins}');
if not Is64BitInstallMode then
begin
VST2DirPage.Buttons[0].Enabled := False;
VST2DirPage.PromptLabels[0].Enabled := False;
VST2DirPage.Edits[0].Enabled := False;
end;
end;
InitializeWizard event function happens even before the installer is shown. At that point, you do not know yet, what components a user will select.
You have to update a controls' state only when you know what components are selected:
Either when leaving the "Select Components" page.
Or when entering your custom page.
This shows the later approach (implemented using CurPageChanged event function):
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = VST2DirPage.ID then
begin
VST2DirPage.Buttons[0].Enabled := not WizardIsComponentSelected('VST64');
VST2DirPage.PromptLabels[0].Enabled := VST2DirPage.Buttons[0].Enabled;
VST2DirPage.Edits[0].Enabled := VST2DirPage.Buttons[0].Enabled;
end;
end;
Note that the above code not only disables the controls, when the component is selected. It also re-enables them, if user returns back to "Select Components" page and unselects the component.
See also a similar question:
Inno Setup - Change a task description label's color and have a line break.

downloading files over the internet, if the component has been selected (WITH Inno Tools Downloader)

This code actually download me the files and it does not matter whether the selected component is "test" or not. I want those two files download, if you select a component, can do that? I use Inno Inno Setup 5 + Tools Downloader)
[Components]
Name: Dictionaries; Description: "test"; Types: Full; ExtraDiskSpaceRequired: 50;
[Languages]
Name: english; MessagesFile: compiler:Default.isl
#include ReadReg(HKEY_LOCAL_MACHINE,'Software\Sherlock Software\InnoTools\Downloader','ScriptPath','');
[Code]
procedure InitializeWizard();
begin
itd_init;
itd_addfile('http://www.sherlocksoftware.org/petz/files/dogz5.zip',expandconstant('{tmp}\dogz5.zip'));
itd_addfile('http://www.sherlocksoftware.org/petz/files/petz4.zip',expandconstant('{tmp}\petz4.zip'));
itd_downloadafter(wpReady);
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep=ssInstall then begin
filecopy(expandconstant('{tmp}\dogz5.zip'),expandconstant('{app}\dogz5.zip'),false);
filecopy(expandconstant('{tmp}\petz4.zip'),expandconstant('{app}\petz4.zip'),false);
end;
end;
Yes, that's possible. Your are looking for a little helper function called
IsComponentSelected().
It's basically a boolean tester accepting a component name from the [components] and returning the checkbox value (selected=true).
// for a single component
if IsComponentSelected('NameOfTheComponent') then idpAddFile(URL, ...);`
// multiple components with one selection
if IsComponentSelected('dictionaries') then
begin
idpAddFile(URL1, ...);
idpAddFile(URL2, ...);
end;
Comment by TLama:
In which event and where to enqueue the download files?
I would suggest to use the NextButtonClick event with a condition, that the current (CurPage) has to be the component selection screen (wpSelectComponents).
In other words: when you are on the component selection screen and press next, only the selected components are added to the downloader.
The code could look like this:
function NextButtonClick(CurPage: Integer): Boolean;
(*
Called when the user clicks the Next button.
If you return True, the wizard will move to the next page.
If you return False, it will remain on the current page (specified by CurPageID).
*)
begin
if CurPage = wpSelectComponents then
begin
if IsComponentSelected('NameOfTheComponent') then idpAddFile(URL, ...);
end; // of wpSelectComponents
Result := True;
end;
Sidenote: you might switch your download lib to https://code.google.com/p/inno-download-plugin/ This has better features, including decent https support and is actively maintained. InnoTools Download by SherlockSoftware is outdated (2008).

Controlling Custom Page To Show After Specific Tasks

After couple of hours googling and struggling, and got no where, I decide to ask for help here.
What I am trying to accomplish, I am trying to get the IP from the user and use it as a command line parameter in my [Run] Section.
So I have this:
[Task]
Name: "License"; Description: "Usb Key License Driver"; GroupDescription: "UsbLicense"; Flags: checkedonce
Name: "License/Desktop"; Description: "Desktop License"; GroupDescription: "UsbLicense"; Flags: exclusive
Name: "License/NetworkClient"; Description: "Network Client License Key"; GroupDescription: "UsbLicense"; Flags: exclusive unchecked
Now if the user chooses Network client, I want to be able to show a custom page, and get the IP and use it like this:
[Run]
Filename: "{app}\Drivers\Program.exe"; Parameters: "/ip:{code:GetIPhere}"; StatusMsg: "Installing drivers..."; Tasks: License/NetworkClient
I managed to create my own page and ran this:
[Code]
procedure InitializeWizard();
begin
CustomForm_CreatePage(wpSelectDir);
end;
Now for my main question:
1) How do i control WHEN the custom form shows, it shows up before it activates before my Task page.
2) If i can get it to show AFTER task page? How do I write code to make it show under the "Client Network" condition only from task. (If i do this in NextButtonClick method, how do i know what is the page id of my custom page?)
Thanks for all you help, i am just geting so close to finishing my installer, but this is driving me nuts.
----EDITED----
I sovled my problem 1, there is a pageAfter parameter when creating the custom page, and we can use the selectedTask constant to determine it to appear AFTER the Task page:
procedure InitializeWizard();
begin
Form_CreatePage(wpSelectTasks);
end;
Thanks and Regards,
Kev84
You can use the Pascal Script WizardSelectedTasks, which will return you the string of tasks you have created, just do a "Pos" on the returned string and you'll be able to determine if your specific task was chosen.
[code]
function Form_ShouldSkipPage(Page: TWizardPage): Boolean;
var
selectedTask : string;
skipPage : bool;
begin
skipPage := true;
selectedTask := WizardSelectedTasks(false);
if (Pos('client', selectedTask) > 0) then
begin
skipPage := false;
end;
Result := skipPage;
end;

Resources