Basically, you can build a thread that receive all the message (here, it is a function AddEvent). Messages are queued (and timestamped) and written down to the memo when possible (if you're under heavy load...).
Don't forget to clean the memo if it exceeds a number of line, add exception handling etc...
I use something like this :
TThreadedMsgEvent = class( TThread )
private
FLock : TCriticalSection;
FStr : TQueue<String>;
FMemo : TMemo;
function GetEvent : String;
protected
procedure Execute; override;
public
procedure AddEvent( aMsg : String );
constructor Create( AMemo: TMemo );
destructor Destroy; override;
end;
implementation
{ TThreadedMsgEvent }
procedure TThreadedMsgEvent.AddEvent(aMsg: String);
begin
FLock.Acquire;
FStr.Enqueue( FormatDateTime('DD/MM/YY HH:NN:SS.ZZZ',Now)+ ' : '+ aMsg );
FLock.Release;
end;
constructor TThreadedMsgEvent.Create(aMemo: TMemo);
begin
inherited Create(True);
FreeOnTerminate := False;
FOnMessage := ACallBack;
FStr := TQueue<String>.Create();
FLock := TCriticalSection.Create;
FMemo := aMemo;
Resume;
end;
destructor TThreadedMsgEvent.Destroy; override;
begin
FreeAndNil( FStr );
FreeAndNil( FLock );
end;
procedure TThreadedMsgEvent.Execute;
begin
while not Terminated do
begin
try
if (FStr.Count > 0) then
begin
if Assigned( aMemo ) then
begin
TThread.synchronize( procedure
begin
FMemo.Lines.Add( GetEvent );
end; );
end;
end;
except
end;
TThread.Sleep(1);
end;
end;
function TThreadedMsgEvent.GetEvent: String;
begin
FLock.Acquire;
result := FStr.Dequeue;
FLock.Release;
end;
You can also notify this thread with Windows Messages. It might be easier as you won't need any reference to this thread in your classes.