The destructor of juce::FileChooser does not close its open file chooser dialog.
(We updated JUCE to version 6 from 5 and this problem started to occur.)
Our VST plugin opens the native file chooser in windows. If the user decide to quite our plugin by removing it from the DAW the file chooser dialog stays open. In case the user choose a file everything works as expected. We use launchAsync to open the file chooser dialog and in case the plugin is terminated while the dialog is open the destructor of juce::FileChooser is called.
Just for testing I tried to use browseForFileToOpen and removed the plugin form the DAW (tested with Ableton Live). This leads to a crash of the DAW.
I tested to delete the file chooser object (just by a timer) during the dialog is open: The dialog stays open.
In the description of method juce::FileChooser::launchAsync its says:
“… To abort the file selection, simply delete the FileChooser object. …”
Somehow this does not work.
Thank you for your fix. But there are still problems in Windows with DAW Ableton Live 11:
If our plugin is removed while the dialog is still open (started via showDialog within a click handler) the juce leak detector recognize a leak of a juce::StringArray and of three juce::MouseInputSource. Additionally there are several access violations and the DAW Ableton Live 11 crashes.
Using launchAsync just does nothing. No dialog is opened.
I tested this with the latest juce devleop branch (1ff7fc38b1782c86009c1d1edb8e31669afd4860).
Modal loops, such as those used by the synchronous FileChooser functions, are not supported in plugins for exactly this reason. It’s not possible for the plugin instance to interrupt the nested modal loop and exit cleanly. I’d highly recommend setting JUCE_MODAL_LOOPS_PERMITTED=0 in the preprocessor flags for plugin builds, to ensure that modal loops are not used.
How are you calling launchAsync? Note that the dialog will be automatically closed as soon as the FileChooser instance goes out of scope. Therefore, you should not do this:
juce::FileChooser chooser { ... };
// Opens the filechooser dialog.
chooser.launchAsync (...);
// 'chooser' goes out of scope here, immediately closing the dialog again.
Instead, you should ensure that the FileChooser lives for at least as long as the dialog should remain open. Consider adding it as a data member on your editor, or another similarly long-lived object.
This is the class member we use: std::unique_ptr <juce::FileChooser> m_fileChooser{};
In the click handler have this code: m_fileChooser.reset(new juce::FileChooser(...)); m_fileChooser->launchAsync(...);
In juce 6.0.7 the same code launches the FileChooser, but the FileChooser stayes open after the plugin is removed.
As I recognized that launchAsync does not work with the latest juce develop branch I just tried showDialog.
I collected further details:
For the native windows FileChooser the method launchAsync throws this exception: std::bad_weak_ptr at memory location 0x000000000014EA40.
Since this exceptions happens inside launchAsync which is called at the end of the click handler
it appeared to me like nothing happens.
If I only set useOSNativeDialogBox to false in the constructor call to juce::FileChooser the complete process with launchAsync works as expected with the non-native juce FileChooser opening. After choosing a file the non-native juce FileChooser closes as expected and returns the choosen file.
If I close the plugin during the non-native juce FileChooser is open the FileChooser closes too, and
I get a memory leak of one juce::StringArray.
I have tested your new commit fd2f866dd1de58344ec4ed27611cfd61643ae303.
Results:
The exception is gone and the native Windows FileChooser opens. I could choose a file and the FileChooser returns this file.
I removed the plugin while the native windows FileChooser was open. Then FileChooser was also closed.
Small problems:
The native Windows FileChooser creates a memory leak of 24 bytes for every open-cancel-cycle and open-choose-close-cycle (on Windows 64 Bit, Ableton Live 11). The non-native juce FileChooser has no memory leak.
Both FileChooser (native and non-native) create a memory leak of a juce::StringArray instance if I remove the plugin while they are still open.
I think I see what’s going wrong here, I’ll push a fix shortly.
I can’t repro this - could you try replacing the JUCE_LEAK_DETECTOR in StringArray with a JUCE_HEAVYWEIGHT_LEAK_DETECTOR instead? This should give a stack trace pointing to where the leaked object is allocated.
I still can’t repro the second leak. If it’s not also reported by MSVC’s CRT leak checker, there’s a chance this is a false positive due to a JUCE component being retained by the system and freed after the plugin’s destructor has run.
The second leak seems to be a leak of FileDropTarget which contains DragInfo and DragInfo contains StringArray. I added HeavyweightLeakedObjectDetector to FileDropTarget to get this info:
That’s really strange - it looks like the peer being leaked is that of the main editor, rather than the FileChooser. Does this issue definitely only occur when you add a FileChooser to your editor?
I checked again: Just removing the plugin in the state just before one click to open the native FileChooser no memory leak occurs. Closing the plugin while the FileChooser is open the memory leak appears. Opening the FileChooser and closing it with cancel then removing the plugin no memory leak occurs.
I checked a bit more:
a)
Opening the plugin is calling the constructor FileDropTarget once
Removing the plugin is calling the destructor FileDropTarget once
No leak
b)
Opening the plugin is calling the constructor FileDropTarget once
Opening the FileChoose another call to constructor FileDropTarget happend
Closing the FileChooser the destructor of FileDropTarget is called
Removing the plugin is calling the destructor FileDropTarget again
No leak
c)
Opening the plugin is calling the constructor FileDropTarget once
Opening the FileChoose another call to constructor FileDropTarget happend
Removing the plugin the destructor FileDropTarget is called once for the object the FileChooser has created. The object created during plugin start is not destructed.