We package few other thirdparty softwares along with our installer,and also install them during
the installation of our product. We install them in silent mode and capture their exit codes,
so sotimes, they get installed successfully and give exitcode as "3010" which is reboot required.
So, in those cases we want to show reboot page at the last but want to give a custom message.
what is the best way to show the custom message on the finish page?
[Messages]
#if FileExists("c:\RebootFile.txt")==0
FinishedRestartLabel=To complete the installation of ConditionalMessageOnWizard, Setup must restart your computer. Would you like to restart now?
#else
FinishedRestartLabel=Reboot Required
#endif
I am using the above code, but i am unable to use the dynamic paths like {sd} or {tmp} for fileexists function.
Can anyone help?
After clarification of your problem, we've found that you actually want to check if a certain file exixts and conditionally change the FinishedLabel caption at runtime.
The #emit, or in short # starting statements are used by preprocessor. And preprocessing runs right before the compilation. It allows you to conditionally modify the script, which is, after this process is done, compiled. So, with your above script you are in fact checking, if the file c:\RebootFile.txt exists on the machine where the setup is being compiled and depending on the result it then chooses value of the FinishedRestartLabel message. But it never compiles both texts into the setup binary.
You can modify the FinishedLabel caption from code e.g. this way. There you can expand constants without any problem:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
function NeedRestart: Boolean;
begin
Result := True;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if not FileExists(ExpandConstant('{sd}\RebootFile.txt')) then
WizardForm.FinishedLabel.Caption := 'RebootFile NOT found. Restart ?'
else
WizardForm.FinishedLabel.Caption := 'RebootFile WAS found. Restart ?';
end;
Related
At the end of an installation I need to run a Pascal function that updates a flight simulator .cfg file (called .ini file in Inno Setup). The Pascal function exists in its [Code] section and runs correctly. I would like to run this Pascal function in the [Run] section using StatusMsg to tell the user what is going on.
[Run]
Filename: {code:FsxEditSceneryFile|Add#<scenerySpec>}; StatusMsg: "Add scenery to FSX";
; <scenerySpec> is just a place holder of the actual scenery specification!
All works as expected with the exception that Inno Setup forces me to use a string as return value of the Pascal function. However the Filename statement requires a Boolean as return value to specify if the execution was successful (True) or failed (False). This type mismatch produces an error message box at the end of the execution of the Filename statement saying
CreateProcess failed; Code 87. Wrong Parameter.
Any proposal how this could be solved? I know that event functions exist that I could use e.g. CurStepChanged() but I find the StatusMsg mechanism very nice to tell the user what is done by the installation.
You are abusing Filename parameter resolution to execute some code. It's undocumented when the parameter value is resolved. This makes your approach unreliable. You cannot know that the value is resolved, while the StatusMsg is displaying. And moreover, the value must resolve to an executable path anyway. And Inno Setup will try to execute it (hence the error). What you probably do not want. Do not do that.
Instead, as you already suggested, use the CurStepChanged. You can display the status message from Pascal code by accessing the WizardForm.StatusLabel.
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
WizardForm.StatusLabel.Caption := 'Installing something...';
{ Install something }
end;
end;
In case somebody else is stumbling across this problem, here's a solution that will allow you to run pascal script procedures "in place" of any statement in the [RUN] section.
I needed to move the execution of a powershell script to the [CODE] section, since the [RUN] section doesn't provide a way of reacting to the exit code of external cmd programs.
As I've got a bunch of sub installers that I need to execute in a particular order within my setup, it's really handy to have the option to control the position of the pascal script within the [RUN] section and not just when entering the [RUN] sections setup step.
It works by using a "dummy" program (like ping) as Filename for the [RUN] statement, then using the build-in BeforeInstall parameter to call the pascal script procedure containing the actual execution logic.
[RUN] section
Filename: "ping"; BeforeInstall: RunSQLSetupScript; Components: "thirds\db"; StatusMsg: "Installing SQL Server ..." ; Flags: runhidden
Pascal script procedure containing the actual execution logic
(Note that {tmp}\{#SQLServerInstallScript} would be the actual path to the script in this example, as it is generally advised to avoid hard coded paths and use constants + the temporary directory instead.)
// runs the SQL setup script and terminates the setup process, if the script returns an exit code that indicates an error
procedure RunSQLSetupScript();
var
runSuccess: Boolean;
retVar: Integer;
msgText: String;
begin
runSuccess := ShellExec('', 'powershell.exe', '-NoProfile -File ' + ExpandConstant('{tmp}\{#SQLServerInstallScript}'), '', SW_SHOW, ewWaitUntilTerminated, retVar);
// the external script will return an exit code > 0 if an error occurred
if (runSuccess = False) or (retVar > 0) then
begin
msgText := 'SQL Server setup script returned error code ' + IntToStr(retVar) + ', indicating an unsuccessful installation of SQL Server. Setup will now terminate.';
MsgBox(msgText, mbCriticalError, MB_OK);
// => further handle error case here, like cancelling the running setup or log the issue etc.
end;
end;
What would cause the message:
Cannot call "WIZARDISCOMPONENTSELECTED" function during Uninstall.
It has to be here:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
if WizardIsComponentSelected('mycomponent') then
begin
DoSomething(False);
end;
end;
end;
How do you determine if the component was selected at uninstall time? It has to undo some stuff before uninstalled. Typically Inno Setup has ways to get if something was installed or not.
There's no API in Pascal Script to tell in the uninstaller, what tasks were selected when installing. Probably because, there might have been multiple installations, each with different set of tasks. So it would be questionable, what set of tasks the API should report.
What you should (and need) to do is to check the effect of the task. Like checking if a specific file or a specific Registry key exists.
Though note that Tasks parameters still work in the uninstaller sections [UninstallDelete] and [UninstallRun]. So you can make use of them.
See also Inno Setup Uninstall some components only
At the end of an installation I need to run a Pascal function that updates a flight simulator .cfg file (called .ini file in Inno Setup). The Pascal function exists in its [Code] section and runs correctly. I would like to run this Pascal function in the [Run] section using StatusMsg to tell the user what is going on.
[Run]
Filename: {code:FsxEditSceneryFile|Add#<scenerySpec>}; StatusMsg: "Add scenery to FSX";
; <scenerySpec> is just a place holder of the actual scenery specification!
All works as expected with the exception that Inno Setup forces me to use a string as return value of the Pascal function. However the Filename statement requires a Boolean as return value to specify if the execution was successful (True) or failed (False). This type mismatch produces an error message box at the end of the execution of the Filename statement saying
CreateProcess failed; Code 87. Wrong Parameter.
Any proposal how this could be solved? I know that event functions exist that I could use e.g. CurStepChanged() but I find the StatusMsg mechanism very nice to tell the user what is done by the installation.
You are abusing Filename parameter resolution to execute some code. It's undocumented when the parameter value is resolved. This makes your approach unreliable. You cannot know that the value is resolved, while the StatusMsg is displaying. And moreover, the value must resolve to an executable path anyway. And Inno Setup will try to execute it (hence the error). What you probably do not want. Do not do that.
Instead, as you already suggested, use the CurStepChanged. You can display the status message from Pascal code by accessing the WizardForm.StatusLabel.
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
WizardForm.StatusLabel.Caption := 'Installing something...';
{ Install something }
end;
end;
In case somebody else is stumbling across this problem, here's a solution that will allow you to run pascal script procedures "in place" of any statement in the [RUN] section.
I needed to move the execution of a powershell script to the [CODE] section, since the [RUN] section doesn't provide a way of reacting to the exit code of external cmd programs.
As I've got a bunch of sub installers that I need to execute in a particular order within my setup, it's really handy to have the option to control the position of the pascal script within the [RUN] section and not just when entering the [RUN] sections setup step.
It works by using a "dummy" program (like ping) as Filename for the [RUN] statement, then using the build-in BeforeInstall parameter to call the pascal script procedure containing the actual execution logic.
[RUN] section
Filename: "ping"; BeforeInstall: RunSQLSetupScript; Components: "thirds\db"; StatusMsg: "Installing SQL Server ..." ; Flags: runhidden
Pascal script procedure containing the actual execution logic
(Note that {tmp}\{#SQLServerInstallScript} would be the actual path to the script in this example, as it is generally advised to avoid hard coded paths and use constants + the temporary directory instead.)
// runs the SQL setup script and terminates the setup process, if the script returns an exit code that indicates an error
procedure RunSQLSetupScript();
var
runSuccess: Boolean;
retVar: Integer;
msgText: String;
begin
runSuccess := ShellExec('', 'powershell.exe', '-NoProfile -File ' + ExpandConstant('{tmp}\{#SQLServerInstallScript}'), '', SW_SHOW, ewWaitUntilTerminated, retVar);
// the external script will return an exit code > 0 if an error occurred
if (runSuccess = False) or (retVar > 0) then
begin
msgText := 'SQL Server setup script returned error code ' + IntToStr(retVar) + ', indicating an unsuccessful installation of SQL Server. Setup will now terminate.';
MsgBox(msgText, mbCriticalError, MB_OK);
// => further handle error case here, like cancelling the running setup or log the issue etc.
end;
end;
After creating my setup.exe I have to pack it for various software deployment tools. Therefore I can't call the setup.exe with parameters, instead I have placed my own parameters in a setup.ini file next to the setup.exe
[Code]
var
MyIniFile: String;
function InitializeSetup(): Boolean;
var
LoadFromIniFile: String;
begin
Result := true;
MyIniFile := ExpandConstant('{srcexe}'); //writes the full path of the setup.exe in "MyIniFile"
MyIniFile := Copy(MyIniFile, 1, Length(MyIniFile) - Length(ExtractFileExt(MyIniFile))) + '.ini'; //changes the ".exe" into ".ini"
if FileExists(MyIniFile) then LoadFromIniFile := MyIniFile; //checks wether there is a ini-file
if LoadFromIniFile <> '' then begin
MyLogFile := GetIniString('Setup', 'Log', MyLogFile , LoadFromIniFile);
ProductName := GetIniString('Setup', 'ProductName', ProductName, LoadFromIniFile);
end;
end;
Now I want to also place the so called "Setup Command Line Parameters" (listed on the Inno Setup Help site) in my ini-file. I think that there is a way for the /Dir="x:\dirname parameter, which I did not figure out yet. But I also want to have the /SILENT parameter in there, do you think there is a way to do this? If yes, how would you do this? If not, can you please give me a hint why not?
So customize your installer for different products, I'd recommend you to use a pre-processor and automatically build the installer for each product (with different "defines"), instead of using an external INI file.
For example to be able to change application name and resulting executable when building the installer, use a script like:
[Setup]
AppName={#AppName}
OutputBaseFilename={#BaseFilename}
Now you can create two different installers automatically using command-line:
ISCC.exe Example1.iss /dAppName=App1 /dBaseFilename=SetupApp1
ISCC.exe Example1.iss /dAppName=App2 /dBaseFilename=SetupApp2
Regarding the implicit silent installation:
There's no API other than the command-line /SILENT switch to trigger silent installation.
But you can create a near-silent installation by disabling most installer pages:
[Setup]
DisableWelcomePage=true
DisableDirPage=true
DisableProgramGroupPage=true
DisableReadyPage=true
DisableFinishedPage=true
Actually the above example disables all default pages. But Inno Setup compiler will ignore the DisableReadyPage=true, if all other previous pages are disabled.
You may want to choose a different page to show instead. For example a Welcome page (by omitting DisableWelcomePage=true, but keeping the DisableReadyPage=true).
If you do not mind about using external files (as you already use an external INI file), you can of course wrap the installer to a batch file and call the installer with the /SILENT switch.
Is there some way to set the DefaultDirName by code depending on some decission a user did on installtion?
Let me comment:
I have some code which is build for two different systems (using different interops/ocx's and such stuff). My input files are stored in two directories input\A and input\B.
I want to have only one setup-file for both systems.
In the setup file i use CreateInputOptionPage with 2 options to determin which files to install (using Check on each file). This works okay.
But i do also have some ShellExec on finish of setup, which at the moment uses {app} to e.g. register some .Net classes and ShellExec to unregister the .Net classes on InitializeUninstall (also uses {app})
The setup must install the software on two different locations (depending on the selection of the user (eg. c:\software_a or c:\software_b). Can't change this.
So is there some way to specify the DefaultDirName before the files get copied to the system, so i can use the same ShellExec on install and uninstall? I could of course add the same ShellExec for both systems on installtation and use an if to check which files to register (depending on the user selection) but on uninstall i would not have this information (user selection), so i can not unregister the .Net classes.
thanks
In your CreateInputOptionPage code section, you might be able to set a value then use that value in the code snippet below. I haven't tested it but it might work.
[Setup]
DefaultDirName={code:getpath}
[Code]
function GetPath( Default: string ): string;
begin
if (CreateInputOptionPageValue1) then
Result := ExpandConstant({sd}) + '\path1';
else
Result := ExpandConstant({sd}) + '\path2';
end;
If you need to change the install folder after the DefaultDirName has been initialized, this was working for me quite well:
procedure CurPageChanged(CurPageID: Integer);
begin
{ updates the install path depending on the install type or the entered suffix }
if CurPageID = wpSelectDir then begin
WizardForm.DirEdit.Text := ExpandConstant('{pf}') + '\MyAppName' + GetAppSuffix('');
end;
end;
Cheers
Chris