Der schöne Günther 332 Posted Thursday at 03:04 PM I am fully aware that Delphi's VCL interface library exclusively lives in the main thread. Everything VCL related must happen in the main thread. Do not access VCL elements, don't even create and destroy something like a VCL Bitmap or Canvas in a thread. Maybe you don't have to be that cautious, but that's what I remember. Yet, it still occasionally happens to us. An event that used to run in the main thread gets moved to a different thread. Sometimes, things might trigger something that might cause the UI to refresh something. I know that with perfect documentation and more attention this probably wouldn't happen. Yet, it does. My question: Are there any kind of "runtime checks" I can enable to "detect" -anything- from outside the main thread touches the VCL? My motivation is to easier "find" places where the UI layer is accidently accessed from outside of the main thread. Share this post Link to post
Brian Evans 112 Posted Thursday at 04:17 PM Note you can access the VCL from another thread in a thread-safe manner using Synchronize (https://docwiki.embarcadero.com/Libraries/Athens/en/System.Classes.TThread.Synchronize) Code inside the Synchronize is run using the main thread. Share this post Link to post
Der schöne Günther 332 Posted Thursday at 04:26 PM Thank you, I am well aware of that. I am not directly accessing any GUI elements from threads. I am invoking events to which a VCL form or frame might be subscribed. Or background event handler might trigger something else that might then update the UI. Similar to like FastMM can throw an access violation at runtime when using memory after it has been freed, I am looking for something that might ~detect~ accessing the VCL in the context of a different thread. Share this post Link to post
Uwe Raabe 2127 Posted Thursday at 04:31 PM 1 hour ago, Der schöne Günther said: Are there any kind of "runtime checks" I can enable to "detect" -anything- from outside the main thread touches the VCL? Indeed there are: Set TControl.RaiseOnNonMainThreadUsage := True This will raise an EInvalidOperation when CheckNonMainThreadUsage is called for a control. This is automatically done inside CreateWnd. 2 2 Share this post Link to post
Remy Lebeau 1534 Posted Thursday at 05:11 PM (edited) 2 hours ago, Der schöne Günther said: Everything VCL related must happen in the main thread. Do not access VCL elements, don't even create and destroy something like a VCL Bitmap or Canvas in a thread. FYI, you can create and use a VCL TBitmap in a worker thread, you just have to lock the bitmap's Canvas to prevent the main thread from releasing the bitmap's GDI resources. 38 minutes ago, Uwe Raabe said: Indeed there are: Set TControl.RaiseOnNonMainThreadUsage := True The new TControl.RaiseOnNonMainThreadUsage property was introduced in 11.0 Alexandria: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/What's_New#Threading_safety_improvement However, it is currently only being used by the VCL when a TWinControl's window is created, not when it is accessed. Fortunately, there is a new TControl.CheckNonMainThreadUsage() method that is public, so you can call it for your own purposes before you access a UI control's members. Edited Thursday at 05:16 PM by Remy Lebeau 1 Share this post Link to post
Der schöne Günther 332 Posted Friday at 08:16 AM (edited) Thank you both, that is exactly what I was looking for. 😇 It's a pity the VCL itself uses it just this once but I am sure there were performance considerations. I think this brought me on the right track. I will probably use a TVirtualMethodInterceptor and use the OnBefore hook to watch for methods like ['Repaint', 'Update', 'SetBounds', 'DefaultHandler'] and then call CheckNonMainThreadUsage(). Not sure if it will be sufficient to just use one interceptor on a TForm or TFrame, or if I will need to have a VirtualMethodInterceptors for every kind of control (Buttons, Panels, ...). I will still need to figure that out. 🤔 Edited Friday at 08:16 AM by Der schöne Günther Share this post Link to post