Inno Setup - 让Inno安装程序安装程序向主安装程序报告安装进度状态
我目前有两个Inno Setup安装程序。我需要其中的一个将其状态作为子安装程序报告给另一个安装程序,即使它使用VERYSILENT
命令运行。Inno Setup - 让Inno安装程序安装程序向主安装程序报告安装进度状态
我需要这个来根据子安装程序的安装进度在我的主安装程序中显示一个进度条,因为我不需要任何无限(字幕)进度条。
我也读过关于IPC Mechanism in Delphi。我如何将这种通信能力像泵一样添加到Inno Setup源代码中?任何启动提示?
在此先感谢。
我不认为你需要为此编写花哨的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
代替LoadStringFromFile
和SaveStringToFile
。 TFileStream
支持读共享。通过LoadStringFromFile
和SaveStringToFile
,如果双方碰巧试图同时读取和写入,则进度报告可能偶尔会暂时失败。
参见Inno Setup LoadStringFromFile fails when file is open in another process。
这说明孩子和主安装程序进展如何链接(如果孩子安装不与/verysilent
开关运行,但只有/silent
):
如果您需要使用独立进度栏,则可以使用以下主安装程序代码:
#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;
非常感谢您的回答..........我想知道在不安装Master Installer中的“TOutputProgressWizardPage”的情况下是否只能使用'TNewProgressBar'? – GTAVLover
也许吧。但是,您可能在抽取消息队列时遇到问题。这就是'TOutputProgressWizardPage'的使用所能解决的问题。它在“SetProgress”方法中抽取消息队列。 –
如果我只使用进度条,如何抽取消息?通过'.Position'?或其他?我在问,因为我更喜欢在这里只使用进度条,而且我还需要在卸载程序中使用“修复”模式执行此方法,因此运行卸载程序时不存在“WizardForm”。 : - (.........所以只有一个进度条可以在Uninstaller中使用,我想在卸载过程中不能创建TOutputProgressWizardPage。 – GTAVLover