Jump to content
dummzeuch

alternative to TThread.ForceQueue for Windows

Recommended Posts

"newer" Delphi versions have got the class method TThread.ForceQueue that executes a method in the main thread and forces this to be queued even if the current thread (the one calling TThread.ForceQueue) is already the main thread, so the call returns without having executed that method and it gets executed later.

 

I need similar functionality for older Delphi versions, ideally back to Delphi 6, which didn't even have TThread.Queue. The code only needs to work on Windows and in a VCL application (in this case the Delphi IDE).

 

So far I had the following ideas:

  1. Use Post Message (but I don't have a window handle readily available to which I could post, so I would need to create one).
  2. Check if the thread is the main thread, if not, use TThread.Synchronize, if yes, create a TTimer with interval 0 and assign the method to its OnTimer method.

 

Any better ideas?

Share this post


Link to post

1 & 2 are basically the same except the timer creates the window handle for you and you will suffer (or benefit, YMMV) from the special properties of WM_TIMER.

 

How about this; A function that creates a simple object which:

  • Allocates a window handle.
  • Posts a message to this handle.
  • When the message arrives, deallocates the handle, calls the delegate function, and then destroys itself.

If you expect this to be called a lot, you could cache a single instance of the class to improve performance a bit. Protect the cached instance (or the pointer to it) with InterlockedCompareExchange. I probably wouldn't bother.

  • Like 3

Share this post


Link to post

What about using a TApplicationEvents instance and wire its OnMessage event. You can post the message to Application.MainForm.Handle then.

  • Like 1

Share this post


Link to post

Or TApplicationEvents.OnIdle (not sure if that is available in D6 though)

Share this post


Link to post

You could do something like this:

 

procedure TForm1.Button1Click(Sender: TObject);
begin
  TLegacyThreading.ForceQueue(MyCustomMethod);
end;

procedure TForm1.MyCustomMethod;
begin
  Edit1.Text := FormatDateTime('hh:nn:ss:zzz', Now);
end;

 

Using:

 

unit LegacyThreading;

interface
uses
  Classes;

type

  TLegacyThreading = class(TThread)
  private
    fForceQueueMethood:TThreadMethod;
  protected
    procedure Execute; override;
    constructor Create(const AMethodToCall:TThreadMethod); reintroduce;
  public
    class procedure ForceQueue(const AMethodToCall:TThreadMethod);
  end;

implementation


constructor TLegacyThreading.Create(const AMethodToCall:TThreadMethod);
begin
  inherited Create({CreateSuspended=}True);
  fForceQueueMethood := AMethodToCall;
  FreeOnTerminate := True;
end;


procedure TLegacyThreading.Execute;
begin
  Synchronize(fForceQueueMethood);
end;


class procedure TLegacyThreading.ForceQueue(const AMethodToCall:TThreadMethod);
var
  Worker:TLegacyThreading;
begin
  Worker := TLegacyThreading.Create(AMethodToCall);
  Worker.Resume;
end;

end.

Delphi 5+

Edited by Darian Miller
  • Like 1

Share this post


Link to post
17 hours ago, Anders Melander said:

1 & 2 are basically the same except the timer creates the window handle for you and you will suffer (or benefit, YMMV) from the special properties of WM_TIMER.

 

How about this; A function that creates a simple object which:

  • Allocates a window handle.
  • Posts a message to this handle.
  • When the message arrives, deallocates the handle, calls the delegate function, and then destroys itself.

If you expect this to be called a lot, you could cache a single instance of the class to improve performance a bit. Protect the cached instance (or the pointer to it) with InterlockedCompareExchange. I probably wouldn't bother.

Careful here. If that function is called in a background thread the window handle would be created in that thread's context and Windows makes sure messages send or posted to a window are processed in the thread that creates the window handle. A background thread normally has no message loop, however, and so a posted message would never be processed, even if the PostMessage call created a message queue for the thread (not sure if it does that, though).

  • Like 3

Share this post


Link to post
33 minutes ago, PeterBelow said:

If that function is called in a background thread the window handle would be created in that thread's context

Good point; I didn't think of that.

Share this post


Link to post
12 minutes ago, Anders Melander said:

Good point; I didn't think of that.

I did 😉

Share this post


Link to post
On 11/16/2025 at 10:32 AM, Anders Melander said:

1 & 2 are basically the same except the timer creates the window handle for you and you will suffer (or benefit, YMMV) from the special properties of WM_TIMER.

 

How about this; A function that creates a simple object which:

  • Allocates a window handle.
  • Posts a message to this handle.
  • When the message arrives, deallocates the handle, calls the delegate function, and then destroys itself.

If you expect this to be called a lot, you could cache a single instance of the class to improve performance a bit. Protect the cached instance (or the pointer to it) with InterlockedCompareExchange. I probably wouldn't bother.

+1. Before TThread.ForceQueue I always used postmessage to handle this kind of thing. OP said he doesn't have a window handle, so, create one just for this.

Share this post


Link to post

You could replicate what TThread.(Force)Queue() does internally - create a thread-safe list of method pointers, push a method pointer into the list when needed and post a message to the main thread to "wake it up", and when that message is received (and/or also in the OnIdle event) then run through the list, calling and removing each method pointer.

  • Like 2

Share this post


Link to post
On 11/16/2025 at 1:32 PM, Anders Melander said:

1 & 2 are basically the same except the timer creates the window handle for you and you will suffer (or benefit, YMMV) from the special properties of WM_TIMER.

 

How about this; A function that creates a simple object which:

  • Allocates a window handle.
  • Posts a message to this handle.
  • When the message arrives, deallocates the handle, calls the delegate function, and then destroys itself.

If you expect this to be called a lot, you could cache a single instance of the class to improve performance a bit. Protect the cached instance (or the pointer to it) with InterlockedCompareExchange. I probably wouldn't bother.

I did, MainThreadDispatcher.pas
 

procedure TMainForm.FormShow(Sender: TObject);
begin
  RunAsync(AfterFormShowAsync);
end;

procedure TMainForm.AfterFormShowAsync;
begin
  SetButtonStates(IsRunning);
  try
    LoadDataFromDatabase;
  finally
    SetButtonStates(IsStopped);
  end;
end;


and some syntax suggar to simplify

procedure RunAsync(Proc: TProc); overload;
procedure RunAsync(Proc: TProcedure); overload;
procedure RunAsync(Sender: TObject; NotifyEvent: TNotifyEvent); overload;
procedure RunAsync(NotifyEvent: TNotifyEvent); overload;

https://github.com/cesarliws/DelphiMultithreadingBookCodeEnglishEdition/blob/main/0504/DelphiMultithreadingBook0504.MainThreadDispatcher.pas
https://github.com/cesarliws/DelphiMultithreadingBookCodeEnglishEdition/blob/main/0504/DelphiMultithreadingBook0504.AsyncHelpers.pas

 

Share this post


Link to post
1 hour ago, cesarliws said:

I did, MainThreadDispatcher.pas

As far as I can tell, that code suffers from the problem @PeterBelow described; The first invocation must be done from the main thread. Otherwise the shared window handle will be associated with the wrong thread.

  • Like 2

Share this post


Link to post
5 hours ago, cesarliws said:

I did, MainThreadDispatcher.pas

Nice, and i can suggestions a fix to make it robust and reliable,

 

1) as mentioned already by Anders unless its been created by the the main thread (or the one we need to handle the VCL/UI operation), it might be causing trouble, as it is TComponent it should be created by the main thread, but we have a dilemma here, so if you forced the creation of the window in initialization clause then you can't have two of them or it will get more complex a complete refactor into a singleton will be better here, i think you got the idea of when and where you should create the window,

 

