AAX and DllMain multiple definition on Windows


#1

Hi,

I’ve noticed that when building an audio plug-in for Windows, if one wants to include all the available formats (AAX, RTAS and VST), it is likely that the DllMain function in the AAX wrapper will cause a multiple definition because there is no guard against it.

I think the current implementation that avoids this conflict between RTAS and VST could be extended and generalised, in order to accomodate AAX (and other possible plug-in formats in future):

First consideration: disambiguation of DllMain functions is only needed if two or more plug-in formats are to be built, thus this code could do the trick:

#if (JucePlugin_Build_AAX + JucePlugin_Build_RTAS + JucePlugin_Build_VST) > 1
# define JucePlugin_MultiDLLMain 1
#endif

Then, where each wrapper implements its DllMain function, a check against the JucePlugin_MultiDLLMain definition will tell if it can be actually named DllMain or if it needs to have a suffix (DllMainVST, DllMainRTAS, DllMainAAX and so on) to avoid conflicts with the others.

In the latter case, a proper DllMain must be implemented (in a location where it gets compiled regardless of the enabled/disabled plug-in formats), that detects in turn which of the suffixed DllMains should be called.

I think something like this will do:

#if JucePlugin_MultiDLLMain

#if JucePlugin_Build_RTAS
extern "C" BOOL WINAPI DllMainRTAS (HINSTANCE, DWORD, LPVOID);
#endif

#if JucePlugin_Build_VST
extern "C" BOOL WINAPI DllMainVST (HINSTANCE, DWORD, LPVOID);
#endif

#if JucePlugin_Build_AAX
extern "C" BOOL WINAPI DllMainAAX (HINSTANCE, DWORD, LPVOID);
#endif

extern "C" BOOL WINAPI DllMain (HINSTANCE hInstance, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    char moduleFileName [1024] = {0};
    char* pluginExtension = moduleFileName;
    
    GetModuleFileName (hInstance, moduleFileName, 1024);

    for (char* c = moduleFileName; *c != 0; ++c)
    {
        if (*c == '.')
        {
            pluginExtension = c;
        }
    }

#if JucePlugin_Build_AAX
    if (_stricmp (pluginExtension, ".aaxplugin") == 0)
    {
        return DllMainAAX (hInstance, ul_reason_for_call, lpReserved);
    }
    else
#endif

#if JucePlugin_Build_RTAS
    if (GetModuleHandleA ("DAE.DLL") != 0)
    {
        return DllMainRTAS (hInstance, ul_reason_for_call, lpReserved);
    }
    else
#endif

    {
#if JucePlugin_Build_VST
        return DllMainVST (hInstance, ul_reason_for_call, lpReserved);
#else
        jassertfalse;
#endif
    }
}

#endif // JucePlugin_MultiDLLMain

Obviously, if this code is implemented, it replaces the previous implementation of a disambiguating DllMain present in the RTAS wrapper file juce_RTAS_DigiCode3.cpp


#2

Thanks, I’ve done something similar and checked it in now - any sanity-checking would be appreciated!


#3

Ok, I think this is much better but there are a couple of things that I am not sure I understand:

  1. Why is that code left in juce_RTAS_DigiCode3.cpp? Say that I want to build a plug-in which is AAX and VST only. That code never gets compiled, and my plug-in ends having no DllMain at all, only a DllMainAAX and a DllMainVST that have no means to get called. I think adding a small .cpp file with that snippet of code, to be compiled in every plug-in build regardless of the formats, is the way to go

  2. About the body of the DllMain itself, I am not sure why you intend to call them all every time (at least, 2 of them, the DllMainVST and DllMainAAX) while I thought the original purpose of such DllMain was to understand which format has been used to load the plug-in and only call the matching DllMainXXX, skipping the calls to the others. Isn’t that supposed to be like that? Couldn’t this lead to problems?


#4

Yes, it’s in the RTAS file, but it does always get compiled. All of the cpp files are always included in any project, and they use #ifdefs to decide which code actually gets compiled for the formats you enable.

And if you actually look at the DllMain for VST/AAX, there’s actually almost nothing in there, they just need a chance to store the instance handle. It’s only RTAS that actually does any work in that function.


#5

You have a point regarding the content of the VST and AAX DllMains, but there is a problem in the implementation you are giving: in case the AAX plug-in is loaded in ProTools, the DllMainRTAS gets called anyway (because it’s ProTools loading DAE.DLL, so the if condition is true, I have checked setting a breakpoint just to be sure), so it enters the RTAS code even if no RTAS plug-in has been instantiated

As for including it in the RTAS code, honestly it doesn’t look very JUCE-y to me, even adding the fact that sooner or later RTAS plug-ins will be dismissed and that code will have no reason to be fed to the compiler at all. For example, I have some internal projects meant to be VST only, that don’t compile the wrappers for the other formats. Having to compile the RTAS wrapper files just to have a DllMain into my binary does not seem logical to me and I think may lead to confusion as the plug-in will kinda work, but it will lack the instance handle information set in the body of the DllMain


#6

Hmm… Good point about the AAX. I wonder how to tell when it’s getting loaded as an RTAS…?

The module’s manifest dictates that all of its cpp files are added to the project, even though some may not actually be active. Sure, I could put that DllMain stuff in any of the other files, or in a file of its own, but that’s really just an implementation detail. If I ever remove the file that it’s currently in, then of course I’ll move it to somewhere else.


#7

In the code I posted above, the module file extension is checked if it is an AAX. It may not be the most elegant piece of code, but it is working fairly well.

As for the addition of files to projects, I am sure that the manifest says so, but you are assuming that everybody out there uses introjucer to configure AND maintain the project, which I don’t think is always true. A file named juce_win32_PlugInDllMain.cpp would instead speak by itself, regardless of manifest files.

In addition, it seems to me that the function body for DllMainAAX and DllMainVST is identical. Moving it to the beginning of a “vanilla” DllMain stored in a flavour-less place seems sensible to me (I have not checked, but I suspect the RTAS wrapper does the same bit in addition to the rest, so that part could be moved too)


#8

Ah yes, I didn’t spot your filename check. Yeah, that might well be the only solution. I’ll take another look at all this shortly.


#9

Well done! Although there is a compilation error here, because the method name is not “extentionMatches” but “hasFileExtension” (juce_PluginUtilities.cpp, line 49) and the extension for AAX plugin is not “aax” but “aaxplugin”. Once these two last things are fixed, we are done!


#10

Dammit, I’d swear that I checked that bit of code… Thanks!


#11

Not to worry! That probably slipped in because you had either RTAS or AAX build disabled.

I am posting a lot lately: I am porting my projects to JUCE 2 after some years they have been developed using JUCE 1.5x. Over time I customized some little aspects of JUCE and it turned out to work perfectly, so I am suggesting all those little changes now.

Do I better open one topic for each of them (they vary a lot in subject) or do I better post them all into a single topic?


#12

Do it however you like! It’s all much appreciated!