How to show a message box for a specified time? - inno-setup

Is there a way to show a message box for a specified time (that means, the message box will close itself when the specified time elapses) ?

Windows API has a function for showing a message box for a specified time, but for some reason is that function undocumented, which means it is not officially supported and may well be subject to change.
That function is called MessageBoxTimeout, and it has even export in the user32.dll library, what makes me feel that the only thing this function lacks is the official documentation. But who knows...
The following script shows how to display a message box for 5 seconds before the wizard form is shown. If the user doesn't click the OK button, nor manually close the window, the message box is automatically closed when that 5 seconds period elapses:
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
MB_TIMEDOUT = 32000;
MB_ICONERROR = $10;
MB_ICONQUESTION = $20;
MB_ICONWARNING = $30;
MB_ICONINFORMATION = $40;
function MessageBoxTimeout(hWnd: HWND; lpText: string; lpCaption: string;
uType: UINT; wLanguageId: Word; dwMilliseconds: DWORD): Integer;
external 'MessageBoxTimeout{#AW}#user32.dll stdcall';
procedure InitializeWizard;
begin
MessageBoxTimeout(WizardForm.Handle, 'This message will be automatically ' +
'closed in 5 seconds!', 'Caption...', MB_OK or MB_ICONINFORMATION, 0, 5000);
end;
For more information about parameters and result values refer to the MessageBox function help site and some of the unofficial articles describing the MessageBoxTimeout function itself, like e.g.:
Maurizio Pisano: MessageBoxTimeout API (CodeProject)
Eddie Shipman: Undocumented MessageBoxTimeOut function (Embarcadero)

If you want a more customized implementation than the MessageBoxTimeout from #TLama's answer allows (like countdown display or custom button captions):
create a custom form using CreateCustomForm;
use SetTimer to implement the timeout/count down.
For a complete code, see MsgBox - Make unclickable OK Button and change to countdown - Inno Setup.

Related

Buttons are being cropped on Inno Setup install and a custom message is showing in gibberish (for Japanese) [duplicate]

