Windowed mode and console mode executables sharing the same Juce DLL


#1

Hi,

My software solution is now composed by multiples executables files (.exe).
Each executable links with Juce.dll. (-DJUCE_DLL=1)
Some executables run in windowed mode, others run in console mode.


My Juce DLL is built once without the _CONSOLE compiler definition.
All windowed executables link perfectly with juce.dll,
but all executables in console mode failed to link because of the missing definition of the following method

static int JUCEApplicationBase::main(int argc, const char* argv[]);


I thinks it will be nice that JUCEApplicationBase expose the two different kind of entry point allowing Juce.dll be linked with all executable kinds. This will avoid to rebuilt Juce twice with different compiler definitions.


To allow this, I modify the current juce_ApplicationBase.cpp file like this:

 

#if JUCE_ANDROID

StringArray JUCEApplicationBase::getCommandLineParameterArray() { return StringArray(); }
String JUCEApplicationBase::getCommandLineParameters()          { return String(); }

#else

#if JUCE_WINDOWS // D/Labs comment this && ! defined (_CONSOLE)

String JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameters()
{
    return CharacterFunctions::findEndOfToken (CharPointer_UTF16 (GetCommandLineW()),
                                               CharPointer_UTF16 (L" "),
                                               CharPointer_UTF16 (L"\"")).findEndOfWhitespace();
}

StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray()
{
    StringArray s;

    int argc = 0;
    if (LPWSTR* const argv = CommandLineToArgvW (GetCommandLineW(), &argc))
    {
        s = StringArray (argv + 1, argc - 1);
        LocalFree (argv);
    }

    return s;
}

#else

#if JUCE_IOS
 extern int juce_iOSMain (int argc, const char* argv[]);
#endif

#if JUCE_MAC
 extern void initialiseNSApplication();
#endif

// D/Labs comment this
// #if JUCE_WINDOWS
//  const char* const* juce_argv = nullptr;
//  int juce_argc = 0;
// #else
 extern const char* const* juce_argv;  // declared in juce_core
 extern int juce_argc;
// #endif

String JUCEApplicationBase::getCommandLineParameters()
{
    String argString;

    for (int i = 1; i < juce_argc; ++i)
    {
        String arg (juce_argv[i]);

        if (arg.containsChar (' ') && ! arg.isQuotedString())
            arg = arg.quoted ('"');

        argString << arg << ' ';
    }

    return argString.trim();
}

StringArray JUCEApplicationBase::getCommandLineParameterArray()
{
    return StringArray (juce_argv + 1, juce_argc - 1);
}

#endif // move by D/Labs

int JUCEApplicationBase::main (int argc, const char* argv[])
{
    JUCE_AUTORELEASEPOOL
    {
       #if !JUCE_WINDOWS // D/Labs add this
        juce_argc = argc;
        juce_argv = argv;
       #endif            // D/Labs add this

       #if JUCE_MAC
        initialiseNSApplication();
       #endif

       #if JUCE_IOS
        return juce_iOSMain (argc, argv);
       #else
        return JUCEApplicationBase::main();
       #endif
    }
}

// #endif // D/Labs move this

 

This modification lets the console mode executables to benefit of the unicode version of getCommandLineParameters, who is currently only available for Windowed mode executables.

An optional enhancement of this fix is to modify the START_JUCE_APPLICATION to always call the main(int argc, const char* argv[]) overload and make the JUCEApplicationBase::main() private.


Thanks for your concern about this !

 


#2

Nobody is interested by this fix ?

sad


#3

Sorry, hadn't seen this post!

Not sure about whether it's a good idea - it's a real edge case. Isn't there some other kind of trick you could use, like defining your own version of the missing symbol?


#4

Thank you for having had a look on this post

I cannot define my own version of the missing symbol because it's prefixed by the JUCEApplicationBase class name. As you can see in the signature returned by Visual Studio 2010 SP1 64bit Linker:

error LNK2019: unresolved external symbol 
"__declspec(dllimport) public: static int __cdecl juce::JUCEApplicationBase::main(int,char const * * const)" (__imp_?main@JUCEApplicationBase@juce@@SAHHQEAPEBD@Z) referenced in function main

 

Another ugly trick could be to localy redefine START_JUCE_APPLICATION when I build console executable only :

#undef START_JUCE_APPLICATION
#define START_JUCE_APPLICATION(AppClass) \
    static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
    extern "C" JUCE_MAIN_FUNCTION \
    { \
        juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \
        return juce::JUCEApplicationBase::main (/**enforce no argument overload*/); \
    }

I thinks this last fix version is not very friendly for the Juce comunity: Extra code to add on the client side, and force Juce.dll to be built without _CONSOLE preprocessor definition.

That's why I prefer the first solution with common client code and preprocessor definition agnostic Juce.dll build. I tested both solution here, they work pretty well. I hope this can help

Thanks again spending your precious time on this edge case.