Inno Setup - 正确定时更新进度条
Posted
技术标签:
【中文标题】Inno Setup - 正确定时更新进度条【英文标题】:Inno Setup - Correctly timing update of progress bar 【发布时间】:2016-12-02 17:02:05 【问题描述】:我忘记了如何在 Inno Setup 中根据条件正确更新进度条,我写了一个代码来更新我在 Wizard 中创建的进度条。
问题是我在 ProgressBar 的最后一个位置获得 95、96、97、98 或 100、101,并且在我运行安装程序时它的更新不时相同。而且我用来划分的值(这里是 6)不会在所有系统上类似地工作,因为它们的性能与每个系统都非常不同。
我想知道一种正确更新进度条而不会出现此类问题的方法。
[Files]
Source: "C:\Program Files (x86)\Inno Setup 5\Examples\MyProg.exe"; DestDir: "app"; Flags: ignoreversion
Source: "C:\InnoCallback.dll"; DestDir: "app"; Flags: ignoreversion
[Code]
Type
TTimerProc = procedure(HandleW, msg, idEvent, TimeSys: LongWord);
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): LongWord; external 'SetTimer@User32.dll stdcall';
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; external 'wrapcallback@tmp\InnoCallback.dll stdcall delayload';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord; external 'KillTimer@User32.dll stdcall';
var
TTimer: LongWord;
ConditionTracker: String;
P: Integer;
TestingPB: TNewProgressBar;
procedure Install;
var
ErrorCode: Integer;
begin
if ShellExec('Open', 'Timeout.exe', '/T 10', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode) = True then
ConditionTracker := 'DONE';
end;
procedure UpdateProgressBar(HandleW, msg, idEvent, TimeSys: LongWord);
begin
if ConditionTracker = 'DONE' then begin
KillTimer( 0, TTimer);
TestingPB.State := npbsPaused;
end else begin
P := P + 1;
Log('ProgressBar Position: ' + IntToStr(P div 6));
TestingPB.Position := P div 6;
if (P div 6) = 100 then P := 600;
end;
end;
procedure InitializeWizard;
begin
TestingPB := TNewProgressBar.Create(WizardForm);
with TestingPB do begin
Parent := WizardForm;
Width := WizardForm.ProgressGauge.Width;
Top := 200;
Left := (WizardForm.ClientWidth - Width) div 2;
Max := 100;
Position := 0;
Hide;
ExtractTemporaryFile('InnoCallback.dll');
P := 0;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then begin
TestingPB.Show;
TTimer := SetTimer( 0, 0, 0, WrapTimerProc(@UpdateProgressBar, 4));
Install;
end else
TestingPB.Hide;
end;
提前致谢。
【问题讨论】:
【参考方案1】:两个主要问题是:
您没有设置计时器间隔(SetTimer
function 的第三个参数)。然后该函数默认为USER_TIMER_MINIMUM
,即 10 毫秒。一些机器可能过于频繁,并且机器可能无法经常执行计时器。
因此,您在不同的机器上会得到不同的结果。而且代码中的幻数都是任意的。
而且在所有机器上,每秒钟执行 100 次计时器会严重浪费系统资源。
使用一些合理且可达到的频率(至少 100 毫秒,甚至更多)。
无论如何,您都不能依赖定时器调用的频率。系统不保证您调用计时器。特别是,如果系统忙于繁重的安装过程,则计时器将不可靠。
您的计算应该基于实时。 GetTickCount
function 通常用于此目的。见How to get time difference in Inno Setup?
您的代码的其他问题:
进度条应将页面 (WizardForm.SelectTasksPage
) 作为其父页面。那么你就不需要显式地隐藏和显示它了。
不要使用绝对坐标。至少缩放它们(ScaleX
和 ScaleY
)。
见Inno Setup Placing image/control on custom page。
无需显式提取InnoCallback.dll
。请改用external '...k@files:InnoCallback.dll
声明。无需安装到app
(除非您以后需要它),使用Flags: dontcopy
。虽然在 Inno Setup 6 中,有 CreateCallback
function,所以你不需要你需要 InnoCallback.dll
。
ConditionTracker
应该是 Boolean
,而不是 string
。
[Code]
function GetTickCount: DWord;
external 'GetTickCount@kernel32 stdcall';
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): LongWord;
external 'SetTimer@User32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
external 'KillTimer@User32.dll stdcall';
var
Timer: LongWord;
Done: Boolean;
TestingPB: TNewProgressBar;
InitialTime: DWord;
const
Duration = 10000;
procedure Install;
var
ErrorCode: Integer;
begin
if ShellExec('Open', 'Timeout.exe', '/T ' + IntToStr(Duration div 1000), '',
SW_HIDE, ewWaitUntilTerminated, ErrorCode) then
begin
Done := True;
end;
end;
procedure UpdateProgressBar(HandleW, msg, idEvent, TimeSys: LongWord);
begin
if Done then
begin
KillTimer(0, Timer);
TestingPB.State := npbsPaused;
TestingPB.Position := TestingPB.Max;
end
else
begin
TestingPB.Position := GetTickCount - InitialTime;
end;
end;
procedure InitializeWizard;
begin
TestingPB := TNewProgressBar.Create(WizardForm);
with TestingPB do
begin
Parent := WizardForm.SelectTasksPage;
Width := WizardForm.ProgressGauge.Width;
Left := WizardForm.ProgressGauge.Left;
Top := ScaleY(200);
Max := Duration;
Position := 0;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then
begin
Timer := SetTimer(0, 0, 100, CreateCallback(@UpdateProgressBar));
InitialTime := GetTickCount;
Install;
end
end;
【讨论】:
谢谢........我忘记的是GetTickCount
:-) .......这很好......
我想在这里问与这个问题有关的一点点......而且也不喜欢将它作为重复发布......我是通过 cmets 在这里问的吗?我想知道这个 ProgressBar,它是如何显示这个 Progress Bar 直到任何任务完成,而不仅仅是一个具有预定义 Max 和 Duration 的 Timeout。
我的意思是诸如ShellExec('Open', ExpandConstant('C:\Setup.exe'), '/VERYSILENT /SUPPRESSMSGBOXES /LOG="F:\Installation.log"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode
.....如何根据这个单独进程的状态正确显示进度条??
好吧,如果你需要那个,你为什么要问固定进度条?这是一个完全不同的问题。无论如何,你所问的通常是不可能的,正如这里提到的:***.com/a/34349900/850848 除非子安装程序可以在这里报告它的状态:***.com/q/39247947/850848 - 虽然似乎另一个 Inno Setup 安装程序中的子安装程序 - 对吗? Inno Setup 无法提供其状态。但如果它是您的安装程序(并且如果您可以修改它),您可以让它报告其状态。但这是一个新问题的任务。
嗨,我正在尝试使用此代码来更新我的进度条,这就是问题所在:i64.tinypic.com/2ildend.png。如何解决?以上是关于Inno Setup - 正确定时更新进度条的主要内容,如果未能解决你的问题,请参考以下文章