2) This is the meat of my failure point i predict, it is protected with try..finally and that is correct but it will protect against memory leak in case of exception 

https://github.com/cesarliws/DelphiMultithreadingBookCodeEnglishEdition/blob/main/0504/DelphiMultithreadingBook0504.MainThreadDispatcher.pas#L79-L99

What i see is that if an exception raised then this might happen 

A) we landed here at WMRunPosted by either PostMessage or SendMessage, and these two will return to the Windows (OS) 

B) an escaping or running exception on the unwind will unwind through the OS, and will either return to the caller in case of SendMessage or somewhere else

C) if case of if the caller is a thread from your application this means it either coming from the main thread and it is already protected by try..except inside the application loop or from one of your own threads which is already protected, in both cases the exception will be handled or dropped but will not let run back to the OS causing fatal crash, as it will cause fatal crash, this is important to know and remember.

D) the edge case if your application had a call back thread like these in media handling, these exceptionally sensitive, and they belongs to the OS or an component from the OS, it is easy to use this code with it and in case an exception being raised then try..finally will not help and the exception will return to the OS and will cause fatal crash, as media call back functions there is the more dangerous ones, the ones that are coming form filter drivers like file handling or antivirus ... these also go harsh on exceptions. 

 

So add try..except around 

https://github.com/cesarliws/DelphiMultithreadingBookCodeEnglishEdition/blob/main/0504/DelphiMultithreadingBook0504.MainThreadDispatcher.pas#L93

