Enable close/cancel button of Inno Setup on Finished page - inno-setup

Is it possible to enable close button on the final page of Inno Setup form, and add behaviour of an exit?

It's easy to enable the close button, use EnableMenuItem WinAPI function. See also Inno Setup Disable close button (X).
Difficult is to make the close button working actually. Inno Setup window is not designed to be closed on the "Finished" page. The only way is probably to forcefully abort the process using ExitProcess WinAPI function. See Exit from Inno Setup Installation from [code].
The complete code would be:
function GetSystemMenu(hWnd: THandle; bRevert: Boolean): THandle;
external 'GetSystemMenu#user32.dll stdcall';
function EnableMenuItem(hMenu: UINT; uIDEnableItem, uEnable: UINT): Boolean;
external 'EnableMenuItem#user32.dll stdcall';
const
MF_BYCOMMAND = $0;
SC_CLOSE = $F060;
procedure ExitProcess(exitCode:integer);
external 'ExitProcess#kernel32.dll stdcall';
procedure FormClose(Sender: TObject; var Action: TCloseAction);
begin
Log('Exiting by user after installation');
ExitProcess(1);
end;
procedure CurPageChanged(CurPageID: Integer);
var
Menu: THandle;
begin
if CurPageID = wpFinished then
begin
{ Enable "close" button }
Menu := GetSystemMenu(WizardForm.Handle, False);
EnableMenuItem(Menu, SC_CLOSE, MF_BYCOMMAND);
{ Make the "close" button working }
WizardForm.OnClose := #FormClose;
end;
end;

Related

Inno Setup: cursor doesn't change when I press outside the installer [duplicate]

This question already has an answer here:
How to set a custom .cur or .ani cursor in Inno Setup?
(1 answer)
Closed 1 year ago.
I made an installer using Inno Setup and inserted a code to change the mouse cursor to a custom one once its started and to return to default one I exit it, but I wanted for the mouse to change to default one i click outside of the installer even if it is still running.
This is my code:
[Code]
const
OCR_NORMAL = 32512;
function SetSystemCursor(hcur: LongWord; id: DWORD): BOOL;
external 'SetSystemCursor#user32.dll stdcall';
function LoadCursorFromFile(lpFileName: string): LongWord;
external 'LoadCursorFromFileW#user32.dll stdcall';
function CopyIcon(hIcon: LongWord): LongWord;
external 'CopyIcon#user32.dll stdcall';
function LoadCursor(hInstance: LongWord; lpCursorName: LongWord): LongWord;
external 'LoadCursorA#user32.dll stdcall';
var
OriginalCursor: LongWord;
procedure InitializeWizard();
var
PathToCursorFile: string;
Cursor: LongWord;
begin
// Remember the original custom
OriginalCursor := CopyIcon(LoadCursor(0, OCR_NORMAL));
// Load our cursor
ExtractTemporaryFile('MyCursor.cur')
PathToCursorFile := ExpandConstant('{tmp}\MyCursor.cur');
Cursor := LoadCursorFromFile(Graphics\Images\PixelCursor.cur);
SetSystemCursor(Cursor, OCR_NORMAL);
end;
procedure DeinitializeSetup();
begin
// Restore original cursor on exit
SetSystemCursor(OriginalCursor, OCR_NORMAL);
end;
There is a function SetCursor in user32.dll, you can use it instead of SetSystemCursor.
This is example from Microsoft Docs: Using Cursors.

How do you prompt a user to read a guide and restart/log off from their computer on the finished page using Inno Setup?

I am building an installer and would like to ask the user to restart using a radio button. I would also like to include an option to open the user guide if the user selects "No, I will restart later". My current method for asking the user to open the user guide is putting it in the [Run] section like this:
[Run]
Filename: "{app}\userguide.pdf"; Description: "View the User Guide"; Flags: shellexec runasoriginaluser postinstall nowait unchecked
This works perfectly, it even opens in the default PDF viewer. However, whenever I try to include a restart option, it overrides the user guide option and completely removes it. So, trying:
[Code]
function NeedRestart(): Boolean;
begin
Result := True;
end;
as well as:
[Setup]
AlwaysRestart=yes
work in the sense that they include an option for restart but they also override the user guide button. Is there a way to make a custom page that, upon checking the "No I will restart later" radio button, will show an option on opening the user guide? I am not too familiar with using Inno Setup and Delphi/Pascal.
You have to code that. For example you can add your own checkbox for starting the user guide:
[Setup]
AlwaysRestart=yes
[Files]
Source: "userguide.pdf"; DestDir: "{app}"
[Code]
var
LaunchCheckbox: TCheckbox;
procedure YesNoRadioClicked(Sender: TObject);
begin
// Disable the user guide checkbox when "restart" is selected
LaunchCheckbox.Enabled := WizardForm.NoRadio.Checked;
end;
procedure InitializeWizard();
begin
LaunchCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
LaunchCheckbox.Caption := 'View the User Guide';
LaunchCheckbox.Checked := False;
LaunchCheckbox.Left := WizardForm.YesRadio.Left;
LaunchCheckbox.Width := WizardForm.YesRadio.Width;
LaunchCheckbox.Height := ScaleY(LaunchCheckbox.Height);
LaunchCheckbox.Parent := WizardForm.FinishedPage;
if (WizardForm.YesRadio.OnClick <> nil) or (WizardForm.NoRadio.OnClick <> nil) then
begin
Log('Restart radio button event handler unexpectedly set');
end
else
begin
WizardForm.YesRadio.OnClick := #YesNoRadioClicked;
WizardForm.NoRadio.OnClick := #YesNoRadioClicked;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
// Adjust to the initial start of restart selection
YesNoRadioClicked(nil);
// Only now the restart radio buttons have their definitive vertical position
LaunchCheckbox.Top :=
WizardForm.NoRadio.Top + WizardForm.NoRadio.Height + ScaleY(16);
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
ErrorCode: Integer;
begin
if CurStep = ssDone then
begin
if (not WizardSilent) and
(not WizardForm.YesRadio.Checked) and
LaunchCheckbox.Checked then
begin
Log('Opening user guide');
ShellExecAsOriginalUser(
'open', ExpandConstant('{app}\userguide.pdf'), '', '', SW_SHOWNORMAL,
ewNoWait, ErrorCode);
end;
end;
end;
The code assumes AlwaysRestart. Were the restart conditional, the code will need an update to adjust to a different layout of the Finished page, when restart is not needed. For a full solution see my installer for WinSCP:
https://github.com/winscp/winscp/blob/master/deployment/winscpsetup.iss

