Application hangs after posting message to previously not shown frame
Asked Answered
S

1

5

I have PageControl with two TabSheets. On each of them there is a Frame, lets call it Frame2. I have a background thread which is posting message to Frame2 when it has finished its task. The Frame2 is on the second TabSheet so it is not visible when user starts application.

The problem is that my application stops refreshing its content when the thread posted message to Frame2, but only in case when Frame2 has not been previously shown. This leads me to think that message queue for the Frame2 has not yet been initialized and it gets initialized when the Frame2 is shown on the screen for the first time. Am I guessing correct?

Could someone give me tips how to initialize message queue right after constructing the Frame2 so it could listen messages immediately?

Sigmund answered 31/7, 2015 at 13:29 Comment(3)
Windows do not have queues, a valid window handle for the frame should suffice for PostMessage to succeed.Uniformitarian
Thanks for suggestion, unfortunately it does not help. Everything hangs when I click on the tab to display data.Sigmund
Show the code you use for posting the message. The hang reeks more like a synchronization problem, likely not due to posting a message but something following that.Seaboard
A
10

It's not that the frame doesn't have a message queue — it's threads that have message queues, not windows — but that the frame doesn't yet have a window handle. The window handle is most likely only created when the frame is first shown, unless you post it a message, in which case the window is created on demand.

If you attempt to post a message to it, you probably have a statement like this: PostMessage(Frame2.Handle, ...).

Reading the Handle property of a component will cause that component to create its window, if it doesn't already have a handle. When that happens in your secondary thread, then the frame's window is created belonging to that secondary thread. That can lead to any number of problems down the line. Like all VCL windows, the frame's window needs to belong to the main, VCL thread.

Even if you ensure that the frame's handle is created in the main thread before you direct messages to it (such as by calling HandleNeeded in the main thread), there's still a chance that reading the frame's Handle property will cause problems. This is because a VCL control might re-create its window. Then, once again, reading the Handle property may trigger creation of the frame's window in the wrong thread.

The safe technique is for the frame to to call AllocateHWnd to create a dedicated message-only window. Do that in the frame's constructor so it's guaranteed to happen in the main thread, and then post messages there. When you create the window, you'll provide a callback method that will be called any time that window receives a message. That callback method should belong to the frame control so that it has access to all the fields and methods of the frame it's associated with.

Abeabeam answered 31/7, 2015 at 13:53 Comment(6)
Thank you, it works now but I am not happy that I have to create separate hidden window to receive messages when there is one in the Frame2. People suggest using PostMessage() instead of Synchronize() but I wouldn't had such problems in case of Synchronize().Sigmund
@Wodzu: VCL windows are not persistent and not thread-safe. What you are attempting requires a persistent thread-safe window. If you don't want to created a dedicated window for your messages, you can use the Application.Handle window instead, using Application.OnMessage or Application.HookMainWindow() to receive your posted messages.Housebound
There is a window in Frame2 right up until it gets recreated. Then you are screwed. AllocateHWnd is the clean way. Don't use Application.Handle which will make your code an unholy tangle. Keep the locality with AllocateHWnd. Of course TThread.Queue is another clean option.Dialectologist
PostMessage and Synchronize are not interchangeable. If you have a task for the main thread, but don't want to block the secondary thread, then use Queue instead of Synchronize. It doesn't need a window handle.Abeabeam
Yes I know, it's just that pre Delphi 2009 (or somewhere around that time) it was only Synchronize so I let myself for this inaccuracy. I just wonder what is a ground rule for today, should I use PostMessage or Queue? I read a lot about this (like Allen Bauer's responses on few SO questions) but still I am not sure. Perhaps it's a matter of taste? Anyway, thank you guys for your support and comments. Much appreciated.Sigmund
If you can use Queue then I would do so. It results in much cleaner and readable code.Dialectologist

© 2022 - 2024 — McMap. All rights reserved.