使 Inno Setup Installer 向主安装程序报告其安装进度状态

问题描述 投票:0回答:1

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

VERYSILENT
命令运行。

我需要它根据子安装程序的安装进度在我的主安装程序中显示进度条,因为我不想要任何无限(选取框)进度条。

我还阅读了Delphi 中的 IPC 机制。如何将泵等通信功能添加到 Inno Setup 源代码中?有什么入门建议吗?

提前致谢。

installation progress-bar inno-setup ipc
1个回答
6
投票

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

子安装程序代码:

[Code]

function SetTimer(
  Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
  external '[email protected] 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 '[email protected] stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
  external '[email protected] 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
事件函数。有关示例,请参阅我对 Inno Setup 种子下载实现的回答。


对于

CreateCallback
函数,您需要 Inno Setup 6。如果您无法使用 Inno Setup 5,您可以使用
InnoTools InnoCallback
库中的 WrapCallback 函数。


最好使用

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

请参阅 Inno Setup LoadStringFromFile 在另一个进程中打开文件时失败


这显示了子安装程序和主安装程序的进度如何链接(如果子安装程序未使用

/verysilent
开关运行,而是仅使用
/silent
运行):


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

#define ChildInstaller "mysetup.exe"

[Files]
Source: {#ChildInstaller}; Flags: dontcopy

[Code]

function SetTimer(
  Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
  external '[email protected] stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
  external '[email protected] 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;
© www.soinside.com 2019 - 2024. All rights reserved.