0
  • Q1: Why during downloading process the modal form cannot be closed
  • Q2: Why when download is finished the progress bar doesn't rich 100% (it's a manner of repaint?)
  • Q3: Why if I stop and restart the connection to the web server during download the transfer is stopping without indicating any error and never continue? What can I do to get and catch the error and go back to initial status (download and install with progress bar at position 0)

Remark: IdAntiFreeze is active

procedure Tform_update.button_downloadClick(Sender: TObject);
var
  FS: TFileStream;
  url, file_name: String;
begin
  //execute download
  if button_download.Tag = 0 then
    begin
      Fdone:= False;
      Fcancel:= False;

      url:= APP_DOMAIN + '/downloads/Setup.exe';
      file_name:= 'C:\Temp\Setup.exe';

      if FileExists(file_name) then DeleteFile(file_name);

      try
        FS:= TFileStream.Create(file_name, fmCreate);
        Http:= TIdHTTP.Create(nil);

        Http.OnWorkBegin:= HttpWorkBegin;
        Http.OnWork:= HttpWork;

        Http.Get(url, FS);
      finally
        FS.Free;
        Http.Free;
        if Fdone then ModalResult:= mrOk;
      end;
    end
  else
    //cancel download
    begin
      Fcancel:= True;
    end;
end;

procedure Tform_update.HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  ContentLength: Int64;
  Percent: Integer;
begin
  ContentLength:= Http.Response.ContentLength;
  if AWorkCount = ContentLength then Fdone:= True; //

  if (Pos('chunked', LowerCase(Http.Response.TransferEncoding)) = 0) and (ContentLength > 0) then
    begin
      sleep(15);
      Percent := 100 * AWorkCount div ContentLength;
      progress_bar.Position:= Percent;
    end;

  //stop download
  if Fcancel and Http.Connected then
    begin
      Http.IOHandler.InputBuffer.Clear;
      Http.Disconnect;
      Fcancel:= False;

      button_download.Caption:= _('Download and Install');
      button_download.Tag:= 0;
      progress_bar.Position:= 0;
    end;
end;

procedure Tform_update.HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
  if AWorkMode <> wmRead then Exit;

  button_download.Tag:= 1;
  button_download.Caption:= _('Cancel');
end;

procedure Tform_update.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Fcancel:= True;
end;
mjn
  • 36,362
  • 28
  • 176
  • 378
REALSOFO
  • 852
  • 9
  • 37
  • You've apparently missed how this site works. This is a **Question and Answer** site (singular), not **Multiple Questions and Answers** (plural). Posts should contain a single question. If you have more than one, create separate posts; you can always link back to one or more of the others to provide reference material. – Ken White Jul 16 '15 at 17:11
  • Thank you all guys for quick responses! All answers are helpful. I will do it with threading. @KenWhite I will keep in mind for next questions. – REALSOFO Jul 16 '15 at 18:55

3 Answers3

1

A different approach is to use TThread to control the execution. Something like this:

  ThreadUpdate = class(TThread)
  protected
    procedure Execute; override;
  public

procedure ThreadUpdate.Execute;
begin
  inherited;
  while (not terminated) do 
  begin
      //YOUR CODE HERE - maybe your button_download Click
      Terminate;
  end;
end;   

Also you may try to let Windows process messages for your app.

  if (Pos('chunked', LowerCase(Http.Response.TransferEncoding)) = 0) and (ContentLength > 0) then
  begin
    sleep(15);
    Percent := 100 * AWorkCount div ContentLength;
    progress_bar.Position:= Percent;
    **Application.ProcessMessages;**
  end;
Leo Melo
  • 196
  • 5
  • 1
    This answer is basic at best. For example, one must consider thread safety when it concerns UI access from a non UI thread. – whosrdaddy Jul 16 '15 at 14:39
1

Q1. Indy is blocking. All Antifreeze does invoke windows messages processing regularly. It doesn't stop the blocking nature of Indy and so unless you explicitly have a way to handle errors it won't behave how you want. You need to do the download in a different thread and use your form to monitor the status of that thread rather than try and rely on antifreeze. Don't put any UI actions in that thread, leave them in the main thread, so don't try and update the progress bar from within the thread. Set a synchronised variable to the progress percentage and read that from a timer in the main thread, for example. Remember that UI components are not thread safe and so should only ever be updated from a single thread.

Q2. I've seen that too. Nothing to do with Indy. I think that when you set the status bar to 100% the component does not immediately respond but tries to move smoothly to that point (but doesn't have time). That is just a guess, though. I am not sure. Or it may be the frequency with which antifreeze processes messages I guess (in which case it is to do with Indy).

Q3. Really the same as Q1, with the same solution. Put in a separate thread and monitor the status of that thread from the main thread.

Once you have moved the Indy actions to a separate thread, you should not need Antifreeze.

Dsm
  • 5,870
  • 20
  • 24
0

Regarding Q1 and Q2, a thread is certainly better. If you decide to keep using Indy Antifreeze, you should make sure the OnlyWhenIdle flag is set to False so it can process messages whenever work is done.