Search code examples
multithreadingdelphimessage-queuedelphi-2009

Application hangs after posting message to previously not shown frame


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?


Solution

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