Exit from Inno Setup installation from [Code]

后端 未结 6 1955
逝去的感伤
逝去的感伤 2020-12-01 21:37

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

相关标签:
6条回答
  • 2020-12-01 21:47

    You can use Abort() if you are in these events:

    InitializeSetup
    InitializeWizard
    CurStepChanged(ssInstall)
    InitializeUninstall
    CurUninstallStepChanged(usAppMutexCheck)
    CurUninstallStepChanged(usUninstall)
    
    0 讨论(0)
  • 2020-12-01 21:51

    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, MB_OK);
        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, MB_OK);
          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, MB_OK);
          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.


    0 讨论(0)
  • 2020-12-01 21:53

    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.
    0 讨论(0)
  • 2020-12-01 21:56

    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;
    
    0 讨论(0)
  • 2020-12-01 22:02

    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

    0 讨论(0)
  • 2020-12-01 22:07

    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.

    0 讨论(0)
提交回复
热议问题