This question already has an answer here:
Inno Setup Unicode encoding issue with messages in ISS script
(1 answer)
Closed 4 years ago.
In my [run] section I have this entry:
Filename: "{win}\hh.exe"; \
Parameters: "{app}\MeetSchedAssist.chm::/msa-revision-history.htm"; \
WorkingDir: "{app}"; \
Flags: nowait postinstall runmaximized; \
Description: "{cm:ViewChangeHistory}"
When I run my installer in English it looks fine:
A user has just recorded a video installing it on a Japanese computer:
There are two issues:
The buttons of the GUI are all cropped.
The "View Change History" custom string is gibberish.
The English custom messages file (UTF-8 encoded) has:
English.ViewChangeHistory=View Change History
The Japanese custom messages file (UTF-8 encoded) has:
Japanese.ViewChangeHistory=変更履歴を表示
Why is it not displaying correctly? I am using the most recent version of Inno Setup Unicode. I do know that the user is using a Ultra HD monitor.
Also, I am rebuilding the run list like this:
type
TRunEntry = record
Caption: string;
Checked: Boolean;
Object: TObject;
end;
procedure RebuildRunList;
var
RunEntries: array of TRunEntry;
I: Integer;
begin
// Save run list ...
SetArrayLength(RunEntries, WizardForm.RunList.Items.Count);
for I := 0 to WizardForm.RunList.Items.Count - 1 do
begin
RunEntries[I].Caption := WizardForm.RunList.ItemCaption[I];
RunEntries[I].Checked := WizardForm.RunList.Checked[I];
RunEntries[I].Object := WizardForm.RunList.ItemObject[I];
end;
// ... clear it ...
WizardForm.RunList.Items.Clear;
// ... and re-create
for I := 0 to GetArrayLength(RunEntries) - 1 do
begin
// the first three entries are radio buttons
if (I = 0) or (I = 1) or (I = 2) then
begin
WizardForm.RunList.AddRadioButton(
RunEntries[I].Caption, '', 0, RunEntries[I].Checked, True, RunEntries[I].Object);
end
else
begin
WizardForm.RunList.AddCheckBox(
RunEntries[I].Caption, '', 0, RunEntries[I].Checked, True, True, True,
RunEntries[I].Object);
end;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
// Only now is the RunList populated.
// Two entries are on 64-bit systems only.
if IsWin64 then RebuildRunList;
end;
end;
Update
I commented out the code that rebuilds the run list and it made no difference on my PC:
However, the skinning works correctly. So the button position matter must be related to the users using a ultra HD configuration and and/or scaled text.
Then I commentted out the skinning code and tried again on my PC. No change:
I really don't understand why this is happening!
Update
I include the custom messages like this:
[CustomMessages]
#include AddBackslash(SourcePath) + ".\l.jpn\CustomMessagesJapanese.txt"
This depends on how you're including the text.
In an ISL file with MessagesFile
According to http://www.jrsoftware.org/ishelp/topic_unicode.htm, it is not legal to save an ISL file in UTF-8.
You must save it as ANSI encoded with the code page specified in the file.
Directly in your ISS file or using #include
Alternatively you can use UTF-8 strings directly in your iss file by using Language.Key values directly in a local [Messages] or [CustomMessages] section, like so:
[CustomMessages]
English.ViewChangeHistory=View Change History
Japanese.ViewChangeHistory=変更履歴を表示
You can also #include an external file that uses the same format.
In both cases (whether directly in the iss file or in a #included file), you need to ensure that the file is saved as UTF-8 with BOM.

Checking if only one specific component is selected in Inno Setup

I have a requirement to skip a page when only a particular component is selected in the component selection page. Please look at the below image
[Components]
Name: DBS\TRACE; Types: DBS TBLWOS; Description: DBS Tracing Bodylife Database;
The requirement is to skip a page when the option "DBS Tracing Bodylife Database" (highlighted in the image) alone is selected and clicked on Next button. If I select only that option, I am successfully able to skip the page using below code.
if PageID = PageToBeSkipped.ID then begin
Result := not (IsComponentSelected('not DBS\TRACE'));
end;
But If I select any other additional component along with the "DBS Tracing Bodylife Database" from this page which is nearly 20, the page should not be skipped. The above code skips the page if any additional component is selected too.
How can I handle this?
Thanks in advance!
To test if any component, except one specific, is selected, you can use WizardSelectedComponents function (which returns a comma-separated list of selected components).
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = PageToBeSkipped.ID then
begin
Result := (CompareText(WizardSelectedComponents(False), 'DBS,DBS\TRACE') = 0);
end;
end;
Note that the WizardSelectedComponents returns even "partially" selected components groups.

How to show progress during "PrepareToInstall"?

An installer I'm working on does most of its work in the PrepareToInstall function, as everything I need to do might fail, and therefore this is the appropriate place to handle these things in case they do fail. Any failure can be automatically reported by passing the error message in the function's result. There are only 3 small files which the installer actually copies over.
The problem is that the wizard seems to freeze (or not respond rather) during this function, just showing a blank page titled "Preparing to install..." while in reality, it's going through my install process.
I would like to show the progress to the user with a simple procedure ShowProgress(const S: String); which shows the user what it's actually doing. How can I do this?
This is how I'm doing my install, where I'd like to wrap each call to Log()...
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
R: Integer;
begin
Result:= '';
try
Log('Doing this...');
R:= DoThis;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing this.';
end;
else begin
Result:= 'Unexpected error doing this: ' + IntToStr(R);
end;
end;
if Result = '' then begin
Log ('Doing that...');
R:= DoThat;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing that.';
end;
else begin
Result:= 'Unexpected error doing that: ' + IntToStr(R);
end;
end;
end;
if Result = '' then begin
Log ('Doing something else...');
R:= DoSomethingElse;
case R of
0: begin
Result:= '';
end;
1: begin
Result:= 'Error message 1 was raised while doing something else.';
end;
else begin
Result:= 'Unexpected error doing something else: ' + IntToStr(R);
end;
end;
end;
//A few more blocks like above
//Error logging
if Result <> '' then begin
Log('FAILURE: '+Result);
end;
except
Result:= 'EXCEPTION';
Log('EXCEPTION');
end;
end;
I mixed all your answers guys and I found another possible solution to this question.
I know this is not a so called 'elegant solution'.
Anyway it is a quick solution that can avoid you to develop a separated DLL with custom controls managing the communication between the installer and the DLL.
The following approach describe how to create a custom Page that is a copy of the Page Inno Setup shows during PrepareToInstall setup phase.
This copy must have the same look and feel of the original one plus a progress bar and a label that developer can use to indicate the current step of the PrepareToInstall setup phase.
Let's see some code.
First of all, prepare the custom Page:
[Code]
var
PrepareToInstallWithProgressPage : TOutputProgressWizardPage;
Now define PrepareToInstall function to add our custom stuff:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
PrepareToInstallWithProgressPage.SetProgress(0, 0);
PrepareToInstallWithProgressPage.Show;
try
// First preinstallation step: suppose to kill currently app you are going to update
PrepareToInstallWithProgressPage.SetText('Exiting MyApp Running Instances'), '');
ExecuteCmd('taskkill', '/F /IM MyApp');
// Set progress bar to 10%
PrepareToInstallWithProgressPage.SetProgress(1, 10);
// Second preinstallation step
// DoSomething usefull...
// Set progress bar to 20% after done
PrepareToInstallWithProgressPage.SetProgress(2, 10);
// ...do other preinstallation steps till the end
PrepareToInstallWithProgressPage.SetProgress(10, 10);
finally
PrepareToInstallWithProgressPage.Hide;
end;
end;
At this installation phase we completed the steps for prepare to install phase so the default PrepareToInstall Page of Inno Setup is shown for a while.
Now, the user probably cannot understand that the page changes because our PrepareToInstallWithProgressPage has the same look and feel of the original one.
In order to let our page has the same look and feel we can use SetupMessage function to get the 2 strings of the original Inno Setup PrepareToInstall page.
Using SetupMessage we can avoid to duplicate and localize strings for our custom page to copy the originals.
Just like this:
procedure InitializeWizard;
var
A: AnsiString;
S: String;
begin
// The string msgWizardPreparing has the macro '[name]' inside that I have to replace with the name of my app, stored in a define constant of my script.
S := SetupMessage(msgPreparingDesc);
StringChange(S, '[name]', '{#MY_APPNAME}');
A := S;
PrepareToInstallWithProgressPage := CreateOutputProgressPage(SetupMessage(msgWizardPreparing), A);
end;
I hope this help.
Have a look at the CodeDlg.iss example script included with Inno, in particular at the code that uses CreateOutputProgressPage.
This page type allows you to show a status message and/or a progress bar while you are performing other actions. And it automatically pumps messages whenever you change the progress label/value to ensure that the user sees the correct status.
It is intended for precisely this kind of code.
The code written in the PrepareToInstall function blocks the Windows message pump from processing, rendering the wizard form non-responsive. If many controls are needed to be visible in this screen, showing perhaps a progress bar and list of steps to take, this can be done with a form inside of a DLL. Make sure there is a thread in this DLL which does the actual install process, and only update the GUI to display the status to the user. This DLL form may overlay the wizard, or may even be embedded inside of the wizard in a custom wizard page, for example. The idea is to move your install code from the PrepareToInstall function to within this thread, and only then will you be able to achieve a fully responsive GUI during this process. From the PrepareToInstall function, there should be just one DLL call to initiate the install process, which in turn shows the form from the DLL.
The result may look something like this (in the works):

