从 [Code] 退出 Inno Setup 安装

16

在使用Inno Setup创建的安装程序的[Code]部分的函数中,是否有可能退出安装程序?

我不想设置退出代码,我想要做的是对某个要求进行自定义检查,如果未满足该要求,则退出安装程序。


问题已在此处得到解答:https://dev59.com/Umw15IYBdhLWcg3w3vpe - 101010
6个回答

20
为了防止安装程序在先决条件测试失败时运行,只需从InitializeSetup返回False即可。这将在向导显示之前退出安装程序。
function InitializeSetup(): Boolean;
begin
  Result := True;

  if not PrerequisitesTest then
  begin                     
    SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
    Result := False;
  end;
end;

enter image description here


如果您需要在安装开始之前测试先决条件(例如,InitializeSetup 太早),则可以从 CurStepChanged(ssInstall) 调用Abort 函数

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;

enter image description here


尽管针对这种情况,考虑使用PrepareToInstall事件函数机制,而不是退出安装程序。
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
  Result := '';

  if not PrerequisitesTest then
  begin                     
    Result := 'Prerequisites test failed';
  end;
end;

enter image description here


如果您需要在其他任何时间强制终止安装程序,请使用ExitProcess WinAPI调用:
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;

尽管这是相当不安全的退出方式,因此只应将其用作最后的手段。如果您加载了任何外部DLL,则可能需要先卸载它以避免崩溃。这也不会清理临时目录。

enter image description here



15
你可以在以下事件中使用Abort():
InitializeSetup
InitializeWizard
CurStepChanged(ssInstall)
InitializeUninstall
CurUninstallStepChanged(usAppMutexCheck)
CurUninstallStepChanged(usUninstall)

11

我的做法是:

procedure ExitProcess(exitCode:integer);
  external 'ExitProcess@kernel32.dll stdcall';

使用它的方法是:

[Code]
  if .... then begin
     ExitProcess(0);
  end;

1
虽然从用户角度来看这似乎是有效的,但它会留下临时文件。 - Gogowitsch
这个答案使用了Wizard.Close,似乎可以避免这个问题。https://dev59.com/Umw15IYBdhLWcg3w3vpe - 101010
@JerryDodge 你可以抑制提示。请参见https://dev59.com/Umw15IYBdhLWcg3w3vpe#12849863 - PolyTekPatrick
1
@010110110101 在静默模式(/silent)下,使用WizardForm.Close无法工作,因此用户可以轻松绕过检查。 - Martin Prikryl
WizardForm.Close 在 ssInstall 之后的自定义页面上也无法正常工作。 - MSalters

5

这是我今天从Inno 5.6.1中摸索出来的内容,你可以在https://github.com/jrsoftware/issrc[ref1]找到相关资源。

“Exit from [Code]”可能有用的终极解决方案

简化实例:

[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;

以上代码的作用是:

Inno Setup退出/取消和终止的解析

终止

Inno文档中对于终止的说明如下:

描述:从当前执行路径中跳出,而不报告错误。

终止引发了一个特殊的“静默异常”,其操作方式与任何其他异常相同,但不会向最终用户显示错误消息。

备注:
终止不会导致安装或卸载退出除非它在以下这些事件函数之一中被调用(或由它们调用的另一个函数):

InitializeSetup InitializeWizard CurStepChanged(ssInstall) InitializeUninstall CurUninstallStepChanged(usAppMutexCheck) CurUninstallStepChanged(usUninstall)

终止()行为解释

终止函数以这种方式工作的原因是因为,在内部Inno引发了一个EAbort异常,并且该异常通过Delphi UI loop进行特殊处理仅在列出的函数中,Inno开发人员已经添加了特殊处理EAbort的功能(例如在CurStepChanged(ssInstall)[ref2]的情况下),

-- 或该函数未通过UI loop进行调用,例如在InitializeSetup的情况下,它是从Setup.dpr的主程序中调用的,并且任何直接的EAbort都会在那里通过except块进行特殊处理。

在所有其他Inno事件函数(例如NextButtonClick等)中,EAbort异常将到达主程序/UI loop并在那里被忽略。

这正好引导我们到:

运行/SILENT(或/VERSILENT)时的终止()行为

当Inno以静默方式运行时,它不显示向导表单UI。此时,“向导”/ Inno的进度不由UI Loop驱动,而是由与例如InitializeSetup相同的顶级try/except块下调用的WizardForm.ClickThroughPages驱动的[ref3]。

由于这个原因,如果 Inno 在静默模式下调用,则 Abort() 会从大多数 [Code] 函数中退出安装程序,并且在文档中给出的 Abort 列表在静默模式下运行安装程序时将无效。

取消

要取消安装,用户可以单击“[取消]”按钮或安装向导的“[X]”关闭按钮。
在这种情况下,Inno 将调用回调函数 CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean)(如果已定义)并终止安装程序,可能会显示一个退出窗口:

当用户单击“取消”按钮或单击窗口的“关闭”按钮时调用。 Cancel 参数指定是否应进行正常取消处理;默认为 TrueConfirm 参数指定是否应显示“退出安装程序?”消息框。

用户可以通过调用 WizardForm.Close() 来触发取消按钮机制,但这仅适用于 Setup 显示向导表单的情况,并且在静默模式下不起作用。

取消详细信息

WizardForm.Close[ref4],或者实际按钮单击,最终将调用 TMainForm.FormCloseQuery(位于 Main.pas 中),该函数将调用 CancelButtonClick 回调[ref5],并根据 Confirm 值执行 TerminateApp(); 或首先调用 helper 函数 ExitSetupMsgBox() 显示消息框给用户。
  • [ref1] : 针对非Delphi人员:如果在文本编辑器中搜索源代码,请确保至少包括.iss .pas.dpr
  • [ref2] : ssInstall异常处理位于issrc\Projects\Main.pasTMainForm.Install通过SetStep(ssInstall, False);调用,并在TMainForm.Install的结尾处的except块中调用TerminateApp.
  • [ref3] : 查看Setup.dprMain.pas调用MainForm.InitializeWizard,当not InstallMode = imNormal即静默情况下,将调用WizardForm.ClickThroughPages
  • [ref4] : WizardForm.Close()内部调用MainForm.Close()(参见:TWizardForm.FormClose
  • [ref5] : 实际上,在Inno [Code]中,有两种可定义的取消按钮单击回调函数:CancelButtonClick全局过程和每个向导页面还具有可以设置的OnCancelButtonClick:TWizardPageCancelEvent

4

请查看InnoSetup帮助文档中的InitializeSetup和Abort。正如Cody所说,这是可能的。如果您遇到问题,请发布您已完成的内容以及您遇到的问题。


1

在你的代码段中的某处,你执行了一个检查,对吧? 作为这个检查的结果,你想要退出安装。 要执行退出,插入以下代码:

PostMessage (WizardForm.Handle, $0010, 0, 0);  { quit setup, $0010=WM_CLOSE }

希望这能有所帮助


这可以很容易地通过使用静默模式(/silent)来绕过。 - Martin Prikryl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接