martinarcher
martinarcher

Reputation: 137

TIdFTP in a thread not firing Work method

I've got a VCL application written in Embarcadero's C++ 10 Seattle. The main thread spawns a thread to run the TIdFTP functionality since the Get method indefinitely hangs if the connection is lost during the transfer. The idea is to detect if the connection has hung, orphan the thread, and spawn a "retry". My problem is I've declared the object in the thread's h file as well as the methods that should fire (WorkBegin, WorkEnd, Work, etc) like follows....

class FTPThread : public TThread
{
protected:
    void __fastcall Execute();
    void __fastcall UpdateUI();
public:
    __fastcall FTPThread(bool CreateSuspended);
    String FTPhostname;
    String FTPusername;
    String FTPpassword;
    String remoteDir;
    String localDir;
    String localTempDir;
    String fileName;
    TIdFTP *FTPClnt;
    void __fastcall FTPClntWorkBegin(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax);
    void __fastcall FTPClntWorkEnd(TObject *ASender, TWorkMode AWorkMode);
    void __fastcall FTPClntWork(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount);
    void __fastcall FTPClntAfterGet(TObject *ASender, TStream *AStream);

};

I then implemented the Work functions in the thread's cpp file like this.....

void __fastcall FTPThread::UpdateUI()
{
    if (FrmFTP) {
        //FrmFTP->FTPProg->Max = fileSizeBytes;
        FrmFTP->FTPProg->Position = ftpProgress;
        FrmFTP->BringToFront();
        Application->ProcessMessages();
    }
}

void __fastcall FTPThread::FTPClntWorkBegin(TObject *ASender,
    TWorkMode AWorkMode, __int64 AWorkCountMax) {
    ftpProgress = 0;
    if (FrmFTP) {
        FrmFTP->FTPProg->Max = fileSizeBytes;
    }
    Synchronize(&UpdateUI);
}
// ---------------------------------------------------------------------------

void __fastcall FTPThread::FTPClntWorkEnd(TObject *ASender,
    TWorkMode AWorkMode)

{
    if (FrmFTP) {
        ftpProgress = FrmFTP->FTPProg->Max;
    }
    Synchronize(&UpdateUI);
}
// ---------------------------------------------------------------------------

void __fastcall FTPThread::FTPClntWork(TObject *ASender, TWorkMode AWorkMode,
    __int64 AWorkCount)

{
   ftpProgress = AWorkCount;
   Synchronize(&UpdateUI);
}
// ---------------------------------------------------------------------------

void __fastcall FTPThread::FTPClntAfterGet(TObject *ASender,
    TStream *AStream) {
    ftpCompletionStatus = 1;
}
// ---------------------------------------------------------------------------

None of these methods are ever fired as the Get begins, transfers, and completes. Any idea why they wouldn't fire? The same functionality on my main thread worked fine. I feel like I'm somehow not trying these methods I've implemented to the TIdFTP Object but I'm not sure how to go about doing that.

Thanks!

Upvotes: 0

Views: 260

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596497

the Get method indefinitely hangs if the connection is lost during the transfer.

It should fail eventually. It just might take awhile for the OS to actually detect the lost connection and invalidate the open sockets so I/O operations start reporting errors. If you don't want to wait that long, make sure to set the ReadTimeout property to a reasonable timeout (TIdFTP set it to 1 minute by default), as well as the TransferTimeout property (infinite by default).

The idea is to detect if the connection has hung, orphan the thread, and spawn a "retry".

NEVER "orphan" a thread. You could simply have the main thread call TIdFTP's Abort() and Disconnect() methods instead, to terminate a transfer in progress and close the command socket, respectively.

My problem is I've declared the object in the thread's h file as well as the methods that should fire (WorkBegin, WorkEnd, Work, etc) like follows....

DO NOT call Application->ProcessMessages() inside of a synched method. The method is already running inside of a main thread message handler, just exit the synched method so control returns to the main message loop.

Also, your OnWork... handlers are directly accessing FrmFTP->FTPProg->Max from outside of the main thread. Those accesses need to be synched as well. ANYTHING that touches the main UI needs to be synched.

None of these methods are ever fired as the Get begins, transfers, and completes. Any idea why they wouldn't fire?

Did you hook them up to begin with? Presumably FTPClnt is being created in FTPThread::Execute(), but is it assigning your handlers to the FTPClnt->OnWork... events after creating FTPClnt? They are not going to hook themselves up for you, you have to do it yourself, eg:

FTPClnt = new TIdFTP;
FTPClnt->OnWorkBegin = &FTPClntWorkBegin;
FTPClnt->OnWorkEnd = &FTPClntWorkEnd;
FTPClnt->OnWork = &FTPClntWork;

The same functionality on my main thread worked fine.

If you drop a TIdFTP object on a Form at design-time, and assign its OnWork... events within the Object Inspector, then the DFM streaming system hooks up the event handlers for you when the Form is created at runtime.

Simply declaring event handlers is not enough. Somebody (you or the DFM) has to actually hook them up at runtime.

Upvotes: 1

Related Questions