AlertWindow from within a DLL


#1

I’ve been trying to call a function that pops up a Juce AlertWindow from within a DLL, but with no success. If I change my function to the following:

The following works…

/** Give the user a window to choose a port to open. */
extern "C" __declspec(dllexport) 
   bool popupPortSelector ()
{
   AlertWindow::showNativeDialogBox (
      T("test"),
      T("test"), 
      true);
   return false;
}

But if i change it to this, or any other Juce alertwindow alternative, I get an exception and crash as soon as the function is called

/** Give the user a window to choose a port to open. */
extern "C" __declspec(dllexport) 
   bool popupPortSelector ()
{
   AlertWindow::showMessageBox (
      AlertWindow::InfoIcon,
      T("test"), 
      T("test"));
   return false;
}

Is there something obvious i need to do first? I’m using some other juce features fine (Core stuff) but obviously there’s no ‘app’ setup stuff going on. perhaps that’s what i need to do too? (this is the only time the dll tries to create any sort of Component, although the program using the DLL is juce based).


#2

You’ve got to call initialiseJuce_GUI() and shutdownJuce_GUI(), and probably call PlatformUtilities::setCurrentModuleInstanceHandle() with your DLL hModule

Cedric


#3

thanks for that! :slight_smile:

now, however, i’m having an odd thread-related problem. My DllMain appears to get called TWICE with DLL_THREAD_ATTACH on load, and only ONCE with DLL_THREAD_DETACH on closing. A few seconds after the program has been closed, i get a breakpoint warning telling me that a thread needs to be killed by force.

Anyone have any ideas what might be causing this? The only jucey thing i’m doing is creating an alertwindow on the stack, calling its runModalLoop and responding to it; it is obviously deleted after the function has ended.

i’m stuck once more!


#4

if it’s any help, the remaining thread is called __tmainCRTStartup …

/me goes bonkers


#5

well, it’s nothing to do with the alertwindow. if i use any functions from the DLL in my code, i get this problem even if they do nothing (using a function just to make sure that the DLL is actually loaded).

if it helps at all, this is what my DllMain function looks like:

extern "C"  int WINAPI DllMain (HANDLE hmodule, DWORD dwReason, PVOID pvReserved)
{
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
		{
			DBG (T("DLL_PROCESS_ATTACH"));
			PlatformUtilities::setCurrentModuleInstanceHandle (hmodule);
			initialiseJuce_GUI ();
			return true;
			break;
		}
	case DLL_PROCESS_DETACH:
		{
			DBG (T("DLL_PROCESS_DETACH"));
			shutdownJuce_GUI ();
			return true;
			break;
		}

	case DLL_THREAD_ATTACH:
		DBG (T("DLL_THREAD_ATTACH!"));
		return true;
		break;        
    case DLL_THREAD_DETACH:
		DBG (T("DLL_THREAD_DETACH!"));
		return true;
        break;

	default:
		DBG (T("Unknown DLLMAIN reason ???"));
        return false;
    }
	return false;
}

a few lines in there are redundant i realise, but does this reveal something completely obvious that i’m missing?

I thought that maybe my shutdownJuce_GUI might be buggering up the equivalent call in my main (juce based) process; taking that out removes the exception but obviously causes loads of memory leaks.


#6

million posts every five minutes :smiley:

it’s definitely getting stuck at shutdownJuce_GUI().

this is what the top of the call stack looks like at the point of the crash…

