2016-12-03 287 views
2

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

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

我也读过关于IPC Mechanism in Delphi。我如何将这种通信能力像泵一样添加到Inno Setup源代码中?任何启动提示?

在此先感谢。

回答

3

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

儿童安装代码:

[Files] 
Source: InnoCallback.dll; Flags: dontcopy 

[Code] 

type 
    TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord); 

function SetTimer(
    Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord; 
    external '[email protected] stdcall'; 

function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; 
    external '[email protected]:innocallback.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, WrapTimerProc(@ReportProgressProc, 4)); 
    end; 
end; 

主安装程序代码:

#define ChildInstaller "mysetup.exe" 

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

[Code] 

type 
    TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord); 

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

function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; 
    external '[email protected]:innocallback.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, WrapTimerProc(@UpdateProgressProc, 4)); 

    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; 

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


代码使用InnoTools InnoCallback library作为调度计时器。


这可能是最好使用TFileStream代替LoadStringFromFileSaveStringToFileTFileStream支持读共享。通过LoadStringFromFileSaveStringToFile,如果双方碰巧试图同时读取和写入,则进度报告可能偶尔会暂时失败。

参见Inno Setup LoadStringFromFile fails when file is open in another process


这说明孩子和主安装程序进展如何链接(如果孩子安装不与/verysilent开关运行,但只有/silent):

Linked progress


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

#define ChildInstaller "mysetup.exe" 

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

[Code] 

type 
    TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord); 

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

function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; 
    external '[email protected]:innocallback.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, WrapTimerProc(@UpdateProgressProc, 4)); 

    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; 
+0

非常感谢您的回答..........我想知道在不安装Master Installer中的“TOutputProgressWizardPage”的情况下是否只能使用'TNewProgressBar'? – GTAVLover

+0

也许吧。但是,您可能在抽取消息队列时遇到问题。这就是'TOutputProgressWizardPage'的使用所能解决的问题。它在“SetProgress”方法中抽取消息队列。 –

+0

如果我只使用进度条,如何抽取消息?通过'.Position'?或其他?我在问,因为我更喜欢在这里只使用进度条,而且我还需要在卸载程序中使用“修复”模式执行此方法,因此运行卸载程序时不存在“WizardForm”。 : - (.........所以只有一个进度条可以在Uninstaller中使用,我想在卸载过程中不能创建TOutputProgressWizardPage。 – GTAVLover

相关问题