PeterPanettone 186 Posted November 8 (edited) The VCL is not thread-safe! How to Determine (Without Code) Whether an Event Handler Runs in the Main Thread or a Background Thread? Event Runs in Background Thread? Source OnDeactivate YES Windows sends WM_ACTIVATEAPP OnActivate YES Windows sends WM_ACTIVATEAPP OnClick NO UI Thread (VCL) OnTimer NO UI Thread OnClose NO UI Thread OnException NO UI Thread Golden Rule All events triggered by Windows outside the message loop run in a background thread! Can anyone confirm or deny this? EXAMPLE: 1. Declare a Global Constant in FormCreate: private FMainThreadID: TThreadID; procedure TForm1.FormCreate(Sender: TObject); begin FMainThreadID := TThread.CurrentThread.ThreadID; // ... Code end; 2. Now, check in any method: function TForm1.IsMainThread: Boolean; begin Result := (TThread.CurrentThread.ThreadID = FMainThreadID); end; Edited November 8 by PeterPanettone Share this post Link to post
Uwe Raabe 2271 Posted November 8 BTW, there already exists a global variable MainThreadID. In addition Winapi.Windows provides a function GetCurrentThreadId. Thus the implementation of IsMainThreadID can be written as Result := (GetCurrentThreadId = MainThreadID); I also suggest to make that a global function which is available even at other places than just a form and its descendants. Delphi 13 offers additional handling of accidentally doing VCL work not in the main thread. TControl has a Boolean class property RaiseOnMainThreadUsage, which is False per default. If True a call to the controls CheckNonMainThreadUsage method raises an EInvalidOperation exception when done not in the main thread. Currently TWinControl.CreateWnd is the only place where the VCL calls this itself, but it can be called at whatever place is suitable in code working with a control. 1 1 Share this post Link to post
DelphiUdIT 275 Posted November 8 1 hour ago, PeterPanettone said: Event Runs in Background Thread? Source OnDeactivate YES Windows sends WM_ACTIVATEAPP OnActivate YES Windows sends WM_ACTIVATEAPP In my Forms, Activate and Deactivate events run in the mainthread. In general, you can't rely on which thread "runs" an event. It could change with a release or even after a patch. Events generated by VCLs (the ones you see in the object inspector) are generally generated in the MAIN THREAD, but there are components like Indy, for example, that have a flag to generate events in a thread outside the Main Thread. If it's important to ensure that a given code runs within the Main Thread, then you should do what you suggest (in the most up-to-date form proposed by @Uwe Raabe), which is to use a hypothetical IsMainThread. However, it seems quite absurd to put such a thing into practice. If you follow good programming rules, including not using VCLs in threads, I don't see why you should have any problems. Share this post Link to post
PeterPanettone 186 Posted November 8 (edited) 55 minutes ago, DelphiUdIT said: In my Forms, Activate and Deactivate events run in the mainthread. In general, you can't rely on which thread "runs" an event. It could change with a release or even after a patch. Events generated by VCLs (the ones you see in the object inspector) are generally generated in the MAIN THREAD, but there are components like Indy, for example, that have a flag to generate events in a thread outside the Main Thread. If it's important to ensure that a given code runs within the Main Thread, then you should do what you suggest (in the most up-to-date form proposed by @Uwe Raabe), which is to use a hypothetical IsMainThread. However, it seems quite absurd to put such a thing into practice. If you follow good programming rules, including not using VCLs in threads, I don't see why you should have any problems. You're absolutely right that many VCL events (like OnClick, OnClose) run in the main thread – that's the standard behavior. However, OnActivate and OnDeactivate are special: - They are triggered by WM_ACTIVATEAPP, which Windows sends in the context of the thread that owns the window. - In practice, this is not the main thread – especially when another app activates/deactivates yours. Proof (tested in Delphi 12.2): procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject); begin OutputDebugString(PChar('ThreadID: ' + IntToStr(GetCurrentThreadId))); // → NOT equal to MainThreadID! end; Why it matters: Calling Application.Minimize in OnDeactivate → crash if not in main thread. Solution: TThread.Queue(nil, ...) – mandatory. IsMainThread is not absurd – it's defensive programming. Even if you avoid VCL in threads, Windows doesn't. And in Delphi 13, TControl.RaiseOnMainThreadUsage := True proves: Embarcadero agrees: thread safety is important. Edited November 8 by PeterPanettone Share this post Link to post
Uwe Raabe 2271 Posted November 8 33 minutes ago, PeterPanettone said: Proof (tested in Delphi 12.2): Do you have a complete test case for this? A quick test didn't show this, but I admit that there may be scenarios where it does. Therefore my request. Share this post Link to post
Remy Lebeau 1694 Posted November 8 I haven't tested this (not in front of a computer right now) but I find it hard to believe that the OnActivate/OnDeactivate events are being triggered in a background thread, given that it is message handlers in the main thread message loop that processes them. Yes, WM_ACTIVATEAPP is sent to the active window, which could be a window in a background thread, but the TApplication and TForm windows are the ones that handle that message and they create their windows in the main thread under normal operation. You have to really go out of your way to change that behavior. 4 Share this post Link to post
Uwe Raabe 2271 Posted November 8 6 minutes ago, Remy Lebeau said: given that it is message handlers in the main thread message loop that processes them In addition the WndProc handling WM_DEACTIVATE does call PostMessage(Handle, CM_DEACTIVATE, 0, 0); inside, which also places a message in the message loop and immediately returns. The OnDeactivate event is actually not called during handling the WM_DEACTIVATE but the CM_DEACTIVATE. As I still believe that Peter has encountered what he describes, I am interested in the way this could have happened. Share this post Link to post
PeterPanettone 186 Posted November 8 21 minutes ago, Uwe Raabe said: I am interested in the way this could have happened. I am sorry: I was wrong. OnDeactivate runs in the MAIN THREAD! The reality is very complicated: I was relying on outdated sources to handle a complex use case, trusting something I considered defensive programming. Sorry again, you were all right! Share this post Link to post
DelphiUdIT 275 Posted November 8 4 minutes ago, PeterPanettone said: I am sorry: I was wrong. OnDeactivate runs in the MAIN THREAD! The reality is very complicated: I was relying on outdated sources to handle a complex use case, trusting something I considered defensive programming. Sorry again, you were all right! In the meantime I tried to use various codes to simulate what you had said, but everyone has given a positive result in all situations: type TForm1 = class(TForm) private { Private declarations } protected class procedure APPDeactivate(Sender: TObject); public { Public declarations } end; //Alternative (from HACK ideas) procedure APPDeactivate(Sender: TObject); var Form1: TForm1; implementation {$R *.dfm} // Classic way around //************************************** class procedure TForm1.APPDeactivate(Sender: TObject); begin Application.Minimize; if MainThreadId = GetCurrentThreadId then ShowMessage('YES, I am the main thread') else ShowMessage('NOOOOO, I am not the main thread') end; //************************************** //Alternative (from HACK ideas) //************************************** procedure APPDeactivate(Sender: TObject); begin Application.Minimize; if MainThreadId = GetCurrentThreadId then ShowMessage('YES, I am the main thread') else ShowMessage('NOOOOO, I am not the main thread') end; function ResultLikeEvent(Proc: Pointer): TMethod; begin Result.Code := Proc; Result.Data := nil; end; //************************************** initialization //Classic way around //Application.OnDeactivate := TForm1.APPDeactivate; //Alternative way around Application.OnDeactivate := TNotifyEvent(ResultLikeEvent(@AppDeactivate)); end. 1 Share this post Link to post
PeterPanettone 186 Posted November 8 4 hours ago, DelphiUdIT said: In the meantime I tried to use various codes to simulate what you had said, but everyone has given a positive result in all situations: if Winapi.Windows.GetCurrentThreadId <> System.MainThreadID then ShowMessage('Background thread!') else ShowMessage('Main thread!'); Share this post Link to post
Kas Ob. 177 Posted November 9 17 hours ago, PeterPanettone said: Can anyone confirm or deny this? Well, my answer is yes and no to this question .. I really believe that you witnessed this and the logical thing is to deduce what you said it is not safe, yet it is impossible and should not be the case, it is absolutely safe and all these events are coming form the main thread, violating this will violate almost the Windows process-mainthread relationship, the whole thing will collapse if that happen in real life running process. But what about what did you witness ? well that is an artefact from the Delphi debugger involvement from evaluation to debug step over at some rare case, without the debugger you should never witness that, and to that extend i witnessed that in few times in the past and but it is only cosmetic. Share this post Link to post
Remy Lebeau 1694 Posted November 9 8 hours ago, Kas Ob. said: violating this will violate almost the Windows process-mainthread relationship Windows itself doesn't really have any concept of a "main thread", and doesn't require a UI to be tied to a specific thread. Any thread can create and manage a UI window. The "main thread" relationship is more of a convention/restriction of UI frameworks, like VCL. 1 Share this post Link to post
PeterPanettone 186 Posted November 9 (edited) 1 hour ago, Remy Lebeau said: Windows itself doesn't really have any concept of a "main thread", and doesn't require a UI to be tied to a specific thread. Any thread can create and manage a UI window. The "main thread" relationship is more of a convention/restriction of UI frameworks, like VCL. The original reason I started this thread: When I restore my app programmatically (i.e., not with a mouse click on the taskbar icon and not from its initial state after program start), but e.g., with a global shortcut, then OnDeactivate is not fired when clicking another app. This seems to be a Windows restriction that requires an app to be activated by an explicit user action for OnDeactivate to work. (Not sure about this.) Edited November 9 by PeterPanettone Share this post Link to post
Remy Lebeau 1694 Posted November 9 22 minutes ago, PeterPanettone said: When I restore my app programmatically (i.e., not with a mouse click on the taskbar icon and not from its initial state after program start), but e.g., with a global shortcut, then OnDeactivate is not fired when clicking another app. That is a VERY different issue than which thread fires an event. 1 Share this post Link to post
Kas Ob. 177 Posted November 10 14 hours ago, Remy Lebeau said: Windows itself doesn't really have any concept of a "main thread", and doesn't require a UI to be tied to a specific thread. Any thread can create and manage a UI window. The "main thread" relationship is more of a convention/restriction of UI frameworks, like VCL. I agree on that that there is no real distinguish between any thread running in a process, but it is more about the best practice or lets say ... well, i don't know how to formulate this right.. so i will put it in plain logic .. Windows loader will create a thread and we will call it main thread, it is the initializer thread for the process, being User Process or Service .., this one will be loading the imported libraries that declared as dynamic loaded by the PE, now to the exiting/terminating part .. All it takes and should be rightfully way to exit is for one thread to call ExitProcess, and the one calling this API will be responsible for freeing/unloading all the already loaded libraries in reverse order, while all other threads in the process will be suspended and later terminated forcefully, though it is not documented or at least i can't find a resource to suggest or point to make sure that the initial thread shold call the ExitProcess, yet it makes sense to keep it like that, we don't know if the (OS or any) libraries did initialize something and/or locked a device or did something, yet in all runtime libraries the exit loop watching and executing tied to that one, hence it better to stick the best practice for keeping and maintaining a watching and handling loop in that main thread for exit signals being message like WM_QUIT or service stop or console break signal... I hope that makes sense. Share this post Link to post
PeterPanettone 186 Posted November 10 (edited) 15 hours ago, Remy Lebeau said: That is a VERY different issue than which thread fires an event. It is indeed. However, I needed a solution to the problem that, when PROGRAMMATICALLY restoring my application (e.g., with a global shortcut), OnDeactivate no longer fired! (Windows has internal restrictions on stealing focus.) However, my application has a legitimate right to do so (as it needs to be minimized when it loses the focus), so I now use the "AttachThreadInput" trick to temporarily attach my application's input thread to the foreground window's thread, thereby bypassing Windows' restrictions on stealing focus and thus making OnDeactivate fire again when the user focuses another application. Edited November 10 by PeterPanettone Share this post Link to post
Lars Fosdal 1987 Posted November 10 Does the OnActivate trigger on the programmatical restore? If not, should your app restore send a wm_activate to itself to attempt to trigger any Windows internal states? If not, it kinda looks like a bug that OnDeactivate doesn't trigger after an app-initiated restore of the app - by timer, or by other kinds of events in the app. Share this post Link to post
PeterPanettone 186 Posted November 10 6 minutes ago, Lars Fosdal said: Does the OnActivate trigger on the programmatical restore? If not, should your app restore send a wm_activate to itself to attempt to trigger any Windows internal states? If not, it kinda looks like a bug that OnDeactivate doesn't trigger after an app-initiated restore of the app - by timer, or by other kinds of events in the app. This is not a bug. It is an internal Windows mechanism to protect the user from apps he hasn't interacted with stealing the focus. When you programmatically restore a window (like in my case with a global shortcut), Windows doesn't consistently activate the application window, so subsequent focus changes don't trigger OnDeactivate. This means the application window is visible but not the active window. When you click another application, Windows treats it as an activation, but your app was never truly "active" in the first place, so OnDeactivate doesn't fire. The focus-stealing prevention mechanism was introduced in Windows 2000/Windows ME (released in 2000). Before Windows 2000/ME, applications could freely call SetForegroundWindow to bring their windows to the foreground, as in Windows 95/NT4. Starting with Windows 2000/ME, Microsoft introduced restrictions that prevent background applications from forcing their windows to the foreground while the user is working with another window. Instead of allowing applications to steal focus, SetForegroundWindow would activate the window and call FlashWindowEx to notify the user (typically causing the taskbar button to flash). The registry hack using ForegroundLockTimeout that existed in Windows XP was actually designed to make XP behave more like Windows 2000 in preventing focus stealing. In Windows 7 and later, Microsoft claimed this protection was built in by default, though many users reported that some applications still managed to steal focus. Share this post Link to post
DelphiUdIT 275 Posted November 10 For me, RESTORE WORKS ONLY with a postmesssage. RESTORE doesn't work alone. begin postmessage(handle, WM_ACTIVATE, 0, 0); Application.Restore; end; I simply try with a Timer and the application without that postmessage doesn't restore after been minimize. Manually clicking on taskbar, it works (Application was restored to previous sieze and position). The event DEACTIVATE (of Application) for me seems to works always. Share this post Link to post
PeterPanettone 186 Posted November 10 9 minutes ago, DelphiUdIT said: For me, RESTORE WORKS ONLY with a postmesssage. RESTORE doesn't work alone. begin postmessage(handle, WM_ACTIVATE, 0, 0); Application.Restore; end; I simply try with a Timer and the application without that postmessage doesn't restore after been minimize. Manually clicking on taskbar, it works (Application was restored to previous sieze and position). The event DEACTIVATE (of Application) for me seems to works always. Your workaround might appear to work in some scenarios, but it's fighting against the OS design rather than working with it. The real issue is that Windows won't let you programmatically steal focus unless your application meets the conditions Windows sets (i.e., direct user interaction). I couldn't believe it myself until I tested it. Share this post Link to post
Rollo62 639 Posted November 11 On 11/8/2025 at 5:16 PM, PeterPanettone said: Solution: TThread.Queue(nil, ...) – mandatory. As a side note: In such cases I prefer TThread.ForceQueue(nil, ...), in cases where it is not 100% sure, that calling this part from background or main UI thread, and could be both possible threads somehow. With that ForceQueue you are always on the safe side. Share this post Link to post
cesarliws 0 Posted November 11 3 hours ago, Rollo62 said: As a side note: In such cases I prefer TThread.ForceQueue(nil, ...), in cases where it is not 100% sure, that calling this part from background or main UI thread, and could be both possible threads somehow. With that ForceQueue you are always on the safe side. TThread.Queue(nil, ...) called on mainthread executes imediatelly, called from other threads enqueue. TThread.ForceQueue(nil, ...) called on mainthread or other threads always enqueue. Both are safe. Share this post Link to post
Rollo62 639 Posted November 11 13 minutes ago, cesarliws said: TThread.Queue(nil, ...) called on mainthread executes imediatelly, called from other threads enqueue. TThread.ForceQueue(nil, ...) called on mainthread or other threads always enqueue. Both are safe. Right, but if you don't know or cannot for 100% ensure that you ALWAYS will be called from within the background thread, then ForceQueue is a little safer than Queue, because it doesn't care. Share this post Link to post
Uwe Raabe 2271 Posted November 11 Well, there is a difference between Queue and ForceQueue when called from the main thread: Queue will execute in sync, so is blocking until the task code is executed and executes the following code after the task code. ForceQueue will queue the task as if it were called from another thread, thus is non-blocking. Therefore the following code is executed before the task code. Share this post Link to post
Rollo62 639 Posted November 11 That is correct: https://docwiki.embarcadero.com/Libraries/Athens/en/System.Classes.TThread.Queue Quote Warning: If the caller thread is the main thread, Queue executes the call directly (synchronously). In this case, use ForceQueue to queue the execution of a method call within the main thread. I assume if somebody asks to "Queue" something, he would intrinsically expect that this might not naturally be synchronously. Maybe its just me, always trying to think as simple as possible, the world is complex enough Share this post Link to post