Posting a message - desktop component deletion problem


#1

Is there a way to post a message to the message thread for later execution, after the current callback returns?

MessageManager::callFunctionOnMessageThread() allows worker threads to post messages to the message thread, but when used from the message thread, it’s just a call.

Here is a situation I don’t know how to deal with without the ability to post:

I’ve got a DocumentWindow with some controls on it, including a checkbox.

When I press Alt+F4, the window closes, no matter what control has the focus. This is accomplished by having the DocumentWindow react to the keypress.

Now when I press Alt+F4 while the focus is on a checkbox, the keyboard event is initially processed by Button::keyStateChanged and from there it gets passed to the parent windows, I don’t quite understand what happens, but in VC9 debug mode a danglig pointer exception is produced in the last line of Button::keyStateChanged() because the window and all its components was deleted by a parent’s reaction to the keypress - even though the parent returns true to indicate it processed the keypress.

Juce programs with a single or few windows won’t have this problem because window deletion is done on processing the quit message. But for programs that can create an arbitrary number of windows, a method of delayed deletion would make things a lot easier. The ability to generically post a function call asynchroneously is the most generic way I can think of, and since the posting mechanism is already there for cross-thread communication, I figure it wouldn’t be much work. The problems caused by deleting a window in direct response to an event of one of its controls are rather nasty to track. You could stuff all the code with deletion checkers and still miss the crucial one…


#2

There are already loads of async messaging classes you could use - MessageListeners, ActionListeners, ChangeListeners, Component::postCommandMessage, etc. The callFunctionOnMessageThread is designed to wait for the return value of the function call.

Looking at the problem though, I think the problem is actually much simpler: try this small change to Button::keyStateChanged:

[code] if (isEnabled() && wasDown && ! isKeyDown)
{
internalClickCallback (ModifierKeys::getCurrentModifiers());
return true;
}

return isKeyDown || wasDown;

[/code]

I think the only real flaw was that I hadn’t noticed that isKeyDown is a member variable, not a local, so that line couldn’t survive the button being deleted.

In fact, an even more minimal fix could be just to swap the order of the last line:

return wasDown || isKeyDown;