I forgotten how to update a progress bar correctly in Inno Setup according to a condition and I wrote a code to update a progress bar I created in Wizard.
The problem is I am getting 95, 96, 97, 98, or 100, 101 for the ProgressBar's last Position and its updating is not same from time to time when I runs my Installer. And the value I used to divide (here it is 6) won't work on all systems similarly as their performance is very different from each system.
I like to know a way to correctly update progress bar without having such problems.
[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;
Thanks in advance.
The two primary problems are:
You do not set the timer interval (the third argument of the SetTimer
function). The function then defaults to USER_TIMER_MINIMUM
, what is 10 ms. One some machines that can be too frequent and the machine may not be able to execute the timer that often.
Hence you get different results on different machines. And the magic numbers in your code are all arbitrary.
And on all machines, you severely waste system resources by executing the timer 100 times every seconds.
Use some reasonable and attainable frequency (100 ms at least, or even more).
You cannot rely on frequency of the timer call anyway. The system does not guarantee you to call the timer. Particularly, if the system is busy with heavy installation process, the timer would be unreliable.
You should base your calculation on a real time. The GetTickCount
function is commonly used for this. See How to get time difference in Inno Setup?
Other problems with your code:
WizardForm.SelectTasksPage
) as its parent. Then you do not need to explicitly hide and show it.ScaleX
and ScaleY
).InnoCallback.dll
. Use external '...k@files:InnoCallback.dll
declaration instead. No need to install it to {app}
(unless you will need it later), use Flags: dontcopy
. Though in Inno Setup 6, there's CreateCallback
function, so you do not need you need InnoCallback.dll
.ConditionTracker
should probably be Boolean
, not 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;