REALSOFO
REALSOFO

Reputation: 862

Delphi: Issues during download of update file, with Indy (TidHttp)

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;

Upvotes: 0

Views: 1261

Answers (3)

Leo Melo
Leo Melo

Reputation: 196

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;

Upvotes: 1

David Buterbaugh
David Buterbaugh

Reputation: 26

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.

Upvotes: 0

Dsm
Dsm

Reputation: 6013

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.

Upvotes: 1

Related Questions