NativeMessageBox::showMessageBox hangs JUCE applications sometimes


#1

If I call NativeMessageBox::showMessageBox in some other thread than the main GUI thread, the whole GUI message thread gets "locked up" and does not process any more input messages (mouse/keyboard etc), effectively "hanging" the application.

I need to be able to do this, or at least recover from it (since the Win32 ShowMessageBox call is in a DLL over which I have no control, and I don't want to call the DLL function from the GUI thread).

Ideas ?

/Rob

 


#2

I can add an assertion..

Usually when you hit a deadlock like that, it'll be because the message thread is waiting to end the thread that's trying to launch the message box.


#3

Ok, but the thread ends...

Let me explain: I call a function in a DLL to send over data, and this can be a lengthy operation which is why I don't want to do it in the GUI thread. If the transmission fails, the DLL pops up a message box (ShowMessageBox), and I can click it down so it dissappears. The DLL function call returns, an error message is sent to the GUI thread which pops up a message box (on my app side), and this one I cannot click down. I'm stumped.

 


#4

If you don't need to wait modally for the box to be closed, then probably the best plan is to use an AsyncUpdater or CallbackMessage to trigger the message box?


#5

No, that's not the point, I have no control over when the message box is triggered, this is in a DLL (which does not use JUCE). However, after the message box is popped up (in non-GUI thread, since I don't want to call the DLL function from the GUI-thread), the JUCE GUI thread gets locked up. It is very easy to reproduce, just call MessageBox (Win32, http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx) in a non-GUI thread and you'll see what I mean...


#6

Oh! Sorry, I see your point now. Well TBH I'm not sure what could possibly be done about that..


#7

Hmm.. it would be interesting though to know WHY it happens, as I have a hard time grasping that. Thing is that in the state of "hangup" I can still break into the code and see that the GUI thread is indeed running, but I can't "do" anything.

 


#8

Maybe because something deep in win32 is blocking events from reaching the other non-modal windows..?


#9

This seems interesting, see "Fact 2": http://www.codeproject.com/Articles/121226/MessageBoxes-and-worker-threads

MessageBox is called on a ThreadPool thread, then it stops. And if MessageBox converted that thread to be the UI thread, no wonder nothing is processed anymore. 

Question then is, is there a way to switch it back ?


#10

Jeez.. No idea how it could be converted back!


#11

Yeah, me neither... will keep digging a while, but it seems I need to change the API to allow for logging/popups without resorting to Win32 function calls...


#12

And I'm not sure you need to... :) Anyway, popping up a MessageBox in a non-GUI thread is no problem. Works fine. So the root to my issues must lie elsewhere. Sorry for stirring up the dust...


#13

Not sure if this is still an issue for you anymore but I've figured out what causes this.

When a juce::Thread is created in Windows it calls AttachThreadInput() on the new thread (I'm guessing to allow manipulating any window on any of the threads or something). This essentially serializes all input from windows on any juce threads and the main thread.

When you call MessageBox() on a thread created by juce (or any thread whos input is attached), the message box's input will be serialized with input on the main thread. So basically if the message box has some message waiting to be handled, your app's messages are held hostage until the message box processes its message. If the message box doesn't process the message, your app won't get any more messages.

At first glance this seems like it should be okay (although not ideal) since the message box closes and it should stop getting messages. However, I've found that there usually is a message left on the message box's queue after you close it. The message is something related to multilingual/language bar or something like that, so it might be possible that not all systems will get this message.

There are a couple of ways I've found to work around this:

1. Don't use AttachThreadInput() (Either turn it off after starting the thread, or surround your MessageBox call with code to turn it off and on again). 

2. Flush any remaining messages from the message box.

Something like this after the message box call would flush it:

MSG message;
while (PeekMessage (&message, (HWND) 0, 0, 0, PM_REMOVE) != 0)
{
    TranslateMessage (&message);
    DispatchMessage (&message);
}

#14

Thanks Frongo, yes it is possibly still an issue, as plugin DLL implementors are free to call MessageBox and thereby mess up "my" application.

So what you're saying is that by using AttachThreadInput(<this thread ID>, <juce message thread ID>, FALSE) would prevent this from happening ? I'll have to try that out.


#15

Thanks Frongo. Both of your suggestions work smiley , but I opted to use the message queue purge, because if I detached the thread, a call to MessageBox didn't bring the message boxt up on top of the Juce main window.