Link to home
Start Free TrialLog in
Avatar of Mahesh Yadav
Mahesh YadavFlag for India

asked on

Idftp.Get hangs my application.. how to trap the error if connection is lost

I am using Delphi 6 and Indy component 10. In my application i need to download some files from the ftp server. I am using IdFtp.Get method.  Below is my code sample: -

Function TFormMain.DownloadFile(strURL: String; strLocalPath: string): Boolean;
Begin
    Result := False;
    try
        try
            IdFTP1.Connect;
            IdFTP1.Get(strURL, strLocalPath, True);
            Result := True;

        except
            on E: Exception do begin
              MessageDlg(e.Message ,mtError, [mbOK], 0);
              Exit;
            end
        end;
    finally
          IdFTP1.Disconnect;
    end;
end;


Now if after idFTP.Get command is excuted and internet connection gets lost, my application simple hangs. ReadTimeout property & try..except does not seems to be helpful in this senario.

I want to trap this error so that i can reconnect to internet and resume downloading my file.

If anyone can provide sample code that will really be helpful.

Thanks


Avatar of systan
systan
Flag of Philippines image

no you cant resume the download with that try statement.
you can resume it if the ftp server supports it.
if ftpserver support resuming, try to have an idea of that code;
var
  F: TFileStream;
  localsize:integer;
  netsize:integer;
begin
 localsize:=0;
 netsize:=0;

repeat
try
            IdFTP1.Connect;
            if localsize>0 then IdFTP1.Position := localsize;
            IdFTP1.Get(strURL, strLocalPath, True);
            netsize:=IdFTP1.Size;
except
              IdFTP1.Disconnect;
              F := TFileStream.Create(strLocalPath, fmOpenRead or fmShareDenyNone);
              localsize:=F.Size;
end;

until localsize=netsize;// or terminated;
end;

Open in new window

Avatar of Ephraim Wangoya
Do your uploads and downloads in a thread and your application will be responsive. Check the TidStatus and monitor the progress. If you get an error status or there is no response for a defined period of time, clear the channel, and reset the connection.
Avatar of Mahesh Yadav

ASKER

Thanks for your response.

But my problem is if internet connection is lost IdFtp.Get simply hangs my application, it is not coming in exception block. How to catch that error? If i can catch that error then only i can implement what you have suggested about resume downloading.

>>internet connection is lost?
if you unplug the cable? wired/wireless,
Ohm, better try it not on delphi environment, run it as an exe file.
Yes if say i unplug the cable...and internet is not connected after firing the idFtp.Get command

i am running it as a exe file....

ewangoya:

I did try using Thread but it did not solve my problem... may be i did something wrong...

Is it possible for you to create  a sample code for me....

Thanks
is your ftpserver supports resuming?
Not sure, how can i check that?

But even if ftpserver does not support resuming, i still need to either close the application or display some message instead of application getting hanged when internet connection is lost after idFtp.Get.
Put a Disconnect at the first line of the except section.

try
            IdFTP1.Connect;
            /////IdFTP1.Get(STIRNGstrURL, STREAMstrLocalPath, ResumeTrue);
            IdFTP1.Get(STIRNGstrURL, STRINGstrLocalPath, overwriteFALSE, ResumeTRUE);
except
              IdFTP1.Disconnect;
              ///some code here to connect it again if error occurs
end;
But problem is if after idFtp1.Get i disconnect my internet connection... then cursor is not coming to except block... application simply hangs that the issue...
@ziorinfo

I'll create sample code with threading in the weekend if no solution will have been provided by then.
ewangoya:

