Introduction
Most MFC/VC++ programmers generate their projects using App Wizard and are
quite happy with that. Once in a while, you have a programmer who will ask you,
what happened to WinMain
and he is normally satisfied with the answer
given that WinMain
is hidden within the MFC libraries. In this article
I'll try and explain the life-cycle of a typical MFC program. Before I do that
I'd like to introduce you to the smallest MFC program you can write that will
show a window on screen, other than by using a MessageBox
.
Smallest MFC window-program
class CNWinApp : public CWinApp
{
public:
BOOL InitInstance();
};
#include <afxwin.h>
#include "NWinApp.h"
CNWinApp app;
BOOL CNWinApp::InitInstance()
{
CFrameWnd *pnframe=new CFrameWnd;
m_pMainWnd=pnframe;
pnframe->Create(0,"Buster");
pnframe->ShowWindow(SW_SHOW);
return TRUE;
}
So, what happened to good old WinMain?
When you run your program the kernel first calls this function, WinMainCRTStartup
.
WinMainCRTStartup
first initializes the CRT
routines. Then it parses the command line and removes the filename portion and
calls WinMain
passing the parsed command line as lpszCommandLine. But
then where is WinMain
? :-) It is defined in appmodul.cpp which you can
find in your MFC\SRC directory. Here is how the function is implemented.
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
As you will observe, WinMain
simply calls AfxWinMain
.
AfxWinMain
is defined in winmain.cpp which you will find under your
MFC\SRC directory. I'll list the function below exactly as it is defined.
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
As you can see the functions AfxGetThread
and AfxGetApp
are
used to get pointers to the CWinApp
derived global object and the current
thread. If you are surprised as to how the global CWinApp
derived object already
exists, relax, C++ programs will first create all global and static objects
before execution begins. All that happens much before AfxWinMain
gets
called. By the way, it must have been a slight shock to you to see a goto
in there, eh?
Now there might be those of you who might be wondering where AfxGetThread
and
AfxGetApp
got their information from. The answer
is simple. Take a look at the CWinApp
constructor in appcore.cpp. You'll find
the following two lines.
pThreadState->m_pCurrentWinThread = this;
and
pModuleState->m_pCurrentWinApp = this;
pThreadState
is a
AFX_MODULE_THREAD_STATE*
and
pModuleState
is a AFX_MODULE_STATE*
.
Thus when we create our global CNWinApp
object, it's constructor gets called
and the AFX_MODULE_STATE
structure is setup properly. Now AfxWinInit
is
called and this function initializes the MFC framework. Now there is a call to
InitApplication
[this is for backward compatibility with 16-bit
applications]. Now it calls the InitInstance
of the CWinApp
derived
object. And as you can see from our code listing above we have overridden InitInstance
and created a
CFrameWnd
object there. I'll repeat the code
snippet here so that you won't have to scroll upwards.
BOOL CNWinApp::InitInstance()
{
CFrameWnd *pnframe=new CFrameWnd;
m_pMainWnd=pnframe;
pnframe->Create(0,"Buster");
pnframe->ShowWindow(SW_SHOW);
return TRUE;
}
I have created my CFrameWnd
object on the heap, otherwise the moment
InitInstance
exits, the window will get destroyed. I have also set
m_pMainWnd
to point to my CFrameWnd
window. Once InitInstance
returns [if it returns false an error is assumed, so we return true
],
CWinApp::Run
is called. Within the Run
function is implemented our
default message loop. Run keeps getting and dispatching messages till it
receives a WM_QUIT
message. Once WM_QUIT
is received
Run
returns
and control returns to AfxWinMain
which performs clean-up and lastly
calls AfxWinTerm
which deletes all the global application structures that
were created.
Well, that's it. Pretty amusing to think that all this while you were writing
MFC applications and you never bothered to think about what was happening under
the hood.
DISCLAIMER
None of the information in this article is endorsed by me. I don't work for
Microsoft and all my assumptions are just that - assumptions. Some of the info I
have given might indeed be incorrect, though I'd say the probability for that is
rather low. In case I have erred I'll be delighted if someone can correct those
mistakes.