-->

德尔福XE5 - 奇怪的行为需要帮助(Delphi XE5 - strange behavior

2019-10-19 06:27发布

我的工作是记录我的应用程序日志保存至文件TLogger类...

我必须让从文件记录到TMemo的方式:1分配TMemo到TLogger类,那么,分配给真实财产DisplayInMemo,那么就调用GetLogFromFile(); 2.呼叫GetLogsFromFile(); 然后Self.Memo1.Text:= TLogger.LogsResult;

下面...评论的解决方案正常工作......未加注释的解决方案只能每2点击按钮4

procedure TForm1.Button4Click(Sender: TObject);   // get log.file to memo
begin
  // automatic forwarding logs from File to TMemo - it works!
  //logger.DisplayMemo := Self.Memo1;
  //logger.DisplayInMemo := True;
  //logger.GetLogsFromFile();

  // tested - half-automatic method of formwarding logs to TMemo - works every 2 clicks :(
  logger.DisplayInMemo := False;
  logger.GetLogsFromFile();
  Self.Memo1.Text := logger.LogsResult;
end;

全TLogger实现:

unit Logger;

interface

uses
  System.IOUtils, System.TypInfo, System.SysUtils, FMX.Forms, FMX.Dialogs, System.Classes, FMX.Graphics, FMX.ExtCtrls, LoggerThread, FMX.Memo;

type

  TLogger = class
  private
    FileName : String;                // name of file to log
    FilePath : String;                // path to app / log-file

    LStringResult : String;           // result of thread log.file reading
    LLoggerMemo : TMemo;              // copy of memo - place where GetLogsFromFile put results

    LDisplayInMemo : Boolean;         // bool - if True  GetLogsFromFile puts results to DisplayMemo, otherwise waiting in LogsResult
    NewLoggerThread : TLoggerThread;  // thread object - created in Create()

    procedure GetLogsFromFileThreadTerminateHandler(sender: TObject);

  public
    constructor Create(); overload;                     // open or create 'development.log'
    constructor Create(LogFileName : String); overload; // open or create LogFileName for logging
    destructor Destroy(); overload;                     // cleaner of TLogger object
    // main procedures
    procedure Log(LogString : String);                  // add line to log file
    procedure GetLogsFromFile();                        // get all logs from log file to string
    // settings, reading results,
    property DisplayInMemo : Boolean read LDisplayInMemo write LDisplayInMemo; //bool - if True  GetLogsFromFile puts results to DisplayMemo, otherwise waiting in LogsResult
    property LogsResult : String read LStringResult write LStringResult;       //string results after Getters from TLogger usage
    property DisplayMemo : TMemo read LLoggerMemo write LLoggerMemo;           // sets TMemo where results will be put if DisplayInMemo set to True
  end;

implementation

  constructor TLogger.Create();
  begin
    {$IFDEF Android}
      FilePath := TPath.GetDownloadsPath + System.SysUtils.PathDelim;
    {$ELSE}
      FilePath := ExtractFilePath(ParamStr(0));
    {$ENDIF}
    FileName := 'development.log';
    LDisplayInMemo := False;
    // inherited
    inherited Create;
  end;

  constructor TLogger.Create(LogFileName : String);
  begin
    {$IFDEF Android}
      FilePath := TPath.GetDownloadsPath + System.SysUtils.PathDelim;
      //TPath.Combine(TPath.GetDocumentsPath,'test.txt');  // to have / \ auto-change
    {$ELSE}
      FilePath := ExtractFilePath(ParamStr(0));
    {$ENDIF}
    FileName := LogFileName;
    LDisplayInMemo := False;
    // inherited
    inherited Create;
  end;

  destructor TLogger.Destroy();
  begin
    inherited Destroy;
  end;

  // adds a sigle line to log file with date time
  procedure TLogger.Log(LogString : String);
  begin
    NewLoggerThread :=  TLoggerThread.Create(True);
    NewLoggerThread.FreeOnTerminate := True;
    NewLoggerThread.Log := LogString;                                    //log to write - date time then added in execute
    NewLoggerThread.LoggerInstruction := TLoggerInstruction.liLogToFile; //set instuction for thread - LogToFile
    NewLoggerThread.FileName := FileName;  //file to write
    NewLoggerThread.FilePath := FilePath;  //path to file

    try
      NewLoggerThread.Start;
    except
      NewLoggerThread.Free();
    end;

  end;

  // results String with LogFile content
  procedure TLogger.GetLogsFromFile();
  begin
    NewLoggerThread :=  TLoggerThread.Create(True);
    NewLoggerThread.FreeOnTerminate := True;
    NewLoggerThread.OnTerminate := GetLogsFromFileThreadTerminateHandler;
    NewLoggerThread.FileName := FileName;  //file to write
    NewLoggerThread.FilePath := FilePath;  //path to file
    NewLoggerThread.LoggerInstruction := TLoggerInstruction.liGetLogsFromFile; //set instuction for thread - GetLogFromFile

    try
      NewLoggerThread.Start;
    except
      NewLoggerThread.Free();
    end;

  end;

  procedure TLogger.GetLogsFromFileThreadTerminateHandler(sender: TObject);
  begin
    LStringResult := (Sender as TLoggerThread).StringResult;
    if LDisplayInMemo then
        LLoggerMemo.Text := (Sender as TLoggerThread).StringResult;
  end;

end.

正如你所看到的唯一区别是在LDisplayInMemo:如果是真TMemo原木罢了......是假,当我需要点击两下就按钮4得到TMemo结果...

  procedure TLogger.GetLogsFromFileThreadTerminateHandler(sender: TObject);
  begin
    LStringResult := (Sender as TLoggerThread).StringResult;
    if LDisplayInMemo then
        LLoggerMemo.Text := (Sender as TLoggerThread).StringResult;
  end;

有任何想法吗? 说实话,我不知道什么是diffenerce在这两种解决方案的工作的原因:(我也Self.Memo1.Text后试图ProcessMessages:= logger.LogsResult;

Answer 1:

究其原因,下面的代码只能点击按钮后,第二次是你的代码真正得到的信息在另一个线程中运行日志...它是异步的

logger.DisplayInMemo := False;
logger.GetLogsFromFile();
Self.Memo1.Text := logger.LogsResult; //This line runs AT THE SAME TIME you're getting logs!

注意 :您正在阅读的价值logger.LogsResult它会从你的LoggerThread值之前

当您单击该按钮第二次,该线程运行完毕后(第一次),你现在能够读取值。

您的评论部分的作品,究其原因是你在线程终止时只分配备忘录文本 - 即做完这件事的工作。



文章来源: Delphi XE5 - strange behavior help needed