Open external program inside Inno Setup window

I have some animations in Exe format, I need to load them inside a panel in Inno Setup.
I have found these about Delphi:
http://www.delphipages.com/forum/archive/index.php/t-200729.html
How to shell to another app and have it appear in a delphi form
How to implement such thing in Inno Setup?
An equivalent code for Inno Setup will be like:
[Code]
function SetParent(hWndChild: HWND; hWndNewParent: HWND): HWND;
external 'SetParent#User32.dll stdcall';
function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL;
external 'ShowWindow#User32.dll stdcall';
procedure InitializeWizard();
var
Page: TWizardPage;
ResultCode: Integer;
ProgHandle: HWND;
begin
Page := CreateCustomPage(wpWelcome, 'Test', '');
Exec('notepad.exe', '', '', SW_HIDE, ewNoWait, ResultCode);
while ProgHandle = 0 do
ProgHandle := FindWindowByWindowName('Untitled - Notepad');
SetParent(ProgHandle, Page.Surface.Handle);
ShowWindow(ProgHandle, SW_SHOWMAXIMIZED);
end;
Though I suggest you do not do this. It's an unreliable hack. Display the images by the Pascal Code in the installer itself.

Goes to a site when setup is closed / finished / uninstalled

Inno Setup Compiler: How to auto start the default browser with given url? is not what I am wanting for. How / what code in Inno Setup when I want my setup.exe that if it's closed / finished / uninstalled, it will go to a certain site.
To open a web browser from a Pascal Script use a function like this:
procedure OpenBrowser(Url: string);
var
ErrorCode: Integer;
begin
ShellExec('open', Url, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;
To trigger it, when installer closes, you can use the CurStepChanged(ssDone) event:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssDone then
begin
OpenBrowser('https://www.example.com/');
end;
end;
Similarly for an uninstaller, use the CurUninstallStepChanged(usDone) event:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usDone then
begin
OpenBrowser('https://www.example.com/');
end;
end;

How to set StatusMsg from PrepareToInstall event function

My application requires .NET Framework to be installed so I run .NET installation in PrepareToIntall event function. While the installation is running I would like to display some simple message on Wizard.
I found How to set the status message in [Code] Section of Inno install script? but the solution there doesn't work for me.
I tried
WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetMsg');
and also
WizardForm.PreparingLabel.Caption := CustomMessage('InstallingDotNetMsg');
EDIT
I have to do this in PrepareToInstall function, because I need to stop the setup when .net installation fails.
Code looks like this right now:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
isDotNetInstalled : Boolean;
errorCode : Integer;
errorDesc : String;
begin
isDotNetInstalled := IsDotNetIntalledCheck();
if not isDotNetInstalled then
begin
//WizardForm.PreparingLabel.Caption := CustomMessage('InstallingDotNetMsg');
WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetMsg');
ExtractTemporaryFile('dotNetFx40_Full_x86_x64.exe');
if not ShellExec('',ExpandConstant('{tmp}\dotNetFx40_Full_x86_x64.exe'),'/passive /norestart', '', SW_HIDE, ewWaitUntilTerminated, errorCode) then
begin
errorDesc := SysErrorMessage(errorCode);
MsgBox(errorDesc, mbError, MB_OK);
end;
isDotNetInstalled := WasDotNetInstallationSuccessful();
if not isDotNetInstalled then
begin
Result := CustomMessage('FailedToInstalldotNetMsg');
end;
end;
end;
Any Ideas how to achieve this?
The StatusLabel is hosted by the InstallingPage wizard page while you're on PreparingPage page in the PrepareToInstall event method. So that's a wrong label. Your attempt to set the text to the PreparingLabel was correct, but failed because that label is hidden by default (it is shown when you return non empty string as a result to the event method).
But you can show it for a while (you are using ewWaitUntilTerminated flag, so your installation is synchronous, thus it won't hurt anything):
[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
WasVisible: Boolean;
begin
// store the original visibility state
WasVisible := WizardForm.PreparingLabel.Visible;
try
// show the PreparingLabel
WizardForm.PreparingLabel.Visible := True;
// set a label caption
WizardForm.PreparingLabel.Caption := CustomMessage('InstallingDotNetMsg');
// do your installation here
finally
// restore the original visibility state
WizardForm.PreparingLabel.Visible := WasVisible;
end;
end;
Another solution is to use CreateOutputProgressPage to display a progress page over the top of the Preparing to Install page. See the CodeDlg.iss example script included with Inno for an example of the usage; it's fairly straightforward.

Resources