>	DLL.dll!juce::Thread::stopThread(const int timeOutMilliseconds=4000)  Line 176	C++
 	DLL.dll!juce::InternalTimerThread::~InternalTimerThread()  Line 75	C++
 	DLL.dll!juce::InternalTimerThread::`scalar deleting destructor'()  + 0x2b bytes	C++
 	DLL.dll!juce::DeletedAtShutdown::deleteAll()  Line 79 + 0x34 bytes	C++
 	DLL.dll!juce::shutdownJuce_GUI()  Line 301	C++

and this is what i’m starting to look like…


#7

Just as a kick, try it without the PlatformUtilities::setCurrentModuleInstanceHandle (hmodule); line, I had that crashing one of my dll’s.


#8

not a sausage of joy :’( thanks tho


#9

maybe have a gander at the audioplugin code? before it came out I was trying to use juce in a VST. I asked big J and he went away and came back with the JAP, telling me I couldn’t have done it myself as there was complications of using juce in a DLL. to do with the event loop and stuff.

prolly pointing up a duff tree here, but I feel your pain!

g’luck soldier.


#10

your code looks ok to me… same as the JAP code, which works fine.

Can you see any more detail in the bit that’s crashing, e.g. what bit of stopThread() is actually causing the crash? And what other threads are hanging around at the time?


#11

well there’s only that one thread still (__tmainCRTStartup) going when it crashes… the others just disappear calmly. It’s not an ‘exception’ per se, it’s a breakpoint type exception (jassertfalse) - the bit that says “it’s bad karma…” - there’s not an actual explosion inside the program, but the thread just doesn’t die by itself and juce gives me a stern look.

i think i’m going to start a completely basic DLL from scratch and pop stuff back into it to see where it starts going nuts.

cheers for the help so far guys, i’m sure i’ll be back for ROUND 6


#12

one other thing to watch for are any static objects you’ve got - if a static object’s constructor or destructor does any juce stuff, that could be called before or after the dll entry point, so could happen when juce isn’t initialised. That might mean that juce objects get deleted in the wrong order on shutdown.


#13

ooh when you said that i thought “of course!”

i’ve changed my .cpp file to use the heap for the main object the dll functions use, so that i can control where and when it is created/destroyed…

BOOL WINAPI DllMain (HINSTANCE instance, DWORD dwReason, LPVOID)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
         PlatformUtilities::setCurrentModuleInstanceHandle (instance);
         initialiseJuce_GUI();
         midiOutput = new MIDI_SingleOutputManager;
   }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
         deleteAndZero (midiOutput);
         shutdownJuce_GUI();
    }

    return TRUE;
}

so surely it’s not possible for it to be having any kind of effect on it now?but i still have the problem… :frowning: :frowning:

this is the output of the debugger when the program is closed (i’ve put DBG() lines at the beginning and end of the MIDI_SingleOutputManager destructor to make sure any tidying code has definitely been carried out)

The thread 'Win32 Thread' (0xf6c) has exited with code 0 (0x0).
The thread '_threadstartex' (0xf54) has exited with code 0 (0x0).
Deleting SingleOutputManager object...
Deleted manager!
AppUsingDLL.exe has triggered a breakpoint
** Warning - Forced thread termination **
Detected memory leaks!
Dumping objects ->
c:\coding\juce\src\juce_appframework\events\juce_message.h(81) : {310} normal block at 0x00C428C8, 24 bytes long.
 Data: <  ,             > 10 AF 2C 10 CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
Detected memory leaks!
Dumping objects ->
c:\coding\juce\src\juce_appframework\events\juce_message.h(81) : {310} normal block at 0x00C428C8, 24 bytes long.
 Data: <  ,             > 10 AF 2C 10 CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
The program '[3692] AppUsingDLL.exe: Native' has exited with code 0 (0x0).

the breakpoint is line 176 of juce_Thread.cpp (jassertfalse), and at this point the ‘threads’ panel of the debugger shows:
__tmainCRTStartup (in location juce::Thread::stopThread)


#14

Could be that an object derived from Thread is getting deleted twice, so that the second time it’s a dangling pointer. That’d explain why it’s trying to stop a thread that’s not actually running.

Try sticking a breakpoint in Thread::stopThread and inspect the objects that are calling it.


#15

i’ve just done a bare-minimum test project to show what happens even with nothing going on…

Basic Project

There are two projects in the DLLTest project- build them both (dll and app - should be painless) and run it in debug mode. The app is just the basic ‘starting point’ app, and the DLL contains just one foo() function, enough to make sure it is loaded; initialiseJuce_GUI() and shutdownJuce_GUI are called too.

I’ve tried to make it as simple as possible to test- it should really be no effort to just load, build and see what it is it’s doing. it really has me stumped and i can’t figure out what might need doing.

I take it that the DLL is loaded first, with its own juce initialisation, then the main app gets loaded (and uses the juce application init stuff)… then at close the application object is destroyed, then the DLL is detached, and then the process terminates.

That’s the way it seems to be anyway. Maybe it’s just not possible to have this kind of static linking of a DLL using Juce GUI elements into a juce GUI app?


#16

Ok, I just had a go of this…

There are two timer threads - one in the exe, one in the dll, and the first one gets deleted ok, but the second one is forcibly killed by something else - probably the c runtime library or win32 - so that when the juce thread stuff tries to kill it, it gets confused because the thread has already gone without having shut down properly.

I need to look into a bit more to think of a good solution…


#17

many thanks for taking the time to have a look at it jules :slight_smile:


#18

Thanks for all of your hard work Jules. I’d like to post a follow-up on this question if I may.

Was this issue ever resolved? I’m having the same issue right now - my app worked fine while it was a static library but the minute I converted it into a DLL I started getting runtime errors. If not, can you recommend any workarounds?

Thanks,
Mike


#19

Wow, this is an old post!

I think this was probably before I used some cunning new/delete operators in all the classes to work around the DLL problems, but there could still be errors if any bits of the code are missing a juce_UseDebuggingNewOperator macro or accidentally use malloc or new[] instead of juce_malloc…

TBH I’d really like to scrap the DLL build, though I know some people are using it. What’s your reason for choosing it?


#20

I suppose that a static library would make things a lot easier, maybe I just like making things more complicated than they need to be!

I think I found the root of the problem though. I have two applications, let’s call them A and B. Application B, which references the Juce DLL, is also a DLL which is called by Application A. Application A references the Juce Static Library, which leads to a runtime error when Application A terminates (the program attempts to terminate the Juce library twice, resulting in the error). I guess this means I can’t mix and match my static and dynamic libraries?