Inno Setup-使Inno Setup Installer向主安装程序报告其安装进度状态 [英] Inno Setup - Make Inno Setup Installer report its installation progress status to master 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
代替LoadStringFromFile
和SaveStringToFile
可能更好. TFileStream
支持阅读共享.使用LoadStringFromFile
和SaveStringToFile
,如果双方碰巧试图同时读写,进度报告有时可能会暂时失败.
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屋!