Recently I worked with one of my colleague on an interesting scenario in which the MFC application was crashing on startup. The next step was to run the application under WinDbg. After running the application under WinDbg we saw that we are actually access violating an address which was indeed a NULL pointer. The access violation was coming within mfc90u!AfxWinMain().
(ce8.1018): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffdf000 ecx=77224e10 edx=00000000 esi=00000000 edi=00000000
eip=5749b48a esp=001cf824 ebp=001cf840 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
5749b48a 8b10 mov edx,dword ptr [eax] ds:0023:00000000=????????
The obvious question was why AfxWinMain() would fail with an access violation? We analyzed the callstack to find the reason of the access violation (AV). Looking at the file source information we saw the application was failing in winmain.cpp @ line 37.
*** WARNING: Unable to verify checksum for Test.exe
# ChildEBP RetAddr Args to Child
00 001cf840 010e70ea 010d0000 00000000 001e20ec mfc90ud!AfxWinMain+0x7a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winmain.cpp @ 37]
01 001cf858 010e35fb 010d0000 00000000 001e20ec Test!wWinMain+0x1a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\appmodul.cpp @ 30]
02 001cf908 010e335f 001cf91c 773a4911 7ffdf000 Test!__tmainCRTStartup+0x28b [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 574]
03 001cf910 773a4911 7ffdf000 001cf95c 7721e4b6 Test!wWinMainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 399]
04 001cf91c 7721e4b6 7ffdf000 7748c3a5 00000000 kernel32!BaseThreadInitThunk+0xe
05 001cf95c 7721e489 010e178a 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x23
06 001cf974 00000000 010e178a 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b
Looking at the source code, “C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\winmain.cpp” we saw that the application was failing while accessing pThread. Since pThread was NULL we were getting AV. Now if you carefully see the source code, pThread is being returned from AfxGetThread().
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AV on below line
Above findings were confirmed by displaying the value of pThread using display type command (dt) in WinDbg. CWinThread pointer was in fact NULL.
0:000> dt pThread
Local var @ 0x1cf838 Type CWinThread*
The question was why AfxGetThread() returns NULL? Especially when it’s called from mfc90u!AfxWinMain() where we do not have direct control, unless we override AfxWinMain(). Looking at the source code of AfxGetThread(), it was clear that pThread was coming from the module thread state, AFX_MODULE_THREAD_STATE. For the ones who are wondering what is it? Have a look at afxstat_.h. below lines show where exactly pThread pointer is assigned.
“C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\thrdcore.cpp” @ line 138
CWinThread* AFXAPI AfxGetThread()
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
Now when and where the CWinThread pointer is getting assigned for the module thread state? A further search in the MFC source reveals that the CWinThread pointer is getting assigned from CWinApp constructor. See below line.
“C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\appcore.cpp” @ 381
// below code correctly sets CWinThread with this pointer
pThreadState->m_pCurrentWinThread = this;
Wow! Does that mean the constructor was never called? Yes, the crux of the problem was that someone had accidently commented the global CWinApp constructor. As a result the constructor never got called. Compiler never complained about it being commented, and why should it? Since CWinApp was not correctly set, the application crashed by throwing an AV on pThread. So, as a rule of thumb you should always create the global CWinApp object to make sure it's constructor gets called and the AFX_MODULE_STATE structure is correctly setup. If you see your code works without the correct initialization, probably you are not using enough MFC in your code! This is the reason behind AfxGetThread() returning NULL in AfxWinMain().
- Gaurav Patole,
Technical Lead, Developer Support VC++/C# and Robotics