or around the whole "if Assigned(ProcData^) then" and you are good against the edge case mentioned above.

Share this post


Link to post

Also you might want to register the message with RegisterWindowMessage instead of hard coding the message code "const WM_RUN_POSTED = WM_APP + 1;"

  • Like 1

Share this post


Link to post

I've now used the PostMessage approach, of course using a window handle created in the main thread. It works fine with any Delphi version I have tested so far. 

Thanks for your input, there were some interesting suggestions.

Share this post


Link to post
On 11/18/2025 at 8:41 AM, dummzeuch said:

using a window handle created in the main thread

Just curious:

How do you create the window handle? In my Clipboard Monitor I create it as this class:

                            TMsgWindow          = CLASS(TWinControl)
                                                    CONSTRUCTOR Create(Clip : TClipboard); REINTRODUCE;
                                                    DESTRUCTOR  Destroy; OVERRIDE;
                                                  PROTECTED
                                                    PROCEDURE   CreateParams(VAR Params : TCreateParams); OVERRIDE;
                                                  PRIVATE
                                                    FClip       : TClipboard;
                                                    PROCEDURE   Update(VAR MSG : TMessage); REINTRODUCE; MESSAGE WM_CLIPBOARDUPDATE;
                                                  PUBLIC
                                                    PROPERTY    Clip : TClipboard Read FClip;
                                                  END;

with this CreateParams:

PROCEDURE TMsgWindow.CreateParams(VAR Params : TCreateParams);
  BEGIN
    INHERITED;
    Params.Style:=0; Params.ExStyle:=0;
    Params.X:=0; Params.Y:=0;
    Params.Width:=0; Params.Height:=0;
    // This is the crucial part:
    Params.WndParent:=HWND_MESSAGE  // Makes it a message-only window
  END;

and this CONSTRUCTOR:

 

CONSTRUCTOR TMsgWindow.Create(Clip : TClipboard);
  BEGIN
    INHERITED Create(NIL);
    FClip:=Clip;
    ControlStyle:=ControlStyle+[csAcceptsControls];
    Visible:=FALSE;
    AddClipboardFormatListener(Handle)
  END;

