Delphi XE5 - нужна помощь в странном поведении

Я работаю над классом TLogger, который записывает журналы приложений в файл...

У меня есть способ получения журналов из файла в TMemo: 1. затем назначьте TMemo классу TLogger, назначьте True свойству 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: если True, TMemo заполняет журналы... когда False, мне нужно 2 нажатия на кнопку 4, чтобы получить результаты в TMemo...

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

Есть идеи? Честно говоря, я понятия не имею, в чем причина различий в работе обоих решений :( Я также пробовал ProcessMessages после Self.Memo1.Text := logger.LogsResult;


person Sebastian Xawery Wiśniowiecki    schedule 29.01.2014    source источник


Ответы (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.

Когда вы нажимаете кнопку во второй раз, поток завершает работу (в первый раз), и теперь вы можете прочитать значение.

Причина, по которой работает ваш раздел с комментариями, заключается в том, что вы назначаете текст заметки только тогда, когда поток завершается, т. Е. Закончил свою работу.

person Disillusioned    schedule 06.02.2014