Inno Setup: Modify icon and title of specific error message - inno-setup

I have a program that should only be installed on 64-bit architectures. So I added:
ArchitecturesAllowed = x64
In my Inno Setup file to prevent this.
The problem is that the message displayed is a bit too "violent". It displays "error" in title and a big red cross.
I've found that I can change content of this message (with OnlyOnTheseArchitectures message), but no way to modify icon and title without impacting other error message.
And I do not see what step can match...
Is there a way to make it like an information box?

There's no generic way to modify any standard Inno Setup message box.
You generally have to re-implement the function on your own. What is not always possible.
For your specific case, you are lucky, as you can easily implement your own custom check for 64-bit system using the IsWin64 function from the InitializeSetup event function.
And display your own custom message box using the MsgBox function.
function InitializeSetup(): Boolean;
begin
Result := True;
if not IsWin64 then
begin
MsgBox('This cannot be installed on 32-bit system.', mbInformation, MB_OK);
Result := False;
end;
end;

Related

Inno Setup customize FinishedLabel with Pascal Script

My goal with Inno Setup 6.x is to customize the FinishedLabel text in code, i.e., Pascal Script. The reason why I'm using Pascal Script is that I want to only customize/change the label if IsAdminMode() is true. How can I do that?
The following two approaches do not work:
Use a scripted constant:
[Messages]
FinishedLabel={code:GetFinishedLabel}
[Code]
function GetFinishedLabel(Param: String): String;
begin
Result := 'BLA';
end;
This shows "{code:GetFinishedLabel}" rather than "BLA".
Customize the wizard in InitializeWizard.
Complete (failing) example:
[Code]
procedure InitializeWizard();
begin
WizardForm.FinishedLabel.Caption := 'BLA';
end;
The FinishLabel still shows the original text from Default.isl
Any ideas?
The FinishedLabel is updated at the end of the installation according to various factors. So your value set in InitializeWizard is overridden. You have to set your custom message later, such as in CurPageChanged(wpFinished):
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
WizardForm.FinishedLabel.Caption := 'BLA';
end;
end;
You might consider to improve the code to do what Inno Setup would do, like:
Taking into account, if restart is needed (FinishedRestartLabel);
Taking into account, if icons were created (FinishedLabel vs. FinishedLabelNoIcons);
Adjusting the label height to fit the message;
Shifting RunList position according to the message height.

Setup Programs created using Inno Setup Compiler doesn't display Minimize Animation

