Troubleshooting MFC state related issues
In this article we will be talking about MFC Module state and Thread state , and how do we troubleshoot issues related to Module state mismatch.
Why MFC dll does needs a module-specific state?
Well reason is MFC is a shared dll, it needs to be shared between various modules (dlls\exe) that are linked with it and uses its functionality. The module-specifc information that MFC needs to maintain includes but is not limited to resource-handle, activation-context, reference count of OLE modules, pointer to application object, window proc corresponding to module etc.
Why does it need a per-thread state?
MFC is a multithreaded dll , so it can execute on multiple threads of application at same time , it needs to maintain information about currently executing MFC modules on that thread among other things. Thread state is keyed in thread local storage. One of the important things present in thread state is pointer to module state.
What kind-of failures can one expect.
As a thread of execution leaves one MFC module and enters into another module, it is expected that module state pointer of thread state points to correct corresponding module state. If there is a mismatch then , whenever MFC tries to access a part of module state, like resource-handle ,handle to the activation context since the module state is not correct , application can run into crashes\hangs.
As an example let’s see how a wrongly set module-state can cause a wrong activation being popped from activation-context record stack. The activation context is created in AfxWinInit. It is destroyed in the AFX_MODULE_STATE destructor. An activation context handle is kept in AFX_MODULE_STATE.
The AFX_MANAGE_STATE macro activates and deactivates the activation context. You get a 0xC000000D exception with following at top of callstack.
# ChildEBP RetAddr
00 0012ef9c 7c942980 ntdll!RtlRaiseStatus(long Status = -1073741811)+0x26
01 0012f008 7c80a72d ntdll!RtlDeactivateActivationContext
02 0012f018 781ffa1e kernel32!DeactivateActCtx
03 0012f024 781ff893 mfc80!AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2
04 0012f05c 7e418734 mfc80!AfxWndProcBase
05 0012f088 7e418816 user32!InternalCallWinProc(void)+0x28
06 0012f0f0 7e42a013 user32!UserCallWinProcCheckWow+0x150
07 0012f120 7e42a998 user32!CallWindowProcAorW+0x98
08 0012f140 0663163b user32!CallWindowProcA()
Every regular dll and exe in MFC world has a global application object. Application object contains a pointer to AFX_MODULE_STATE. Following are the steps that one can follow to check if correct module state is keyed into thread state or not.
1. From the call stack , see which is the MFC module on top of call stack and get corresponding app object.
0:000> x my!*theApp*
066682b8 my!theApp = class CMyApp
2. From the app object , we get the pointer to corresponding module state.
0:000> dt 066682b8
+0x01c m_pModuleState : 0x066683a0 AFX_MODULE_STATE
+0x050 m_pszAppName : 0x07c63ac0 "dllname"
0:000> dt 0x066683a0 AFX_MODULE_STATE
+0x010 m_lpszCurrentAppName : 0x07c63ac0 "dllname"
+0x09c m_hActCtx : 0x069eb3c0 Void
3. From the callstack get the pointer to AFX_THREAD_STATE. Check what does m_ pModuleState member points to.
0:000> dt 0x0015acf0 _AFX_THREAD_STATE
+0x000 __VFN_table : 0x781d80f4
+0x004 m_pModuleState : (null)
+0x008 m_pPrevModuleState : 0x0015b600 AFX_MODULE_STATE
Now for a currently executing thread , MFC maintains a AFX_THREAD_STATE which is keyed into Thread Local Storage. Thread state contains a pointer to AFX_MODULESTATE(m_ pModuleState ). This pointer should always point to the AFX_MODULE_STATE corresponding to MFC module that is currently executing ie which is at the top of call-stack right now.
So we can see that module state has not been set correctly and it results in crash here. The way to correct the error would be to make sure that you call AFX_MANAGE_STATE(AfxGetStaticModuleState) at all the entry points of dllname dll.