Inno Setup-使Inno Setup Installer向主安装程序报告其安装进度状态 [英] Inno Setup - Make Inno Setup Installer report its installation progress status to master installer

查看:137
本文介绍了Inno Setup-使Inno Setup Installer向主安装程序报告其安装进度状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I currently have two Inno Setup installers working around. I need one of them to report its status as a sub installer to another installer even it running with VERYSILENT command.

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

I need this to display a progress bar in my main installer according to sub installer's installation progress because I don't want any infinite (marquee) progress bars for this.

我还阅读了有关 Delphi中IPC机制的信息.如何在Inno Setup源代码中添加诸如泵之类的通信功能?有任何启动提示吗?

I also read about IPC Mechanism in Delphi. How can I add this communication abilities like pumps to Inno Setup source code? Any tips for starting?

谢谢.

推荐答案

我认为您不需要为此编写精美的IPC东西.只需通过一个临时文件交换信息即可.

I do not think you need to code fancy IPC stuff for this. Just exchange the information via a temporary file.

子安装程序代码:

[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如下所示,或在安装程序的任何其他位置使用:

You can use the InstallChild like below, or on any other place of your installer process:

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 torrent下载实现的答案.

Another good solution would be to use the PrepareToInstall event function. For an example see my answer to Inno Setup torrent download implementation.

对于 CreateCallback函数,您需要Inno设置6.如果您对Inno Setup 5感到困惑,则可以使用 InnoTools中的WrapCallback函数InnoCallback 库.

For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.

使用TFileStream代替LoadStringFromFileSaveStringToFile可能更好. TFileStream支持阅读共享.使用LoadStringFromFileSaveStringToFile,如果双方碰巧试图同时读写,进度报告有时可能会暂时失败.

It might be better to use the TFileStream instead of the LoadStringFromFile and the SaveStringToFile. The TFileStream supports read sharing. With the LoadStringFromFile and the SaveStringToFile, the progress reporting may occasionally temporarily fail, if both sides happen to try to read and write at the same time.

请参见在另一个进程中打开文件时,Inno设置LoadStringFromFile失败.

这显示了如何将子安装程序和主安装程序的进度链接在一起(如果子安装程序未使用/verysilent开关运行,而仅使用/silent来运行):

This shows how the child and master installer progresses are linked (if the child installer is not running with the /verysilent switch, but with the /silent only):

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

If you need to use a standalone progress bar, you can use the following master installer code:

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

这篇关于Inno Setup-使Inno Setup Installer向主安装程序报告其安装进度状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