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 ucancode.net 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
//NWinApp.h
class CNWinApp : public CWinApp
{
public:
BOOL InitInstance();
};
//NWinApp.cpp
#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)
{
// call shared/exported WinMain
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.
Collapse
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
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
// Check for missing AfxLockTempMap calls
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.