Question

I have built the folowing configuration:

  • A) MFC Extension DLL having 2 MFC dialogs.
  • B) MFC regular dll that uses DLL A functions.
  • C) win32 application (NON MFC) calling for function from DLL B

When calling functions from DLL B that inside call functions from DLL A to display a dialog an error occurs due to the fact that resource can not be found.

I have digged to find the exact root cause and themain reson seems to be the fact that the module context is set to the calling dll B rather than to the DLL A, which contains the dialog resource.

Inside DllMain the initialization is done as described in the MSDN:

static AFX_EXTENSION_MODULE NEAR extensionDLL = { NULL, NULL };

extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{   
   if (dwReason == DLL_PROCESS_ATTACH)
   {      
       Hinstance = hInstance;  //save instance for later reuse
      // Extension DLL one-time initialization
      if (AfxInitExtensionModule(extensionDLL,hInstance) == 0)
      {
          AfxMessageBox("Error on init AfxInitExtensionModule!");
          return 0;
      }
      // Insert this DLL into the resource chain
      new CDynLinkLibrary(extensionDLL);
   }
   else if (dwReason == DLL_PROCESS_DETACH)
   {
       Release();
   }
   return 1;
}

One workarround that i've found was to store the hInstance parameter received from DLLMain: extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) and inside DLL A when functions are called, I save current handle and set new handle the handle received from DllMain:

DLL A function1(............)
{
    HINSTANCE HinstanceOld = AfxGetResourceHandle(); 
    AfxSetResourceHandle(CErrohInstance); 
    .......
    //display dialog
    .....
    AfxSetResourceHandle(HinstanceOld);
}

By using this workarround It still causses assertion but the dialogs are shown.

What should be the normal way of solving this problem?

Was it helpful?

Solution

You have to insert the resources of the extension DLL into the resource chain of the regular DLL, not the EXE. Just create a function in the extension DLL and call it in the InitInstance method of the regular DLL, like this:

void initDLL()
{
  new CDynLinkLibrary(extensionDLL);
}

OTHER TIPS

You say "module context" but in fact the terminus technicus is "module state".

AFAICS this is the relatively standard (i.e., most frequently occurring) MFC module state related use case here, namely: entering via callbacks / public exported APIs into internal implementation area.

AFX_MANAGE_STATE directly mentions this use case: "If you have an exported function in a DLL"

At this point, the module state that is currently active is the one of the calling party, which is not the one which is needed within implementation scope. Since implementation scope knows that it needs a different module state (and it is the one to know which is the correct one!), it needs to temporarily switch to the correct one, to achieve having any module state related lookup (precisely: resource instance lookup) done within the correct instance scope.

And this needs to be done not manually via AfxSetModuleState(), but rather via the properly lifetime-scoped (guarantees proper destruction, at whichever cancellation points may exist, be it return or exception or whatever) mechanism of AFX_MANAGE_STATE macro.

IOW, implementation likely needs to strongly resemble something like:

BOOL MyPublicAPIOrCallback()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); // ensure locally expected module state within this externally-invoked handling scope

    some_handling_which_does_resource_lookup_or_whatever;
}

Dont know if you have already found the solution, if not You can try using

AfxFindResourceHandle

before accessing the problematic resource in Dll A.

I have add this lines in my DLLMain and now I don't have problems to use resources that are in other DLL's called by my DLL, like dialogs. This is the Code:

static AFX_EXTENSION_MODULE CODIAbantailDLLDLL = { NULL, NULL };

AplicacionBase      theApp;

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    // Quitar lo siguiente si se utiliza lpReserved
    UNREFERENCED_PARAMETER(lpReserved);


    if (dwReason == DLL_PROCESS_ATTACH)
    {
        // ******** VERY IMPORTANT ***********************
        // IF you doesn't put this, when you call other DLL that has 
        // its owns resources (dialogs for instance), it crash
        CoInitialize(NULL);
        AfxWinInit(hInstance, NULL, ::GetCommandLine(), 0);
        AfxEnableControlContainer();
        //**************************************************
        TRACE0("Inicializando CODIAbantailDLL.DLL\n");

        // Inicialización única del archivo DLL de extensión
        if (!AfxInitExtensionModule(CODIAbantailDLLDLL, hInstance))
            return 0;

        new CDynLinkLibrary(CODIAbantailDLLDLL);

    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        TRACE0("Finalizando CODIAbantailDLL.DLL\n");

        // Finalizar la biblioteca antes de llamar a los destructores
        AfxTermExtensionModule(CODIAbantailDLLDLL);
    }
    return 1;   // aceptar
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top