1

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.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
GTAVLover
  • 1,407
  • 3
  • 22
  • 41

1 Answers1

1

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:

  • The progress bar should have the page (WizardForm.SelectTasksPage) as its parent. Then you do not need to explicitly hide and show it.
  • Do not use absolute coordinates. At least scale them (ScaleX and ScaleY).
    See Inno Setup Placing image/control on custom page.
  • No need to explicitly extract the 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;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • Thank You........What I forgotten is `GetTickCount` :-) .......This Works Well....... – GTAVLover Dec 03 '16 at 00:40
  • I want to ask little bit related to this question here.......And also don't like to post it as a Duplicate.....Am I ask it here via comments? I like to know about this ProgressBar, it is how to display this Progress Bar until any task completes, no only a Timeout with a predefined Max and Duration. – GTAVLover Dec 03 '16 at 01:17
  • I mean such as `ShellExec('Open', ExpandConstant('C:\Setup.exe'), '/VERYSILENT /SUPPRESSMSGBOXES /LOG="F:\Installation.log"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode`..... How to display a Progress Bar correctly according to this separate process's state?? – GTAVLover Dec 03 '16 at 03:39
  • Well, if you need that, why do you ask about fixed progress bar? That's a completely different question. Anyway, what you ask is generally not possible, as mentioned here: http://stackoverflow.com/a/34349900/850848 except if the sub-installer can reports its status as here: http://stackoverflow.com/q/39247947/850848 - Though it seems that the subinstaller in another Inno Setup installer - right? Inno Setup cannot provide its status. But if it is your installer (and if you can modify it), you can make it report its status. But that's a task for a new question. – Martin Prikryl Dec 03 '16 at 07:40
  • Hi, I am try to use this code to update my progressbar and this is the problem: http://i64.tinypic.com/2ildend.png. How to solve this? – Nico Z Jan 10 '17 at 14:47
  • @NicoZ You should finally learn to describe your problem in words. I do not see any problem on the screenshot. – Martin Prikryl Jan 10 '17 at 14:50
  • @MartinPrikryl Sorry, http://i67.tinypic.com/2wntq45.png 100% but the progressbar is not complete. – Nico Z Jan 10 '17 at 14:51
  • @NicoZ How are those labels related to this question? There are no labels here. – Martin Prikryl Jan 10 '17 at 14:57
  • @MartinPrikryl, sorry. This is my code (minimal) http://textuploader.com/dd7d6 (elapsed time, etc + Correctly timing update of progress bar) – Nico Z Jan 10 '17 at 20:12
  • @NicoZ Sorry, but this is hardly minimal. What does the "long component description" code have to do with the problem? Anyway, once you produce really minimal code, you should post a new question. – Martin Prikryl Jan 10 '17 at 20:14
  • @MartinPrikryl , "long component description" use TTimer function like a ¨Correctly timing update of progress bar¨ code. I think that the error may be due to this. Code without the errors for duplicate identifier http://textuploader.com/dd718 – Nico Z Jan 10 '17 at 20:38
  • @MartinPrikryl , Code without duplicate identifier errors http://textuploader.com/dd718 – Nico Z Jan 10 '17 at 20:44