Thanks. So fat still waiting for the solution :(
what version of delphi your using?
Delphi 6 & Indy 10
ASKER CERTIFIED SOLUTION
Avatar of Ephraim Wangoya
Ephraim Wangoya
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks ewangoya: for the sample code.

But i am not able implement it properly.. My application  runs at seperately in the tray icon.. its job is toh download setup file and after downloading run the setup file.

Now if i implement your thead code, how do i make my application wait untill i hear something from the thread...

In the example, there is a procedure called DoTerminate; which tells you when the thread is done running

Currently it just has a message showmessage('done');

Replace the showmessage with your code to run the setup. Preferebly, use SendMessage to invoke processing of your next action

eg
const
  UM_DOWNLOAD_COMPLETED = WM_USER + 101;

procedure TForm2.Doterminate;
begin
   SendMessage(Handle, UM_DOWNLOAD_COMPLETED, 0, 0);
end;

add method to  the form
procedure TForm2.UMDOWNLOADCOMPLETED(var Message: TMessage); message UM_DOWNLOAD_COMPLETED;

implement
procedure TForm2.UMDOWNLOADCOMPLETED(var Message: TMessage);
begin
   //do something with the download, run your setup
end;
ewangoya:

This is what i am doing: -
I am launching my Download application from my main application. My download application does not apper on the task bar, i am showing it in the tray icon using TCoolTrayIcon. The purpose of this applicaton is to download the file from the server and run it.

Now if i implement your thread example to download the file...
After creating the thread.. excution of my applicaiton continues and it gets terminated as there is nothing else to do....instead my application should wait untill file is downloaded or else an error should be generated if internet connection was lost while downloading the file..

Function TFormMain.DownloadFile(strURL: String; strLocalPath: string): Boolean;
var
  DownloadThread : TTransferThread;
Begin
    Result := False;
    try
        try
            strURL := StringReplace(strURL, 'ftp.yousway.ch', '',[rfReplaceAll, rfIgnoreCase]);
            ThreadTerminated := False;
            DownloadThread := TTransferThread.Create(IdFTP1.Host, IdFTP1.Username, IdFTP1.Password, strURL, strLocalPath, ftpaDownload );
           
{I want to return true once the download is completed or show an error message if download could not be completed or if net connection is lost}
             Result := True;
end;

You need a variable in your form that will store the result of the download and another to signal that the thread is still running. This variable will then be set in the DoTerminate procedure.

On the form
  private
    FDownloadSuccessful: Boolean;
    FThreadRunning: Boolean;

procedure TForm2.DoTerminate;
begin
   FDownloadSuccessful := (Sender as TTransferThread).DownloadSuccessful;
      SendMessage(Handle, UM_DOWNLOAD_COMPLETED, 0, 0);
end;

procedure TForm2.UMDOWNLOADCOMPLETED(var Message: TMessage);
begin
   FThreadRunning := False;
   if FDownloadSuccessful then
  begin
   //run the set

  //After you are done witht the process, thaen only can you close application

  end;
  //close application
  PostMessage(Handle, WM_CLOSE, 0, 0);
end;


//set thread running and do not close your application while it is running
procedure TFormMain.DownloadFile(strURL: String; strLocalPath: string);
var
  DownloadThread : TTransferThread;
Begin
      try
        try
            strURL := StringReplace(strURL, 'ftp.yousway.ch', '',[rfReplaceAll, rfIgnoreCase]);
            FThreadRunning := True;
            DownloadThread := TTransferThread.Create(IdFTP1.Host, IdFTP1.Username, IdFTP1.Password, strURL, strLocalPath, ftpaDownload );
            DownloadThread.OnTerminate := DoTerminate;  //this will signal the thred has ended
        .............

in the thread also add
  TTransferThread = class(TThread)
  private
     FDownloadSuccessful: Boolean;
   .....
   property
      property DownloadSuccessful: Boolean read FDownloadSuccessful;
   ....
   end;
Anytime you get an exception, set FDownloadSuccessful := False

    case FFTPAction of
        ftpaDownload:
        begin
          try
            FFTP.Get(FSourceFile, FDestinationFile, True);
            FDownloadSuccessful := True;
          except
            on E: EIdReadTimeout do
            begin
                FDownloadSuccessful := False;
              //connection is broken or other time out
              //Handle exception
            end;
            on E: Exception do
            begin
               FDownloadSuccessful := False;
              //all other exceptions
            end;
          end;
        end;

   
ewangoya:

thanks for the the sample code. but i am still having problem....

in the time thread u have written

        if (SecondsBetween(Now, FLastUpdateTime) > FMaxIdleTime) then
        begin
          FMonitorThread.Canceled := True;
          IsRunning := False;
        end;

FLastUpdateTime is always 0 not getting set and through this condition is false it still comes inside the if statement n thread is terminated.

any idea?
We need to initalize FLastUpdatetime to current time immediately before we start the thread

FLastUpdatetime := Now;
DownloadThread := TTransferThread.Create(IdFTP1.Host, IdFTP1.Username, IdFTP1.Password, strURL, strLocalPath, ftpaDownload );
Yes i am doing it but in the Timer thread it is still 0 and thread is getting cancelled.

You did not pass the DoWork procedure to the thread, that is what updates the LastUpdateTime to show the thread is running
I have attached an updated version for your reference

For the timer thread, also pass in the DoLastUpdateGetTime procedure


ftpUnit.pas
Unit2.pas
Unit2.dfm