//This thread is used to separately handle events for creating
//message dialog which is not modal and not stops the other
//threads in the application.
//The thread queues messages sent and display them one by one to the user.
unit ThreadMessage;

interface

uses
  Classes, Sysutils, Windows, Messages, Forms;

//The message queue element type.
type
  MessageQueueElementType =
    record
      Caption     : string;
      MessageText : string;
      Style       : Longint;
      Critical    : boolean;
    end;
  PMessageQueueElementType = ^MessageQueueElementType;

//The message thread
type
  TMessageThread = class(TThread)
  private
    { Private declarations }
  protected
    mMessageQueue : TList;
    mException    : Exception;     //Exception var
    procedure Execute; override;
    procedure HandleException; virtual;
    procedure DoHandleException;
  public
    procedure AddMessage(MessageElement : MessageQueueElementType);
  end;

implementation

uses DlgRemote;
{ Important: Methods and properties of objects in VCL can only be used in a
  method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure TMessageThread.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; }

{ TMessageThread }

//The main looop
procedure TMessageThread.Execute;
var pmessage_element : PMessageQueueElementType;
begin
  try
    //Used to handle messages sent by other processes
    mMessageQueue := TList.Create;
    try
      while (not terminated) do
      begin
        //Any new messages?
        if (mMessageQueue.Count > 0) then
        begin
          //Display the nessage
          pmessage_element := mMessageQueue.Items[0];
          Application.MessageBox(PChar(pmessage_element^.Caption),
                                 PChar(pmessage_element^.MessageText),
                                 pmessage_element^.Style);
          mMessageQueue.Delete(0);
          Dispose(pmessage_element);
        end;
        sleep(1000);
      end;
    finally
      mMessageQueue.Free;
    end;
  except
    //Special exception handler
    HandleException;
  end;
end;

//Message add procedure
procedure TMessageThread.AddMessage(MessageElement : MessageQueueElementType);
var pmessage_element : PMessageQueueElementType;
begin
  New(pmessage_element);
  pmessage_element^.Caption     := MessageElement.Caption;
  pmessage_element^.MessageText := MessageElement.MessageText;
  pmessage_element^.Style       := MessageElement.Style;
  mMessageQueue.Add(pmessage_element);
end;

//Handler of new exceptions. Called from HandleException.
procedure TMessageThread.DoHandleException;
begin
  // Cancel the mouse capture
  if (GetCapture <> 0) then
  begin
    SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  end;
  // Now actually show and log the exception
  if (mException is Exception) then
  begin
    FormRemote.Log('Exception:' + mException.Message);
    Application.ShowException(mException)
  end else
  begin
    FormRemote.Log('Exception:' + mException.Message);
    SysUtils.ShowException(mException, nil);
  end;
end;

//Special handler for exceptions, since this thread is running separately
procedure TMessageThread.HandleException;
begin
  //Save the exception
  mException := Exception(ExceptObject);
  try
    // Don't show EAbort messages
    if (not (mException is EAbort)) then
    begin
      //Wait until the thread is active
      Synchronize(DoHandleException);
    end;
  finally
    mException := nil;
  end;
end;

end.
