I have an Inno-Setup installation with the following entry in the [Registry] section
Root: HKCU; Subkey: SOFTWARE\MyCompany\MyApp; ValueName: MyKey; Flags: uninsdeletekey noerror
Which as stated by the flags is to be deleted upon uninstall.
But I need it to be preserved in case the uninstall is due to version upgrade.
How can it be done? Check maybe?
Thanks.
You will need to pass the information that you want to preserve the key to the uninstaller since it doesn't know who executes it (well, you would not need to pass this information explicitly if you'd find the parent process programatically, but it's a rather hacky way).
A reliable solution is telling this to the uninstaller by the command line parameter. One implementation of an uninstaller which will conditionally delete a registry key is shown in the following example:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
#define MyAppRegKey "SOFTWARE\MyCompany\MyApp"
[Registry]
; no uninsdeletekey flag must be used here
Root: HKCU; Subkey: "{#MyAppRegKey}"; Flags: noerror
Root: HKCU; Subkey: "{#MyAppRegKey}"; ValueType: string; ValueName: "MyKey"; ValueData: "MyValue"; Flags: noerror
[Code]
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
// if we are at the post uninstall step and the uninstaller wasn't executed with
// the /PRESERVEREGKEY parameter, delete the key and all of its subkeys (here is
// no error handling used, as your original script ignores all the errors by the
// noerror flag as well)
if (CurUninstallStep = usPostUninstall) and not CmdLineParamExists('/PRESERVEREGKEY') then
RegDeleteKeyIncludingSubkeys(HKCU, '{#MyAppRegKey}');
end;
If you run unistaller of such setup with the /PRESERVEREGKEY command line parameter, the registry key will be preserved, deleted otherwise, e.g.:
unins000.exe /PRESERVEREGKEY
But I can't think of a real usage of the above other than that you are sharing a registry key between two different applications. Applications installed by a different setup (e.g. a setup with different AppId).
Note that there's no need to uninstall the previous version of your application in case of update (in case when you're installing a new version of the setup with the same AppId). If you will follow this rule, Inno Setup will perform an update and your registry key will be preserved until you uninstall the application.
Related
I have configured "Inno Setup" as needed, but there is one point which I'm not able to modify.
We have an application which is installed on a network folder on the customers server. Basically updating the files works as expected.
If some client on the network, someone which has not initiated the update, has opened the application I'm receiving an messagebox/errorbox "delete file failed.... file is in use by another process" - including 3 buttons "try again", "skip this files (not recommended)", "cancel installation". I have attached an example picture (found on the internet, but the same message).
There you can see the 3 buttons. I want to disable "skip this file" and "cancel installation". The user should have the ability to hit "try again" - they should get stucked until all client have closed their application.
I do not think you can alter that message box.
There are two alternatives:
You can delete the target file using your own code before installing the new version, not allowing to proceed until the file is gone.
[Files]
Source: "Source: "C:\SampleApplicationData\*"; DestDir: "{app}\SampleApp"; \
Flags: recursesubdirs createallsubdirs; BeforeInstall: ForcedDelete
[Code]
procedure ForcedDelete;
var
FileName: string;
Deleted: Boolean;
begin
FileName := ExpandConstant(CurrentFileName);
repeat
if not FileExists(FileName) then
begin
Log(Format('File %s does not exist', [FileName]));
Deleted := True;
end
else
if DeleteFile(FileName) then
begin
Log(Format('File %s was deleted', [FileName]));
Deleted := True;
end
else
begin
MsgBox(Format('Error deleting %s.', [FileName]), mbError, MB_OK);
Deleted := False;
end;
until Deleted;
end;
Though I still believe the code should at least allow an abort.
Or use restartreplace flag.
I've made custom pages to manage specific redist tools install depending on the user choice.
Those tools are linked to checkboxes checked by the user if he wants or not install those tools.
Then come a page only there to show the user the progression of the installation of each tool.
The issue I have here is that the progress page is shown only when first ExtractTemporaryFile of the setups for the tools is done, showing the last page as if it has frozen.
The only way I have to let the progress page be shown before the ExtractTemporaryFile happens is to put a MsgBox before any install function.
But even in this case, when the ExtractTemporaryFile is launched, the progress bar animation is frozen until the ExtractTemporaryFile is done...
Here is the part of the code doing this:
procedure CurPageChanged(CurPageID: Integer);
begin
If CurPageID=PageInstallationPersonnalisee.ID then
begin
ProgressBarLabelPageInstPerso.Caption := 'Initialisation...';
if InstallTool1 = True then
begin
ProgressBarLabelPageInstPerso.Caption := 'Installing InstallTool1...';
F_InstallTool1();
end;
if InstallTool2 = True then
begin
ProgressBarLabelPageInstPerso.Caption := 'Installing InstallTool2...';
F_InstallTool2();
end;
if InstallTool3 = True then
begin
ProgressBarLabelPageInstPerso.Caption := 'Installing InstallTool3...';
F_InstallTool3();
end;
ProgressBarPageInstPerso.Style := npbstMarquee;
//ProgressBarPageInstPerso.Style := npbstNormal;
ProgressBarPageInstPerso.Position := 100;
CancelWithoutPrompt:=True;
WizardForm.Close;
end;
end;
Note that ExtractTemporaryFile() is made in each F_InstallTooln() function.
Others part of Setup and File Sections that could help:
[Setup]
SolidCompression=no
[Files]
;Temporary redists
Source: "{#MyRessourcesPath}InstallTool1_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy
Source: "{#MyRessourcesPath}InstallTool2_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy
Source: "{#MyRessourcesPath}InstallTool3_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy
Here, the page PageInstallationPersonnalisee is not shown until first ExtractTemporaryFile is done...
I'm aware that ExtractTemporaryFile can cause some delay in install process, but why should it cause the wizard to freeze?
So my question is: in my scenario, is there a way to force the wizard refreshing so that he shows up before any ExtractTemporaryFile procedure is launched?
The ExtractTemporaryFile really hangs the wizard form. As most code does.
The only custom page that allows forcing Windows message queue to get pumped is the TOutputProgressWizardPage (created by the CreateOutputProgressPage).
You can do something like this:
function NextButtonClick(CurPageID: Integer): Boolean;
var
ProgressPage: TOutputProgressWizardPage;
begin
if CurPageID = wpReady then
begin
ProgressPage := CreateOutputProgressPage('Preparing installations', '');
ProgressPage.Show;
try
ProgressPage.Msg1Label.Caption := 'Installing 1 ...';
ProgressPage.SetProgress(0, 100);
ExtractTemporaryFile('1.exe');
Exec(...);
ProgressPage.Msg1Label.Caption := 'Installing 2 ...';
ProgressPage.SetProgress(33, 100);
ExtractTemporaryFile('2.exe');
Exec(...);
ProgressPage.Msg1Label.Caption := 'Installing 3 ...';
ProgressPage.SetProgress(66, 100);
ExtractTemporaryFile('3.exe');
Exec(...);
ProgressPage.SetProgress(100, 100);
ProgressPage.Hide;
finally
end;
end;
Result := True;
end;
Though it does not work really well either on modern versions of Windows that have fancy progress bar with animation, if you cannot call the SetProgress frequently. Note that the SetProgress call is what does pump the message queue behind the scenes. So it makes sense to call it even when its parameter do not change. But you cannot, as the ExtractTemporaryFile blocks.
Alternatively, you can leave the deployment to the [Files] section and have the installers be executed from the AfterInstall event.
[Files]
;Temporary redists
Source: "{#MyRessourcesPath}InstallTool1_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy; AfterInstall: Install1
Source: "{#MyRessourcesPath}InstallTool2_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy; AfterInstall: Install2
Source: "{#MyRessourcesPath}InstallTool3_Setup.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall noencryption dontcopy; AfterInstall: Install3
I know this is an old thread but I was facing a similar situation where some of the files I was extracting using the ExtractTemporaryFile function were significantly slower than the others.
After some investigation I found this on Inno Setup help pages:
When solid compression is enabled, be sure to list your temporary files at (or near) the top of the [Files] section. In order to extract an arbitrary file in a solid-compressed installation, Setup must first decompress all prior files (to a temporary buffer in memory). This can result in a substantial delay if a number of other files are listed above the specified file in the [Files] section.
This means that for optimal performance you should move the files that you want to extract using that function to the top of the [Files] section.
In the Inno Setup wpInstalling page how can I prevent the initial extraction of the files as defined in the [Files] section from setting the progress bar to (almost) 100%?
My installation script mainly consists of installing a number of third party installation files from the '[Run]' section. Example below:
[Run]
Filename: "{tmp}\vcredist_x86-2010-sp1.exe"; Parameters: "/q /norestart"; \
Check: InstallVCRedist; \
BeforeInstall: UpdateProgress(10, 'Installing Microsoft Visual C++ 2010 x86 Redistributable - 10.0.40219...');
Filename: "{tmp}\openfire_3_8_1.exe"; Check: InstallOpenFire; \
BeforeInstall: UpdateProgress(25, 'Installing OpenFire 3.8.1...');
Filename: "{tmp}\postgresql-8.4.16-2-windows.exe"; \
Parameters: "--mode unattended --unattendedmodeui none --datadir ""{commonappdata}\PostgreSQL\8.4\data"" --install_runtimes 0"; \
Check: InstallPostgreSQL; \
BeforeInstall: UpdateProgress(35, 'Installing PostgreSQL 8.4...'); \
AfterInstall: UpdateProgress(50, 'Setting up database...');
The installation of these third party components takes longer than any other part of the install (by far) but unfortunately the progress bar goes from 0% to close to 100% during the initial extraction of these files. I then can reset the progress bar to an amount of my choosing by using the following procedure:
procedure UpdateProgress(Position: Integer; StatusMsg: String);
begin
WizardForm.StatusLabel.Caption := StatusMsg;
WizardForm.ProgressGauge.Position :=
Position * WizardForm.ProgressGauge.Max div 100;
end;
Ideally however I'd prefer the initial extraction to instead go from 0–10% (approx.) as that would more closely represent what is actually happening.
Is there any event to capture the progression of the extraction of the files or alternatively a way to prevent or block the extraction of the files from updating the progress bar?
You have to increase the WizardForm.ProgressGauge.Max.
But unfortunately there's no event that happens after the Inno Setup sets its initial maximum.
You can abuse the BeforeInstall parameter of the first installed file though.
And then in the [Run] section, use the AfterInstall to progress the bar.
This expands on my answer to Inno Setup: How to manipulate progress bar on Run section?
[Files]
Source: "vcredist_x86-2010-sp1.exe"; DestDir: "{tmp}"; \
BeforeInstall: SetProgressMax(10)
Source: "openfire_3_8_1.exe"; DestDir: "{tmp}"
[Run]
Filename: "{tmp}\vcredist_x86-2010-sp1.exe"; AfterInstall: UpdateProgress(55);
Filename: "{tmp}\openfire_3_8_1.exe"; AfterInstall: UpdateProgress(100);
[Code]
procedure SetProgressMax(Ratio: Integer);
begin
WizardForm.ProgressGauge.Max := WizardForm.ProgressGauge.Max * Ratio;
end;
procedure UpdateProgress(Position: Integer);
begin
WizardForm.ProgressGauge.Position :=
Position * WizardForm.ProgressGauge.Max div 100;
end;
Similar to this question:
How to set the progress bar value in the [Run] section of the Inno Setup install script?
When the Inno Setup gets to the [Run] section, the progress bar shows at 100% and stops in this position.
I have many files that I install in this Run section, which I wish to restart the progress bar and control it, as it goes installing each program.
The status message is easy to change (StatusMsg), but the progress I'm missing something. Could you guys help me out, please?
Example:
[Run]
Filename: "msiexec.exe"; Parameters: "/i ""msxml.msi"" /quiet"; \
StatusMsg: "MSXML..."; Flags: runascurrentuser
Filename: "msiexec.exe"; Parameters: "/i ""capicom_dc_sdk.msi"" /quiet"; \
StatusMsg: "CAPICOM..."; Flags: runascurrentuser
Since I want to control the progress bar during it's installation, I don't know what to do. I thought in maybe using BeforeInstall parameter, creating a code to set the progress bar to 0 by doing something like WizardForm.ProgressGauge.Position = 0; and in the AfterInstall parameter, the opposite, WizardForm.ProgressGauge.Position = 100;, but how to change during the installation?
Thanks.
It would be rather difficult to update the progress bar, while another process is running.
I do not see a point of endeavoring it, as you are unlikely able to tell the progress of the sub-installer, so you won't know what to update the progress bar to.
Except for special cases, when the sub-installer provides an API to report its progress.
For an example, see:
Inno Setup Get progress from .NET Framework 4.5 (or higher) installer to update progress bar position or
Make Inno Setup Installer report its installation progress status to master installer.
To update the progress bar according to number of sub-installers finished, you can do:
[Run]
FileName: "process1"; BeforeInstall: SetProgress(0); AfterInstall: SetProgress(33)
FileName: "process2"; AfterInstall: SetProgress(66)
FileName: "process3"; AfterInstall: SetProgress(100)
[Code]
procedure SetProgress(Position: Integer);
begin
WizardForm.ProgressGauge.Position :=
Position * WizardForm.ProgressGauge.Max div 100;
end;
To divide part of the progress range for installing files and the rest to running the sub-installers, see
Inno Setup - Prevent extraction of files from setting progress bar to 100%
Another option is to use a "marquee" (= infinite) progress bar style.
See Progress bar control styles.
[Run]
FileName: "process1"; BeforeInstall: SetMarqueeProgress(True)
FileName: "process2"
FileName: "process3"; AfterInstall: SetMarqueeProgress(False)
[Code]
procedure SetMarqueeProgress(Marquee: Boolean);
begin
if Marquee then
begin
WizardForm.ProgressGauge.Style := npbstMarquee;
end
else
begin
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;
Works even on Windows XP, despite not being listed in the official Microsoft documentation anymore. Tested on Windows XP SP3.
As an addition to Martin Prikryl answer (https://stackoverflow.com/a/34349900):
When this style should be set on the uninstall form, the call looks slightly different:
procedure SetMarqueeProgressUninstall(Marquee: Boolean);
begin
if Marquee then
begin
UninstallProgressForm.ProgressBar.Style := npbstMarquee;
end
else
begin
UninstallProgressForm.ProgressBar.Style := npbstNormal;
end;
end;
This can be easily called from the InitializeUninstallProgressForm event and the original one from InitializeWizard if there are no files to be installed at all.
I set environment variable in the registry using InnoSetup:
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "MY_PROGRAM_HOME_DIR"; ValueData: "{app}"
But system cannot see this variable until I call SendMessage.
[Code]
procedure DeinitializeSetup();
begin
// HWND_BROADCAST = $FFFF
// WM_SETTINGCHANGE = $001A
SendMessage($FFFF, $001A, 0, Longint(PChar('Environment')));
end;
InnoSetup says:
... Column 60: Type mismatch
How do I correctly typecast PChar into Longint in InnoSetup script?
Use the ChangesEnvironment directive instead of doing the same from your script code. From the reference:
When set to yes, at the end of the installation Setup will notify
other running applications (notably Windows Explorer) that they should
reload their environment variables from the registry.
In InnoSetup, when you use the above directive, the following code is called inside:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
LPARAM(PChar('Environment')), SMTO_ABORTIFHUNG, 5000, MsgResult);
Use the ChangesEnvironment
Work fine for me.
Ex:
[Setup]
ChangesEnvironment=yes
Be careful:
The broadcast message occur before the call of 'DeinitializeSetup'