(making sure to create it on the main thread - thanks for the heads up, I didn't do that before I read this thread).

 

Is that a thread-safe way to do it? I allocate/free the MsgWindow whenever my OnChange event is assigned (=NIL Free, <>NIL=Allocate)

Share this post


Link to post
17 minutes ago, dummzeuch said:

I just use the AllocateHWnd and DeallocateHWnd functions to create a handle without any VCL object.

Basically as described here: Receive Windows Messages In Your Custom Delphi Class – NonWindowed Control/Object

But then you have to have your own Message handler, where you check for (in this case) WM_CLIPBOARDUPDATE, right? By using the TWinControl descendant, I get that "for free".

(not degrading or complaining - just trying to understand the various methods for this, as I have run into the need for a custom message without any link to existing VCL objects on many occasions, and finally came up with this approach as the - to me - easiest way).

Share this post


Link to post
53 minutes ago, HeartWare said:

But then you have to have your own Message handler, where you check for (in this case) WM_CLIPBOARDUPDATE, right?

Yes, of course. How else do you think you tell your code what to do when the message arrives?

53 minutes ago, HeartWare said:

By using the TWinControl descendant, I get that "for free".

Not really. You still have to write your own message handler. You simply chose to use the VCL's message dispatching mechanism for that purpose, but you still need your own handler nontheless. Also, the dispatching system has more overhead, as the message has to travel through several layers of processing before it can reach your message handler.

 

Whereas AllocateHWnd() is more direct and lightweight. It's only overhead is a little memory allocation, but all messages to it flow (almost) directly into the message callback you provide it.

53 minutes ago, HeartWare said:

(not degrading or complaining - just trying to understand the various methods for this, as I have run into the need for a custom message without any link to existing VCL objects on many occasions

Then AllocateHWnd() is a good choice for that. Unless you don't mind calling CreateWindow/Ex() directly.

53 minutes ago, HeartWare said:

and finally came up with this approach as the - to me - easiest way).

So, deriving a class and overriding multiple virtual methods is easier than calling 1 simple function? Interesting.

Share this post


Link to post
2 hours ago, HeartWare said:

and this CONSTRUCTOR:

Why are you setting the ControlStyle to accept child controls, when your class is meant to be invisible?

 

More importantly, the constructor is not the best place to call AddClipboardFormatListener(). You should override the virtual CreateWnd() or CreateWindowHandle() method instead. Otherwise, if your TWinControl ever re-creates its HWND (which CAN happen outside of your control) then you will lose your clipboard monitor.

 

However, if you use AllocateHWnd() instead of TWinControl, you avoid all this ugliness. 

Share this post


Link to post
1 hour ago, Remy Lebeau said:

So, deriving a class and overriding multiple virtual methods is easier than calling 1 simple function? Interesting.

Yes - in the sense that it is more VCL/Delphi like and not Win32-like (I know that behind the lid, it's still Win32, but I prefer the Delphi way of intercepting messages - it's more elegant and descriptive in my view).

Share this post


Link to post
1 hour ago, Remy Lebeau said:

Why are you setting the ControlStyle to accept child controls, when your class is meant to be invisible?

Don't know. It was part of the code I found that I used for the basis, and it doesn't hurt (it may be superflous, so I'll try removing it and see what happens).

Quote

You should override the virtual CreateWnd() or CreateWindowHandle() method instead.

Good point. I'll do that (does the old handle get unregistered automatically, or do I need to call unregister on it first?)

Quote

However, if you use AllocateHWnd() instead of TWinControl, you avoid all this ugliness. 

Well, one man's "ugliness" is another man's "beauty". Remember: Beauty is in the eye of the beholder. I think my version is more "beautiful" than calling Win32 directly, but YMMV.

Share this post


Link to post
8 hours ago, HeartWare said:

does the old handle get unregistered automatically, or do I need to call unregister on it first?

It is best to unregister manually, just in case. Override DestroyWnd() or DestroyWindowHandle() for that.

8 hours ago, HeartWare said:

I think my version is more "beautiful" than calling Win32 directly, but YMMV.

AllocateHWnd() is an RTL function, not a Win32 function.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×