Freigeben über


Why AfxGetThread() returns NULL in AfxWinMain()?

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

mfc90ud!AfxWinMain+0x7a:

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.

0:000> kbn100

*** 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();

      // Snip

// AV on below line

      if (!pThread->InitInstance())

      {

            // Snip

}

// Snip

}

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*

(null)

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;

      return pThread;

}

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

CWinApp::CWinApp(LPCTSTR lpszAppName)

{

      // Snip

// below code correctly sets CWinThread with this pointer

      pThreadState->m_pCurrentWinThread = this;

     

// Snip

}

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

Comments

  • Anonymous
    July 01, 2009
    Thanks! That was really useful! Having the same problem. So now adding CMyApp theApp; as you found out. Let'seeeeeee...

  • Anonymous
    February 02, 2011
    Although this is a 2 years old article, I am now coming across this issue with a 12  yrs old working MFC (compiled under VS98/VC6) server application we use for customer support running a dedicated NT 4.0 server.  We recently updated our NT 4.0 machines and for this one server machine I have 2003 installed and I did a straight recompiled of the server under VS2010.  It worked fine as expected running as a DESKTOP   But during this revamp, we wanted to lock down the machines so I also wanted to make it into a NT service.  To do this, we use a class CWinApp wrapper called CServiceApp.  It is used other commercial product applications running as a GUI and/or NT service without fail. I have not tried recompiled the server app under VC6 with the CServiceAPP wrapper, but overall, I am seeing the same issue with a NULL pThread fault in WinMain.cpp.   The CServiceApp class is pretty sweet and it exports _tWinMain() to call CServerApp::OnWinMain() for NT Services or AfxWinMain() for GUI loads.  It also prepares a global class _AFX_TERM_APP_STATE. to the same AfxInitialize() as AppModul.cpp does.   We are using the same pragma init_seg(lib)  to make sure it does among the first thing done.  Basically CServerApp.cpp module has this pulled from the RTL source: ///////////////////////////////////////////////////////////////////////////// // export WinMain to force linkage to this module #ifdef _MAC extern "C" int PASCAL #else extern "C" int WINAPI #endif _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {    CServiceApp *app = (CServiceApp )AfxGetApp();    if (!app->OnWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) {        return 0;    }    // call shared/exported WinMain    return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } ///////////////////////////////////////////////////////////////////////////// // initialize app state such that it points to this module's core state BOOL AFXAPI AfxInitialize(BOOL bDLL, DWORD dwVersion) {    AFX_MODULE_STATE pModuleState = AfxGetModuleState();    pModuleState->m_bDLL = (BYTE)bDLL;    ASSERT(dwVersion <= _MFC_VER);    UNUSED(dwVersion);  // not used in release build #ifdef _AFXDLL    pModuleState->m_dwVersion = dwVersion; #endif #ifndef _MAC #ifdef _MBCS    // set correct multi-byte code-page for Win32 apps    if (!bDLL)        _setmbcp(_MB_CP_ANSI); #endif //_MBCS #endif //!_MAC    return TRUE; } class _AFX_TERM_APP_STATE { public:   _AFX_TERM_APP_STATE(); #ifndef _AFXDLL   ~_AFX_TERM_APP_STATE(); #endif }; _AFX_TERM_APP_STATE::_AFX_TERM_APP_STATE() {   // initialize this module as an application   AfxInitialize(FALSE, _MFC_VER); } #ifndef _AFXDLL _AFX_TERM_APP_STATE::~_AFX_TERM_APP_STATE() {   AfxTermLocalData(NULL, TRUE); } #endif // force initialization early #pragma warning(disable: 4073) // not 4074 #pragma init_seg(lib) _AFX_TERM_APP_STATE _afxTermAppState; ////////////////////////////////// And for our CMyWinApp, it is now made a child of CServiceApp instead of CWinApp.   In any case, when installed as a service,  _tWinMain is called ad CServiceApp::OnWinMain() is called is checks to see if its a NT Service and if so, starts the StartServiceCtrlDispatcher.  When the START event comes, the RTL AfxWinMain() is called and since AfxGetThread() is NULL there is a FAULT. I am about to restart tracking this down by first seeing it worked under a VC6 compile.   The only other clue I have is that there is a new thread started when the service  StartServiceCtrlDispatcher is started.  AfxGetThread is not NULL before the StartServiceCtrlDispatcher is started.