How to not leak with simple console app + AlertWindow?

Hi all!

I’m trying to get a very simple cross-platform console application running using Juce for the GUI. The app is far from complex enough to warrant a true main window class, so I’ve opted for (ab)using an AlertWindow with addTextEditor etc. I basically only need to get simple user feedback from a dialog, then do something with it and exit. I would really like to just

  • have a single int main()
  • in it, set up an AlertWindow
  • add some text editors to it
  • call AlertWindow.runModalLoop() (no async here please)
  • some simple code to handle the input on OK
  • return 0 and be done with it.

But running a 64-bit debug build in Visual Studio 2015 on Windows 10 using Juce 5.3.2, this leaks either memory or an AsyncCallInvoker… The latter comes from using addTextEditor().

I’m probably doing it all horribly wrong :), but I can’t seem to find pointers on how else to do it as simple as possible. Note that the below example apps are about as complex as the final app will be! So adding a whole GUI class or non-modal showing and running my own message loop etc. would be overkill.

I also did try my code in a more ‘official’ setup;

  • create a true GUI application in the Projucer (JUCEApplication derived)
  • modally pop up an AlertWindow in initialize() right before main window is created
  • start the true Hello World main window
  • click away the main window
  • let it handle app shutdown
    and then it all worked out fine, but I think the below should also just work?

It seems that by having the AlertWindow instance in it’s own scope, it leaks some internals when it goes out of scope, and that it can be protected by having it live in the same scope as the main juce::ScopedJuceInitialiser_GUI. JUCEApplication.quit() also can bring in some magic it seems, but only if called at the right spot.

What I’ve tried is:

========

// console app
// gives 3 memory leaks (16 bytes, 32 bytes and another 32 bytes)
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
int main() {
  juce::ScopedJuceInitialiser_GUI juceGui;
  juce::AlertWindow::showMessageBox(juce::AlertWindow::NoIcon, "Title", "Message", "OK");
  return 0;
}

========

// console app
// same memory leaks
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
int main() {
  juce::ScopedJuceInitialiser_GUI juceGui;
  juce::AlertWindow input("Title", "Message", juce::AlertWindow::NoIcon);
  input.addButton("OK", 0);
  input.runModalLoop();
  return 0;
}

========

// console app
// gives assert on leaked objects;
//    *** Leaked objects detected: 1 instance(s) of class AsyncCallInvoker
//    If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for
//    your object management. Tut, tut. Always, always use std::unique_ptrs, OwnedArrays,
//    ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
// nope.
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
int main() {
  juce::ScopedJuceInitialiser_GUI juceGui;
  juce::AlertWindow input("Title", "Message", juce::AlertWindow::NoIcon);
  input.addTextEditor("text", "", "Text:");
  input.addButton("OK", 0);
  input.runModalLoop();
  return 0;
}

========

// console app
// triggers a breakpoint in ~Desktop()
//    // doh! If you don't delete all your windows before exiting, you're going to
//    // be leaking memory!
//    jassert (desktopComponents.size() == 0);
// seems legit :)
int main() {
  juce::AlertWindow* input;
  {
    juce::ScopedJuceInitialiser_GUI juceGui;
    input = new juce::AlertWindow("Title", "Message", juce::AlertWindow::NoIcon);
    input->addTextEditor("text", "", "Text:");
    input->addButton("OK", 0);
    input->runModalLoop();
  }
  delete input;
  return 0;
}

========

// GUI app
// AsyncCallInvoker not leaked anymore, but got 16 bytes memory leak
// however, much more overhead like this
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
class C_MyApp : public juce::JUCEApplication {
public:
  C_MyApp() {}
  const juce::String getApplicationName() override { return "My app"; }
  const juce::String getApplicationVersion() override { return "1.0"; }
  bool moreThanOneInstanceAllowed() override { return true; };
  void initialise(const juce::String& commandLine) override {
    juce::ScopedJuceInitialiser_GUI juceGui;
    juce::AlertWindow input("Title", "Message", juce::AlertWindow::NoIcon);
    input.addTextEditor("text", "", "Text:");
    input.addButton("OK", 0);
    input.runModalLoop();
    quit();
  }
  void shutdown() override {};
  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(C_MyApp)
};
START_JUCE_APPLICATION(C_MyApp)

========

// GUI app
// AsyncCallInvoker leaked again
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
class C_MyApp : public juce::JUCEApplication {
public:
  C_MyApp() {}
  const juce::String getApplicationName() override { return "My app"; }
  const juce::String getApplicationVersion() override { return "1.0"; }
  bool moreThanOneInstanceAllowed() override { return true; };
  void initialise(const juce::String& commandLine) override {
    juce::ScopedJuceInitialiser_GUI juceGui;
    {
      juce::AlertWindow input("Title", "Message", juce::AlertWindow::NoIcon);
      input.addTextEditor("text", "", "Text:");
      input.addButton("OK", 0);
      input.runModalLoop();
    }
    quit();
  }
  void shutdown() override {};
  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(C_MyApp)
};
START_JUCE_APPLICATION(C_MyApp)

========

// GUI app
// AsyncCallInvoker not leaked anymore, but 16 bytes memleak
#include "..\..\Juce\JuceLibraryCode\JuceHeader.h"
class C_MyApp : public juce::JUCEApplication {
public:
  C_MyApp() {}
  const juce::String getApplicationName() override { return "My app"; }
  const juce::String getApplicationVersion() override { return "1.0"; }
  bool moreThanOneInstanceAllowed() override { return true; };
  void initialise(const juce::String& commandLine) override {
    juce::ScopedJuceInitialiser_GUI juceGui;
    {
      juce::AlertWindow input("Title", "Message", juce::AlertWindow::NoIcon);
      input.addTextEditor("text", "", "Text:");
      input.addButton("OK", 0);
      input.runModalLoop();
      quit();
    }
  }
  void shutdown() override {};
  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(C_MyApp)
};
START_JUCE_APPLICATION(C_MyApp)

I don’t know if your issue is related but :

EDIT : I hadn’t seen that you already tried it

You might think it doesn’t warrant a full GUI, but did you try a full GUI with your text editor as a child of the GUI to see if it solves the problem of leaking? just use a small setSize()