Crea eventos y comparte variables

I am using Delphi 2007 and threads.

My problem (sorry, i'll try to explain better):

1) I created a file "utilities.pas" where i have the function i use more. 2) I created a new program, in this program i have one thread 3) in the execute method of the thread i call one function in my file "utilities.pas". this function connects to an ftp using clever components (tclftp). This components logs the server responce in a dedicated event. What i would like to do is to save the log in a stringlist and then send the stringlist back to the calling thread.

This is part of the file "utilities.pas":

// I created TEventHandlers because it's the only way to assign the event runtime
// without having a class
type
 TEventHandlers = class
  procedure clFtp1SendCommand(Sender: TObject; const AText: string);
 end;

var EvHandler: TEventHandlers; 

// this is the porcedure called from the thread. i want to send the stringlist
// back to it containing the ftp log
procedure Test(VAR slMain: tStringlist);
var cFTP: TclFtp;
begin
 cFTP := TclFtp.Create(nil);

 cFTP.Server := 'XXX';
 cFTP.UserName := 'XXX';
 cFTP.Password := 'XXX';
 cFTP.OnSendCommand := EvHandler.clFtp1SendCommand;

 // i connect to the ftp
 cFTP.Open;

 FreeAndNil(cFTP);
end;

procedure TEventHandlers.clFtp1SendCommand(Sender: TObject; const AText: string);
begin
 // here the component (cftp) sends me back the answer from the server.
 // i am logging it

 // HERE IT'S THE PROBLEM:
 // I can't reach slMain from here.....

 slmain.add(Atext);
end;

this is the calling thread:

procedure TCalcThread.Execute;
var slMain: tstringlist;
begin
  inherited;

  slmain := tstringlist.create(nil);

  Test(slmain);

  if slMain.count > 0 then
    slMain.savetofile('c:\a.txt');

  // i won't free the list box now, but in the thread terminated.
end;

this is the main program:

procedure TfMain.ThreadTerminated(Sender: TObject);
Var ExThread: TCalcThread;
begin
  ExThread := (Sender as TCalcThread);

  if ExThread.slMain.Count > 0 then
    ExThread.slMain.SaveToFile('LOG\Errori.log');

 freeandnil(slMain);
end;

Please can anybody help me in solving this? I really don't know what to do. I hope now it more clear.

p.s. thanks for all the answer..

preguntado el 08 de noviembre de 11 a las 13:11

Typo: slMail should be: slMain -

I assume that you need to do something with slMain after you call cFTP.Open? If so, you should put in a comment. Otherwise, as written, slMain is irrelevant. -

Does procedure "Test" get run within a thread? If so, you should make that clear. -

What determines the parameter list for clFtp1SendCommand? If it's something you wrote yourself, just pass slMain as another parameter. If not, what exactly is Sender going to be? If that's an object that you can attach slMain to, your problem is solved. -

We need more details. What is the list to contain and what do you want to do with the list contents? Like @ChrisThornton says, you could pass the thread instance as the sender and retrieve the list that way. -

3 Respuestas

Another approach would be to have your thread object have its own instance of the stringlist and its own cFTP. If you need to have one "master thread" that everything writes to (perhaps for a summary of what each thread accomplished), use this class: TThreadStringList by Tilo Eckert http://www.swissdelphicenter.ch/torry/showcode.php?id=2167

respondido 08 nov., 11:18

I think one (BAD) approach would be to create a pool of components in the main thread or at design time, and assign one to each thread. i.e. 5 instances of cFTP, 5 stringlists, 5 threads.

Update: Martin James points out why this is a terrible idea, and I agree. So don't do this. Post stays as a deterrent.

respondido 08 nov., 11:18

Breaks encapsualtion - the forms/main thread does not need access to the FTP component/s. There is a lot less chaos if each thread creates its own components and lists - why should the main thread do it? Also, dumping components onto forms that are then used by secondary threads obliges the user to terminate the secondary threads before the form is closed on shutdown to prevent AV/216/217 exceptions. There are far too many 'how do I shut down my thread cleanly' posts on here already - if it can be avoided, avoid it! - Martin James

Agreed. Bad idea. Editing to reflect what not to do. - Chris Thornton

Intercept the event within the thread class, and fire an own typed event from within that handler. Synchronize this call! And try to prevent the global variable. All this as follows:

type
  TFtpSendCommandEvent = procedure(Mail: TStrings; const AText: String) of object;

  TMyThread = class(TThread)
  private
    FclFtp: TclFtp;
    FslMail: TStrings;
    FOnFtpSendCommand: TFtpSendCommandEvent;
    FText: String;
    procedure clFtpSendCommand(Sender: TObject; const AText: String);
    procedure DoFtpSendCommand;
  protected
    procedure Execute; override;
  public
    // You could add this property as parameter to the constructor to prevent the
    // need to assign it separately
    property OnFtpSendCommand: TFtpSendCommandEvent read FOnFtpSendCommand
      write FOnFtpSendCommand;
  end;

// If you dont want to make this a property or private field of the thread class:
var
  EvHandler: TFtpSendCommandEvent;

{ TMyThread }

procedure TMyThread.clFtpSendCommand(Sender: TObject; const AText: string);
begin
  // Store the AText parameter temporarily in a private field: Synchronize only 
  // takes a parameterless method
  FText := AText;
  Synchronize(DoFtpSendCommand);
end;

procedure TMyThread.DoFtpSendCommand;
begin
  if Assigned(FOnFtpSendCommand) then
    FOnFtpSendCommand(FslMail, FText);
  // Or, if you really like to use that global variable:
  if Assigned(EvHandler) then
    EvHandler(FslMail, FText);
end;

procedure TMyThread.Execute;
begin
  ...
  FclFtp := TclFtp.Create(nil);
  FslMail := TStringList.Create(nil);
  try
    FclFtp.Server := 'XXX';
    FclFtp.UserName := 'XXX';
    FclFtp.Password := 'XXX';
    FclFtp.OnSendCommand := clFtpSendCommand;
    FclFtp.Open;
  finally
    FreeAndNil(FclFtp);
    FreeAndNil(FslMail);
  end;
  ...
end;

respondido 08 nov., 11:22

Hi, thanks for all the help. sorry if i did not explain very well before but my function (test) it's not in the tread directly. It's called from a thread and it's part of a file called "utilities.pas". so i don't think i can use this. I also thought about creating a big thread class inside "utilities.pas" containing all the procedures I use more often but then i stopped because i don't know how to call all of them from the main program since I would have to call the function "RunThread" and not the procedures directly... - lorife

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.