On AFX_MODULE_STATE, or – Avoiding afxCurrentInstanceHandle Asserts, Part 2

(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:

  1. AfxGetModuleState()
  2. AfxGetAppModuleState()
  3. AfxGetStaticModuleState()
  4. 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.

Advertisements
This entry was posted in MFC. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s