Change WizardSmallImageFile programmatically - Inno Setup

Is it possible to change the top image on a wizard form depending on the wizard form. I can change the left side image but would like to change the top (small image).
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = 4 then
filename:= 'babylontoolbar.bmp'
else
filename:= 'label2-crop.bmp';
ExtractTemporaryFile(filename);
(*WizardForm.WizardSmallImageFile.Bitmap.LoadFromFile(ExpandConstant('{tmp}\'+FileName));*)
WizardForm.WizardBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\' + FileName));
end;
I just would like to know how to reference the small file to replace the WizardSmallImageFile which does not work.
The WizardSmallImageFile directive is mapped to the WizardSmallBitmapImage control of the WizardForm, so in code you can access it this way (anyway, do not hardcode page ID numbers, but instead use the intended PageID constants):
procedure CurPageChanged(CurPageID: Integer);
var
FileName: string;
begin
if CurPageID = wpInfoBefore then
FileName := 'babylontoolbar.bmp'
else
FileName := 'label2-crop.bmp';
ExtractTemporaryFile(FileName);
WizardForm.WizardSmallBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\' + FileName));
end;
Once again TLama has the answers, just have to keep googling. For those trying to do something similar to this and having problems finding the answer check out Skipping custom pages based on optional components in Inno Setup

How can I show a "please wait" window while checking for prerequisites in my installer?

I do a number of checks in the InitializeSetup function in my script. These take about 10 seconds to complete, during which time nothing is displayed except for the window button on the taskbar (clicking it yields nothing). I would like to show a simple "Please wait" window instead. How can I do this?
Here is sample script that shows how you can create a custom dialog.
Basically create a custom form and place the custom control on it. You can use this as starting point to get the dialog appear as you wish.
[Setup]
AppName='Test Date Script'
AppVerName='Test Date Script'
DefaultDirName={pf}\test
[Code]
function InitializeSetup() : boolean;
var
DlgWait : TSetupForm;
lblWait : TLabel;
I : Integer;
begin
dlgWait := CreateCustomForm;
dlgWait.FormStyle := bsDialog;
dlgWait.Position := poMainFormCenter;
lblWait := TLabel.Create(dlgWait);
lblWait.Parent := dlgWait;
lblWait.Caption := 'Please Wait';
lblWait.Visible := True;
dlgWait.Show;
dlgWait.Refresh; // Process the paint message
for I := 0 to 10 do
begin
Sleep(1000); // Simulate Functions taking 10 sec
dlgWait.Refresh;
end;
DlgWait.Free;
end;

Resources