Jump to content
Der schöne Günther

Is there a way to -detect- that the VCL has been accessed from outside of the main thread?

Recommended Posts

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

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
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.

  • Like 2
  • Thanks 2

Share this post


Link to post
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 by Remy Lebeau
  • Thanks 1

Share this post


Link to post

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 by Der schöne Günther

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

×