2011-11-08 3 views
1

J'utilise Delphi 2007 et les threads.Créer un événement et partager des variables

Mon problème (désolé, je vais essayer d'expliquer mieux):

1) Je crée un fichier "utilities.pas" où j'ai la fonction i utiliser plus. 2) J'ai créé un nouveau programme, dans ce programme j'ai un thread 3) dans la méthode d'exécution du thread j'appelle une fonction dans mon fichier "utilities.pas". cette fonction se connecte à un ftp en utilisant des composants intelligents (tclftp). Ce composant enregistre le serveur responce dans un événement dédié. Ce que je voudrais faire est de sauvegarder le journal dans une liste de chaînes, puis de renvoyer la liste de chaînes au thread appelant.

Cela fait partie du fichier "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; 

c'est le fil d'appel:

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; 

c'est le programme principal:

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; 

S'il vous plaît quelqu'un peut-il m'aider à résoudre ce problème? Je ne sais vraiment pas quoi faire. J'espère que maintenant c'est plus clair.

p.s. merci pour toute la réponse ..

+0

Typo: SLmail doit être: slMain –

+0

Je suppose que vous devez faire quelque chose avec slMain après que vous appelez cFTP.Open? Si oui, vous devriez ajouter un commentaire. Sinon, comme écrit, slMain n'est pas pertinent. –

+0

La procédure "Test" est-elle exécutée dans un thread? Si oui, vous devriez le préciser. –

Répondre

0

Je pense qu'une approche (BAD) serait de créer un groupe de composants dans le thread principal ou au moment du design, et en affecter un à chaque thread. c'est-à-dire 5 instances de cFTP, 5 listes de chaînes, 5 discussions.

Mise à jour: Martin James souligne pourquoi c'est une idée terrible, et je suis d'accord. Alors ne fais pas ça. La poste reste dissuasive.

+1

Rompre l'encapsualtion - les formes/fil principal n'ont pas besoin d'accès aux composants de ftp. Il y a beaucoup moins de chaos si chaque thread crée ses propres composants et listes - pourquoi le thread principal devrait-il le faire? De même, le fait de décharger des composants sur des formulaires qui sont ensuite utilisés par des threads secondaires oblige l'utilisateur à terminer les threads secondaires avant la fermeture du formulaire lors de l'arrêt pour éviter les exceptions AV/216/217. Il y a beaucoup trop de «comment fermer mon fil proprement» - si cela peut être évité, évitez-le! –

+0

D'accord. Mauvaise idée. Modification pour refléter quoi ne pas faire. –

0

Une autre approche consisterait à avoir votre objet thread possède sa propre instance de la liste de chaînes et son propre cFTP. Si vous avez besoin d'avoir un « fil maître » que tout écrit (peut-être un résumé de ce que chaque fil accompli), utilisez cette classe: TThreadStringList par Tilo Eckert http://www.swissdelphicenter.ch/torry/showcode.php?id=2167

0

Intercept l'événement dans la classe de fil, et déclenche un propre événement typé depuis ce gestionnaire. Synchronisez cet appel! Et essayez d'empêcher la variable globale. Tout cela comme suit:

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; 
+0

Salut, merci pour toute l'aide. désolé si je n'ai pas très bien expliqué avant mais ma fonction (test) ce n'est pas dans la foulée directement. Il est appelé à partir d'un thread et fait partie d'un fichier appelé "utilities.pas". donc je ne pense pas que je peux l'utiliser. J'ai aussi pensé à créer une classe de gros threads à l'intérieur de "utilities.pas" contenant toutes les procédures que j'utilise plus souvent mais ensuite je me suis arrêté parce que je ne sais pas comment les appeler tous du programme principal puisque je devrais appeler le fonction "RunThread" et non les procédures directement ... – lorife

Questions connexes