【问题标题】:Make Inno Setup Installer report its installation progress status to master installer让 Inno Setup Installer 向主安装程序报告其安装进度状态
【发布时间】:2017-04-18 06:18:51
【问题描述】:

我目前有两个 Inno Setup 安装程序正在工作。我需要其中一个将其作为子安装程序的状态报告给另一个安装程序,即使它使用VERYSILENT 命令运行。

我需要这个来根据子安装程序的安装进度在我的主安装程序中显示一个进度条,因为我不想要任何无限(字幕)进度条。

我还读到了IPC Mechanism in Delphi。如何将这种通信能力(如泵)添加到 Inno Setup 源代码?有什么入门技巧吗?

提前致谢。

【问题讨论】:

    标签: installation progress-bar inno-setup ipc


    【解决方案1】:

    我认为您不需要为此编写花哨的 IPC 代码。只需通过临时文件交换信息。

    子安装程序代码:

    [Code]
    
    function SetTimer(
      Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
      external 'SetTimer@user32.dll stdcall';
    
    var
      ProgressFileName: string;
      PrevProgress: Integer;
    
    procedure ReportProgressProc(
      H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
    var
      Progress: Integer;
    begin
      try
        Progress :=
          (WizardForm.ProgressGauge.Position * 100) div WizardForm.ProgressGauge.Max;
        if PrevProgress <> Progress then
        begin
          if not SaveStringToFile(ProgressFileName, IntToStr(Progress), False) then
          begin
            Log(Format('Failed to save progress %d', [Progress]));
          end
            else
          begin
            Log(Format('Saved progress %d', [Progress]));
            PrevProgress := Progress;
          end;
        end;
      except
        Log('Exception saving progress');
      end;
    end;  
    
    procedure InitializeWizard();
    begin
      { When run with /progress=<path> switch, will report progress to that file }
      ProgressFileName := ExpandConstant('{param:progress}');
      if ProgressFileName <> '' then
      begin
        Log(Format('Will write progress to: %s', [ProgressFileName]));
        PrevProgress := -1;
        SetTimer(0, 0, 250, CreateCallback(@ReportProgressProc));
      end;
    end;
    

    主安装程序代码:

    #define ChildInstaller "mysetup.exe"
    
    [Files]
    Source: {#ChildInstaller}; Flags: dontcopy
    
    [Code]
    
    function SetTimer(
      Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
      external 'SetTimer@user32.dll stdcall';
    function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
      external 'KillTimer@user32.dll stdcall';
    
    var
      ProgressPage: TOutputProgressWizardPage;
      ProgressFileName: string;
    
    procedure UpdateProgressProc(
      H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
    var
      S: AnsiString;
      Progress: Integer;
    begin
      try
        if not LoadStringFromFile(ProgressFileName, S) then
        begin
          Log(Format('Failed to read progress from file %s', [ProgressFileName]));
        end
          else
        begin
          Progress := StrToIntDef(S, -1);
          if (Progress < 0) or (Progress > 100) then
          begin
            Log(Format('Read invalid progress %s', [S]));
          end
            else
          begin
            Log(Format('Read progress %d', [Progress]));
            ProgressPage.SetProgress(Progress, 100);
          end;
        end;
      except
        Log('Exception updating progress');
      end;
    end;
    
    procedure InstallChild;
    var
      ChildInstallerPath: string;
      ChildInstallerParams: string;
      Timer: LongWord;
      InstallError: string;
      ResultCode: Integer;
    begin
      ExtractTemporaryFile('{#ChildInstaller}');
      
      ProgressPage := CreateOutputProgressPage('Running child installer', '');
      ProgressPage.SetProgress(0, 100);
      ProgressPage.Show;
      try
        Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));
    
        ChildInstallerPath := ExpandConstant('{tmp}\{#ChildInstaller}');
        ProgressFileName := ExpandConstant('{tmp}\progress.txt');
        Log(Format('Expecting progress in %s', [ProgressFileName]));
        ChildInstallerParams :=
          Format('/verysilent /progress="%s"', [ProgressFileName]);
        if not Exec(ChildInstallerPath, ChildInstallerParams, '', SW_SHOW,
                    ewWaitUntilTerminated, ResultCode) then
        begin
          InstallError := 'Cannot start child installer';
        end
          else
        if ResultCode <> 0 then
        begin
          InstallError :=
            Format('Child installer failed with code %d', [ResultCode]);
        end;
      finally
        { Clean up }
        KillTimer(0, Timer);
        ProgressPage.Hide;
        DeleteFile(ProgressFileName);
      end;
    
      if InstallError <> '' then
      begin 
        // RaiseException does not work properly,
        // while TOutputProgressWizardPage is shown
        RaiseException(InstallError);
      end;
    end;
    

    您可以像下面那样使用InstallChild,也可以在安装程序的任何其他位置使用:

    function NextButtonClick(CurPageID: Integer): Boolean;
    begin
      Result := True;
    
      if CurPageID = wpReady then
      begin
        try
          InstallChild;
        except
          MsgBox(GetExceptionMessage, mbError, MB_OK);
          Result := False;
        end;
      end;
    end;
    

    另一个好的解决方案是使用PrepareToInstall event function。例如,请参阅我对Inno Setup torrent download implementation 的回答。


    对于CreateCallback function,你需要 Inno Setup 6。如果你被 Inno Setup 5 卡住了,你可以使用 InnoTools InnoCallback 库中的 WrapCallback 函数。


    最好使用TFileStream 而不是LoadStringFromFileSaveStringToFileTFileStream 支持阅读共享。使用LoadStringFromFileSaveStringToFile,如果双方碰巧同时尝试读写,进度报告可能偶尔会暂时失败。

    Inno Setup LoadStringFromFile fails when file is open in another process


    这显示了子安装程序和主安装程序的进度是如何链接的(如果子安装程序没有使用/verysilent 开关运行,但仅使用/silent):


    如果您需要使用独立的进度条,您可以使用以下主安装程序代码:

    #define ChildInstaller "mysetup.exe"
    
    [Files]
    Source: {#ChildInstaller}; Flags: dontcopy
    
    [Code]
    
    function SetTimer(
      Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
      external 'SetTimer@user32.dll stdcall';
    function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
      external 'KillTimer@user32.dll stdcall';
    
    var
      ProgressFileName: string;
    
    procedure UpdateProgressProc(
      H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
    var
      S: AnsiString;
      Progress: Integer;
    begin
      try
        if not LoadStringFromFile(ProgressFileName, S) then
        begin
          Log(Format('Failed to read progress from file %s', [ProgressFileName]));
        end
          else
        begin
          Progress := StrToIntDef(S, -1);
          if (Progress < 0) or (Progress > 100) then
          begin
            Log(Format('Read invalid progress %s', [S]));
          end
            else
          begin
            Log(Format('Read progress %d', [Progress]));
            WizardForm.ProgressGauge.Position :=
              Progress * WizardForm.ProgressGauge.Max div 100;
          end;
        end;
      except
        Log('Exception updating progress');
      end;
    end;
    
    procedure InstallChild;
    var
      ChildInstallerPath: string;
      ChildInstallerParams: string;
      Timer: LongWord;
      ResultCode: Integer;
    begin
      ExtractTemporaryFile('{#ChildInstaller}');
    
      try
        Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));
    
        ChildInstallerPath := ExpandConstant('{tmp}\{#ChildInstaller}');
        ProgressFileName := ExpandConstant('{tmp}\progress.txt');
        Log(Format('Expecting progress in %s', [ProgressFileName]));
        ChildInstallerParams :=
          Format('/verysilent /progress="%s"', [ProgressFileName]);
        if not Exec(ChildInstallerPath, ChildInstallerParams, '', SW_SHOW,
                    ewWaitUntilTerminated, ResultCode) then
        begin
          MsgBox('Cannot start child installer', mbError, MB_OK);
        end
          else
        if ResultCode <> 0 then
        begin
          MsgBox(Format(
            'Child installer failed with code %d', [ResultCode]), mbError, MB_OK);
        end;
      finally
        { Clean up }
        KillTimer(0, Timer);
        DeleteFile(ProgressFileName);
      end;
    end;
    
    procedure CurStepChanged(CurStep: TSetupStep);
    begin
      if CurStep = ssInstall then
      begin
        InstallChild;
      end;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-11
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      相关资源
      最近更新 更多