So I’ve made a menu-like window that works very well for the most part. The only thing I haven’t been able to figure out is how to deal with a request to quit the application (i.e. the one that loaded the bundle) while the menu-like window is still visible. This leads to a crash
The JUCE menus have the same issue, except that they don’t crash (another) test application. Maybe because they’re modal, although making my own window modal didn’t result in much of a change.
So here are my questions:
- is it possible to detect clicks on the OSX menu bar (and on the dock, I guess)?
- is there a good way to intercept Command-Q from a bundle (as opposed to a standalone app)?
I’ve been looking at lots of potential solutions, but none of them panned out.
(btw, definition of a “menu-like” window thing: it’s a window that closes whenever you click anywhere outside it)
Are you having problems because your window is modal?
If so, have a look at what I’ve done in the introjucer to handle that sort of thing - it’s in JucerApplication::systemRequestedQuit(). Basically, it checks whether anything is modal, and if so, tries to cancel it and then launches a timer that’ll attempt to quit again after a short pause.
No, it doesn’t matter if the window is modal or not.
If the menu like window is open when Command-Q is used, I get an EXC_BAD_ACCESS error in ~NSViewComponentPeer(), at the line [view removeFromSuperview]. This seems to be the main application window, since the stack trace indicates it’s called after my ~MainWindow().
It’s a bit of a specific situation, though I can imagine the same thing happening for VST plugins, when the host is closed. I haven’t tested this, though.
It’s also possible that I’m doing something wrong which causes this crash. But I don’t know enough about Cocoa to know if there are limits to when a window should be destroyed and when not.
Well, that just sounds to me like you’re deleting a dangling pointer or leaking something.
Yes, I agree that’s the most likely cause. I’ll keep looking.
It does only happen when closing the application, i.e. when quit() has been called. Does that ring any bells?
Some more information: it only happens if the menu like window is still on the desktop at the time JUCEApplication::quit() is called. It doesn’t happen if I manually remove it before that happens.
This is the sequence of events that leads to the crash:
- systemRequestedQuit() function: call JUCEApplication::quit()
- in main window destructor, delete main window content component (“MainPanel”)
- from MainPanel destructor, delete object instance in bundle
- from the instance destructor, hide and delete the menu like window
- then it continues with the main window destructor and crashes when deleting the peer object in the main window’s inherited Component destructor (removeFromDesktop)
Does anyone have any idea?
Sounds like the kind of thing that only you can debug. It’ll be something simple, I bet.
If you’re really unable to track it down, try creating a simple test case app that replicates the problem. That can often be revealing.
I’ve attached a simple test project that has this problem (everything except for the JUCE files in modules).
There’s a compiler def (WindowInBundle) at the top of MainWindow.h. If that’s defined, it’ll load the bundle and create the window from that. If not, it’ll create the window directly.
How to reproduce the crash:
- make sure WindowInBundle is defined
- run the app
- click on the app window to show the “menu” window
- close the app
The only thing that I might be doing wrong (that I can think of), is something in the loading/initialization/finalization of the bundle. All the rest is the same in both cases. But I’m no expert on JUCE or Cocoa and I’m quite stuck at this point.
Loading a bundle? You mean you’re mixing up native cocoa with juce stuff? If so, sorry - you’re on your own with that one!
What’s the approved way of creating and using a dynamic library in OSX, in that case?
It’s not something I have any thoughts about - if you’re going to go off-piste, then you’ll just need to figure it out for yourself.
If you have any sensible suggestions for (minor!) modifications that I could make which would help the library to cope with whatever it is that you’re trying to do, then I’d be happy to discuss them. But I certainly can’t help you debug it.
I’m not using any Cocoa other than what’s in JUCE, for what it’s worth.
Anyway, I’ll see about alternatives to a bundle. I just need something that works more or less like a dll would in Windows, so it can be shared between different apps and plugins without having to compile it with each.
Some more info about the problem … it seems to crash when a message is sent to a window. My guess is that there’s still a dangling pointer to the destroyed menu window somewhere, but I’m not sure what I could try.
(btw, I’ll just post what I find here. If you have any ideas, feel free to let me know)
So you’ve got a juce app using other juce components in a library bundle? That’s not something I’d recommend - there are all kinds of obscure linkage and heap management problems you can get into if you start passing things around across DLL boundaries. (And even if you don’t pass anything at all across boundaries, there are horrific obj-C name aliasing dangers). Definitely to be avoided.
The ObjC linking issues should be solved with the prefix in the compiler defines, if I understand correctly?
I don’t really pass anything across app/bundle boundaries except virtual abstract classes, which should be ok in C++. I’ve also been doing this for at least 4 years without any issues, though that was with Juce 1.46/1.50.
Maybe I can get around the issue by making the window modal (through the Cocoa API), but I’m not sure if I’ll be able to manage that.
You can avoid cross-linking between MY classes that way, but all of Cocoa’s underlying system objects will still be shared under the hood.
Yes, that makes sense.
That could be what’s wrong, then. Still, there has to be a good way to handle it, otherwise lots of stuff would become impossible (or a lot more difficult) to do. Not that I’d be very surprised if that were indeed the case in OSX.
Thanks for the help so far, I’ll keep on looking
Progress report: creating an empty NSWindow in a bundle (from a JUCE test app) is ok, no crash.
I started creating my own ComponentPeer to see if that would work better, and with an NSWindow and NSView it worked fine.
Then I created my own descendant of NSView and used that, and it crashed in the same way as before. Even if my descendant just derives from NSView and does nothing else, this happens.