My recent AFX_MODULE_STATE plunge was driven by some real world problems. In parallel to the write-up I mailed the current MFC chief maintainer, Pat Brenner – who directly invited such user interaction.
The correspondence that ensued makes semi-official claims that aren’t documented elsewhere. I got Pat’s approval to blog it – and here it is, almost verbatim (slight abridges to keep to the technical stuff).
Thanks a lot Pat!
Subject: MFC Module States Question
suppose a dll calls back into an executable, as described here. If the callback uses AFX_MANAGE_STATE(AfxGetStaticModuleState()) the program asserts, and doesn’t find the (exe’s) resource handle. The solution that worked for me – which I suggested at that forum – is to use instead the undocumented
Next, suppose some functionality that consumes resources is written in a static lib, and linked into both an executable and a dll (of the same application). Now neither Static nor App module states can work for both cases…
As a user I would expect that AfxGetStaticModuleState would have identical semantics when called from a dll and from an executable (that’s how I view the ‘Static’ description). Digging around the MFC sources I have a conjectured reason why it isn’t so:
RawDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
includes the lines –
// set module state before initialization
_AFX_THREAD_STATE* pState = AfxGetThreadState();
pState->m_pPrevModuleState = AfxSetModuleState(&afxModuleState);
If similar statements would have run somewhere during the exe module initialization, AFX_MANAGE_STATE(AfxGetStaticModuleState) would have worked equally well there too.
…I’ve checked with some other knowledgeable MFC folks and here’s the upshot.
AfxGetStaticModuleState is meant to be used ONLY by DLL functions, and NOT by the main executable. That function will return a NULL pointer when called from the Exe. AfxGetAppModuleState is not documented, and should be, as it is the correct function to call when another MFC module may call back into the exe. There are some very old KB articles and other things that mention this. Normally, the use of the “App” version would not be needed; but when another MFC module has called back into the app (rather than just returning to the app) like this, it would be appropriate to call it to fix the module state. So in your scenario of a static library, you will need to #ifdef your code (just as MFC does for DLL/static) so your code calls the right method.
We will get documentation for AfxGetAppModuleState written so it is no longer undocumented.
The problem with the #ifdef solution for a static library, is that during the static lib generation there’s no general way to tell whether the resulting lib would be linked into an exe or into a dll. This is not (as far as I understand) the MFC scenario where the same source code can form either an exe or a dll – my source generates a lib either way.
The root issue that I don’t understand is why the need for two different module-state accessors (AfxGetStaticModuleState & AfxGetAppModuleState). As noted, it seems that the code line that makes AfxGetStaticModuleState work, is the line from RawDllMain:
If a similar line would be called somewhere along the initialization of an exe module (maybe just somewhere more general, say in the initialization of a WinApp), it seems the ..GetStatic.. api would workfor both dll’s and exe’s.
Was that a deliberate decision to separate the API’s? is there some design consideration or technical limitation i’m missing?
Subject: MFC Module States Question
I’ve checked with another MFC expert, and here is our opinion:
We don’t think there’s a one-size-fits-all solution here. Changing things at the level of RawDllMain can be very complicated, and have unexpected downstream effects. Your statement contains the word “seems” and that often gets me into trouble, given the life-span of MFC and the applications written with it. A change like this might pass all my regression tests and then break some customer after we ship the change. Additionally, I don’t believe you should call either of the functions when linking into an MFC extension (rather than regular) DLL. Or maybe one is OK, but the other not—I would have to investigate to know for sure.
The bottom line is that static libraries are probably overstepping their bounds when doing anything that affects module state. As you said, the static library code doesn’t know the type of module it is linked to, so that makes any module state manipulation dangerous.