dummzeuch 1763 Posted November 16 "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: 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). 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
Anders Melander 2203 Posted November 16 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. 3 Share this post Link to post
Uwe Raabe 2271 Posted November 16 What about using a TApplicationEvents instance and wire its OnMessage event. You can post the message to Application.MainForm.Handle then. 1 Share this post Link to post
dwrbudr 12 Posted November 16 Or TApplicationEvents.OnIdle (not sure if that is available in D6 though) Share this post Link to post
Darian Miller 396 Posted November 16 (edited) 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 November 16 by Darian Miller 1 Share this post Link to post
Kas Ob. 178 Posted November 17 AsyncCalls should solve this https://www.idefixpack.de/blog/bugfix-units/asynccalls-29-asynchronous-function-calls/ https://github.com/ahausladen/AsyncCalls Specially these two will play nicely with the IDE https://github.com/ahausladen/AsyncCalls Share this post Link to post
PeterBelow 265 Posted November 17 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). 3 Share this post Link to post
Anders Melander 2203 Posted November 17 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
dummzeuch 1763 Posted November 17 12 minutes ago, Anders Melander said: Good point; I didn't think of that. I did 😉 Share this post Link to post
Brandon Staggs 403 Posted November 17 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
Remy Lebeau 1694 Posted November 17 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. 2 Share this post Link to post
cesarliws 0 Posted November 18 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
Anders Melander 2203 Posted November 18 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. 2 Share this post Link to post
Kas Ob. 178 Posted November 18 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
Kas Ob. 178 Posted November 18 Also you might want to register the message with RegisterWindowMessage instead of hard coding the message code "const WM_RUN_POSTED = WM_APP + 1;" 1 Share this post Link to post
dummzeuch 1763 Posted November 18 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
HeartWare 11 Posted Monday at 07:55 AM 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
dummzeuch 1763 Posted Monday at 08:32 AM 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 Share this post Link to post
HeartWare 11 Posted Monday at 08:52 AM 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
Remy Lebeau 1694 Posted Monday at 09:58 AM 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
Remy Lebeau 1694 Posted Monday at 10:06 AM 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
HeartWare 11 Posted Monday at 11:58 AM 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
HeartWare 11 Posted Monday at 12:02 PM 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
Remy Lebeau 1694 Posted Monday at 08:07 PM 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