(Part 1.)
The AFX_MANAGE_STATE macro is an iceberg-tip of some heavy machinery, whose documentation leaves much to be desired. Here’s an attempt to fill in some more blanks.
Mid-Depth Dive
Answering a question raised at part 1, the basic statement –
AFX_MANAGE_STATE(AfxGetStaticModuleState())
couldn’t be made simpler, simply because a broad statement like ‘always use resources from the last called-into user-module’ just won’t hold. You must leave a way for a developer to say stuff like ‘this GUI call, in that DLL, needs to use resources from my main executable’. So some coding must be done, one way or another.
So what alternatives are there to AfxGetStaticModuleState? Basically there are only 4 available module state accessors:
- AfxGetModuleState()
- AfxGetAppModuleState()
- AfxGetStaticModuleState()
- AfxGetOleModuleState()
AfxGetOleModuleState() accesses MFC-internal resources, and isn’t really useful to users.
AfxGetStaticModuleState() accesses a per-dll module state. It technically returns the static variable –
_AFX_DLL_MODULE_STATE afxModuleState
– defined in dllmodul.cpp. This file is built into the static-lib component of MFC (such a component exists even when you link against MFC dynamically!), and thus the variable afxModuleState is instantiated separately when every dll is loaded. Hence, this is the module state that you want to switch to and back from (via the AFX_MANAGE_STATE macro), when you need to access dll-specific resources.
AfxGetAppModuleState() accesses a per-executable module state. Technically it returns _afxBaseModuleState, defined as –
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
– in afxstate.cpp. There’s really nothing ‘process local’ in the PROCESS_LOCAL macro, but since afxstate.cpp is built into mfc[ver][u][d].dll, it is indeed a process-wide singleton (provided that all the user dll’s link to MFC dynamically, and against the same MFC version, as they should).
AfxGetModuleState() accesses the currently active module state. This is actually a tricky concept: the currently active module state is managed per-thread, as the member m_pModuleState in the aptly named _AFX_THREAD_STATE type. The AfxGetModuleState implementation falls back essentially to AfxGetAppModuleState if the thread’s ‘current’ module state is null.
Advanced Scenarios
Suppose a dll calls back into the executable, and this callback needs to access exe resources:
// MyDll.cpp: void DllFunc( tCallback cb ) { cb(); } // MyExe.cpp: typedef void (*tCallback)(); void MyCallback() { // What do you declare here to access the proper resource handle? MyDialog dlg; // this dialog template is an *exe* resource dlg.doModal(); } int main() { DllFunc(MyCallback); return 0; }
As noted the only documented module-state management facility is AFX_MANAGE_STATE(AfxGetStaticModuleState()). One would hope that ‘the root executable’ can be a valid ‘StaticModuleState’ but it turns out that the implementation treats dll’s and exe’s differently and this facility doesn’t hold in this case. As I answered on SO a while ago, the undocumented AfxGetAppModuleState gets the job done here, and the right code is –
void MyCallback() { AFX_MANAGE_STATE( AfxGetAppModuleState() ); MyDialog dlg; dlg.doModal(); }
One can easily form even more exotic scenarios: what if dll1 calls into dll2 which calls into dll3, and this topmost function needs to utilize resources from dll1? What if a function using resources is implemented once in a static lib, linked against both an exe and a dll and is called from both?
There are no off-shelf facilities for such cases, I imagine mostly because it’s very non-obvious what is the expected behaviour for them. Using the ready MFC-generated module states and accessors already mentioned one can generally tailor the module-state behaviour to one’s needs. If worst comes to worst you can technically create and manage your own module states, but I have yet to see a situation where AFX_MANAGE_STATE with AfxGetStatic/AppModuleStates does not suffice.
Final Thoughts
Afx module states are just complex. Much of the complexity stems from the real complexity of the tasks at hand: the already-formidable task of masking away resource-handle management must be implemented to support contexts of executables, dll’s and static libs, all linked either dynamically or statically against MFC.
Still, the documentation is badly outdated. (the main technical article refers to MFC 3 and a windows 3.1 relic called Win32s), and the actual initialization of potential module states and thread states seems much more complicated than it needs to be. I have this nagging fear that even MS engineers tread very carefully around the darker corners of this code.