My Problem is why Inno Setup Compiler (Unicode or ANSI) and any Setups made by it don't minimize showing a nice Minimizing Animation like in Other Windows Programs?
It displays a very basic Minimize Animation..........Why that?
I know Borland Delphi as Inno Setup Compiler's Compiler, but Borland Delphi doesn't have such a bad Minimize Animation...........It minimizes normally as Windows System Windows minimize (such as Explorer, Computer, Control Panel).................
I also noticed that the Windows Installer Creater Nullsoft Scriptable Install System - NSIS and Setups made using it are also minimizing well like I said.
How can I resolve this problem?
UPDATED QUESTION
I also added a code to play that nice Zooming Minimize / Restore Animation can be seen in Many Windows Applications on Inno Setup's WizardForm, But when I click the WizardForm's Minimize Button after adding this code to Inno Setup Compiler's Source Code, the Nice Zooming animation not plays and it never can be minimized using it, it only can be minimized using Taskbar button after adding this code. So it means this code not working or anything else wrong...........Why this is not working???
The Code I Added to unit WizardForm:
interface
uses
Windows;
type
TTrayZoom = class(TObject)
private
class function GetTrayRect: TRect;
class procedure DoZoom(const Wnd: HWND; const Src, Dest: TRect);
public
class procedure ZoomToTray(const Wnd: HWND);
class procedure ZoomFromTray(const Wnd: HWND);
end;
implementation
class procedure TTrayZoom.DoZoom(const Wnd: HWND; const Src, Dest: TRect);
begin
DrawAnimatedRects(Wnd, IDANI_CAPTION, Src, Dest);
end;
class function TTrayZoom.GetTrayRect: TRect;
var
TaskbarWnd, TrayWnd: HWND;
begin
TaskbarWnd := FindWindow('Shell_TrayWnd', nil);
TrayWnd := FindWindowEx(TaskbarWnd, 0, 'TrayNotifyWnd', nil);
GetWindowRect(TrayWnd, Result);
end;
class procedure TTrayZoom.ZoomFromTray(const Wnd: HWND);
var
WndRect: TRect;
begin
GetWindowRect(Wnd, WndRect);
DoZoom(Wnd, GetTrayRect, WndRect);
end;
class procedure TTrayZoom.ZoomToTray(const Wnd: HWND);
var
WndRect: TRect;
begin
GetWindowRect(Wnd, WndRect);
DoZoom(Wnd, WndRect, GetTrayRect);
end;
And I called TTrayZoom.ZoomToTray from if WMSysCommand..... = SCMINIMIZE and called TTrayZoom.ZoomFromTray from if WMSysCommand..... = SCRESTORE with the setting HWND parameter to WizardForm.Handle.
But those codes never works, I even don't know if they're get called or not. :(
What is the problem playing this Zooming Animation in this WizardForm?
I'd say there are two issues.
The animation is shown for windows that have a task bar button. The wizard form does not have a task bar button.
The task bar button of the installer belongs to a hidden main window.
Historically the installers had full screen background gradient blue windows. Even Inno Setup supported that.
While that background window is no longer enabled by default (the WindowVisible directive defaults to No in modern versions of Inno Setup), it still exists and owns the task bar button.
Inno Setup is built using an ancient version of Delphi that likely does not play nicely with the minimize feature.
Generally, I'd say you should file a feature request/bug report to get this fixed.

Inno Setup Change AppName based on component(s) selected

I need the installer to show different AppName based on (un)selected components. I tried this:
[Setup]
AppName={code:GetAppName}
AppVersion=1.0
AppVerName=Dagon Video Tools
AppId=Dagon Video Tools
DefaultDirName={sd}\Games\Dagon Video Tools
[Code]
function GetAppName(Value: string): string;
var
CurPageID: Integer;
Begin
Result := 'Dagon Video Tools'
if (CurPageID=wpSelectComponents) and IsComponentSelected('Slasher') and not IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Slasher';
end;
if (CurPageID=wpSelectComponents) and IsComponentSelected('Frankenstein') and not IsComponentSelected('Slasher') then
begin
Result := 'Dagon Frankenstein';
end;
if (CurPageID=wpSelectComponents) and IsComponentSelected('Slasher') and IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Video Tools';
end;
End;
But, as you can guess, this doesn't work. Is this script incomplete or should it be done in a different way altogether?
The AppName directive value is resolved (= your GetAppName is called) immediately after the InitializeSetup (if any) finishes. That is a long before the user is able to change the components.
So you cannot make AppName depend on the selected components.
Some uses of the AppName could be overridden with a custom value though, but not all. See below.
Though, as I know that your question is actually about a setup type, you can do this:
Create custom "type" page (like a menu) as the very first one.
Once the user selects the "type", restart the installer with a custom switch (e.g. /APPTYPE=slasher) and exit.
Once the installer is (re-)run with the /APPTYPE, you know from the beginning, what component/type you are installing and hence you can set the AppName normally.
Of course, you skip the custom "type" page.
This is actually a way simpler to implement. The only drawback is that the setup window is "recreated" after the user selects the "type".
This is the original response in case you do not want to use the above solution.
First, your implementation of the GetAppName is wrong. You are using an uninitialized variable CurPageID. And anyway, as mentioned already, the GetAppName is called even before the wizard window is created, so "current page" is irrelevant here.
The correct implementation would be like:
function GetAppName(Value: string): string;
begin
if IsComponentSelected('Slasher') and not IsComponentSelected('Frankenstein') then
begin
Result := 'Dagon Slasher';
end
else
if IsComponentSelected('Frankenstein') and not IsComponentSelected('Slasher') then
begin
Result := 'Dagon Frankenstein';
end
else
begin
Result := 'Dagon Video Tools';
end;
end;
But this still won't make it working in the AppName directive. We will use it in other contexts though later.
Also note that for your specific installer, you should better use the WizardSetupType(false) function instead of the IsComponentSelected.
FinishedLabel
Just override the Inno Setup default text in CurPageChanged(wpFinished):
procedure CurPageChanged(CurPageID: Integer);
var
S: string;
begin
if CurPageID = wpFinished then
begin
S := SetupMessage(msgFinishedHeadingLabel);
StringChange(S, '[name]', GetAppName(''));
WizardForm.FinishedHeadingLabel.Caption := S;
WizardForm.AdjustLabelHeight(WizardForm.FinishedHeadingLabel);
{ Ideally we should shift the FinishedLabel up or down here, }
{ if the height of the header changed. }
{ Note that other messages (msgFinishedLabelNoIcons or msgFinishedRestartLabel) }
{ are used in special situations, so this is not a complete solution. }
S := SetupMessage(msgFinishedLabel);
StringChange(S, '[name]', GetAppName(''));
WizardForm.FinishedLabel.Caption := S;
WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
end;
end;
Add/Remove Programs
That's easy. There's the UninstallDisplayName directive for this, which is resolved only during the actual installation, when we already know the selected components. So we can use your (fixed) GetAppName here:
[Setup]
UninstallDisplayName={code:GetAppName}
Are you sure you want to completely remove AppName and all of its components?
You cannot change that. You better use some generic name in the AppName so that this message works for any component.
Or make the message not mention the application name at all:
[Messages]
ConfirmUninstall=Are you sure you want to completely remove this game?
Alternatively remove the message completely:
Replace or customize modal uninstallation windows in Inno Setup
Please wait while AppName is removed from your computer
The same solution as for the WizardForm.FinishedLabel. Just use the UninstallProgressForm.PageDescriptionLabel from the InitializeUninstallProgressForm.
AppName was successfully removed from your computer
Similar as with the "Are you sure you want to completely remove AppName and all of its components?"
Either make the AppName generic. Or disable the message with "silent" mode and implement your own message in the CurUninstallStepChanged(usPostUninstall).
Again, see Replace or customize modal uninstallation windows in Inno Setup.
For a similar discussion, see also Changing AppName and AppDir depending on language in Inno Setup.

Inno Setup: how to call custom functions from the InstallDelete section

I would need Inno Setup generated installer to delete certain files prior installation if the software is already installed with an older version.
I tried to do this by comparing version numbers (custom function below) but when compiling, Inno Setup generates an error:
[ISPP] Undeclared identifier: "GetInstalledVersion".
The Inno Setup script relevant extract is:
(...)
[Code]
function GetInstalledVersion(MandatoryButNotUsedParam: String): String;
var Version: String;
begin
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\'+ExpandConstant('AppId')+'_is1', 'DisplayVersion') then
begin
RegQueryStringValue(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\'+ExpandConstant('AppId')+'_is1', 'DisplayVersion', Version);
MsgBox(ExpandConstant('Existing version:'+Version+' New version:'+ExpandConstant('AppVersion')), mbInformation, MB_OK);
Result := Version;
end
else
begin
Result := '';
end
end;
(...)
[InstallDelete]
#define InstalledAppVersion GetInstalledVersion('')
#if "1.013" > InstalledAppVersion
Type: files; Name: {userappdata}\xxx\*.hhd
#endif
Being new to Inno Setup, this is certainly a trivial question but no answer found on forums. The question is thus: how can I properly call the function GetInstalledVersion from the [InstallDelete] section?
Is there an issue because [InstallDelete] section might be called before [code] section is read?
Many thanks for any help / hint!
Do you want to check for currently installed version and if it's below 1.013,
then remove user files from {userappdata}\xxx\*.hhd ?
then what you need is parameter Check http://www.jrsoftware.org/ishelp/index.php?topic=scriptcheck
[Code]
function isOldVersionInstalled: Boolean;
begin
// Result := <True|False>;
end;
[InstallDelete]
Type: files; Name: {userappdata}\xxx\*.hhd; Check:isOldVersionInstalled;
What is wrong with your example:
You are calling a Pascal function from the pre-processor.
Those are two different things.
You can define a macro in pre-processor - that's kind of like a function,
but that's not what you want because Pre-processor only runs on compile time and so it can't be used to check on state of User's files/environment.

Exit from Inno Setup installation from [Code]

Is it possible to exit the installation from a function in the [Code] section of an installer created with Inno Setup?
I'm not interested in setting the exit code, what I want to do is perform a custom check for a requirement, and exit the installation if that requirement was not previously installed.
To prevent the installer from running, when prerequisites test fails, just return False from the InitializeSetup. This will exit the installer even before the wizard shows.
function InitializeSetup(): Boolean;
begin
Result := True;
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
Result := False;
end;
end;
If you need to test prerequisites right before the installation starts only (i.e. the InitializeSetup is too early), you can call the Abort function from the CurStepChanged(ssInstall):
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssInstall then
begin
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
Abort;
end;
end;
end;
Though for this scenario, consider using the PrepareToInstall event function mechanism, instead of exiting the setup.
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
Result := '';
if not PrerequisitesTest then
begin
Result := 'Prerequisites test failed';
end;
end;
If you need to force terminate the installer any other time, use the ExitProcess WinAPI call:
procedure ExitProcess(uExitCode: Integer);
external 'ExitProcess#kernel32.dll stdcall';
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = wpReady then
begin
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
ExitProcess(1);
end;
end;
Result := True;
end;
Though this is rather unsafe exit, so use it only as the last resort approach. If you have any external DLL loaded, you might need to unload it first, to avoid crashes. This also does not cleanup the temporary directory.
You can use Abort() if you are in these events:
InitializeSetup
InitializeWizard
CurStepChanged(ssInstall)
InitializeUninstall
CurUninstallStepChanged(usAppMutexCheck)
CurUninstallStepChanged(usUninstall)
The way I do it is:
procedure ExitProcess(exitCode:integer);
external 'ExitProcess#kernel32.dll stdcall';
And the way of using it is:
[Code]
if .... then begin
ExitProcess(0);
end;
This is a write up of what I fiddled out of my Inno 5.6.1 today and the sources you can find at https://github.com/jrsoftware/issrc [ref1]
A possibly useful catch-all solution to "Exit from [Code]"
TL;DR example:
[Code]
var _ImmediateInnoExit_was_invoked_flag: Boolean; // Inno/Pascal Script initializes all Boolean to False.
procedure ImmediateInnoExit();
var MainFormRef: TForm;
begin
_ImmediateInnoExit_was_invoked_flag := True;
try
MainFormRef := MainForm(); // calls GetMainForm() in Inno pascal code, which will raise an internal exception if the form is not yet initialized.
Log('INFO: ImmediateInnoExit: Calling MainForm.Close()!');
Log('NOTE: If the Event Fn CancelButtonClick is not coded to auto-Confirm, this will display the cancel dialog in the GUI case!');
Log('NOTE: Code will stall inside the Close() function while the Cancel confirmation dialog is displayed.');
MainFormRef.Close(); // this is only effective if the Wizard is visible, but we cann call it even when running siently (as long as the Wizard is initialized)
Log('NOTE: MainForm.Close() invoked. (If confirmed, setup will exit.)');
except
Log('INFO: ImmediateInnoExit did not resolve MainForm -> assuming we were call in an InitializeSetup() context before the Main form has been created!');
end;
Log('INFO: ImmediateInnoExit: Calling Abort() -> EAbort!');
Log('NOTE: Will exit the current scope.');
Log('NOTE: In GUI mode, it will just jump up to the Delphi event loop (and be ignored there). (But the WizardForm.Close() call should lead to exit!)');
Log('NOTE: In Silent Mode, it will be caught and exit the setup.');
Abort(); // Raise EAbort
end;
// This is called when the user clicks the cancel button or the [x] Close button
// the close/cancel can be invoked from code via WizardForm.Close!
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
Log(Format('IN: CancelButtonClick(%d <- Cancel=[%d], Confirm=[%d])', [CurPageID, Cancel, Confirm]));
Confirm := not _ImmediateInnoExit_was_invoked_flag; // if Confirm==False we don't get the dialog prompt.
Log(Format('IN: CancelButtonClick(%d -> [%d], [%d])', [CurPageID, Cancel, Confirm]));
end;
And now to what the point of the above code is:
Anatomy of Inno Setup Exit/Cancel and Abort
Abort
The Inno docs for Abort state:
Description:
Escapes from the current execution path without reporting an error.
Abort raises a special "silent exception" which operates like any
other exception, but does not display an error message to the end
user.
Remarks:
Abort does not cause Setup or Uninstall to exit unless it's called
from one of these event functions (or another function invoked by
them):
InitializeSetup InitializeWizard CurStepChanged(ssInstall)
InitializeUninstall CurUninstallStepChanged(usAppMutexCheck)
CurUninstallStepChanged(usUninstall)
Abort() behaviour explained
The reason the Abort function bevahes in this way is because, internally, Inno raises an EAbort exception, and that exception is treated specially by the Delphi UI loop. Only in the functions listed, the Inno devs have either added special treatment for EAbort (like in the case of CurStepChanged(ssInstall)[ref2]), --
-- or the function os not called via the UI loop, like in the case of InitializeSetup, which is called from the main program in Setup.dpr, and any direct EAbortis handled there specifically in the exceptblock there.
In all other Inno event function (e.g. NextButtonClick etc.) the EAbortexception will reach the main program/UI loop and be ignored there.
Which leads us nicely to:
Abort() behaviour, when running /SILENT (or /VERSILENT)
When Inno runs silently, it does not display the wizard form UI. The "Wizard" / Inno's progress is then not driven by the UI loop, but by WizardForm.ClickThroughPages, which is invoked under same toplevel try/except block as e.g. InitializeSetup. [ref3]
Because of this, if Inno is being called silently, Abort() will exit setup from every most [Code] functions, and the list given in the docs for Abort becomes moot if setup is being run silently.
Cancel
To cancel the setup, the user can click the [Cancel] button or the [X] close button of the Setup Wizard.
In this case, Inno will invoke the callback function CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean) (if defined) and terminates setup, possible with an escape hatch dialog:
Called when the user clicks the Cancel button or clicks the window's
Close button. The Cancel parameter specifies whether normal cancel
processing should occur; it defaults to True. The Confirm parameter
specifies whether an "Exit Setup?" message box should be displayed;
User [Code] can invoke the Cancel Button mechinism via calling WizardForm.Close(), but this only works if Setup is displaying the Wizard Form, and doesn't work in silent mode.
Cancel Details
WizardForm.Close[ref4], or the click on the actual button, will eventually call TMainForm.FormCloseQuery (in Main.pas), which will call CancelButtonClick callbacks[ref5] and dependeing on Confirm value, either call TerminateApp(); directly or first call the helper function ExitSetupMsgBox()that will display the message box to the user.
Footnotes:
[ref1] : For non-delphi folks: If you search over the sources in you text editor, make sure to include at least .iss .pas and .dpr
[ref2] : ssInstall exception handling is located at issrc\Projects\Main.pas: TMainForm.Install via SetStep(ssInstall, False); and the except block at the end of TMainForm.Install where TerminateApp is called.
[ref3] : See Setup.dpr calling MainForm.InitializeWizard from Main.paswhich calls WizardForm.ClickThroughPages iff not InstallMode = imNormal, i.e. in the silent case.
[ref4] : WizardForm.Close() internally calls MainForm.Close() (see: TWizardForm.FormClose)
[ref5] : There are actually two kinds of cancel button click callbacks defineable in Inno [Code]: The global CancelButtonClickprocedure, and each Wizard page also has a OnCancelButtonClick: TWizardPageCancelEvent that can be set.
Take a look at InitializeSetup and Abort in the InnoSetup help. As Cody said, it is possible. If you're having problems, post what you've done and the problem you're having.
Somewhere in your code section you perform a check. Right?
As result of that check you want to exit the installation.
To perform the exit put the line:
PostMessage (WizardForm.Handle, $0010, 0, 0); { quit setup, $0010=WM_CLOSE }
Hopefully this helps

Resources