You are on page 1of 20

How

VC++
Programs
Work

V
C++ programs are either generated through AppWizard and ClassWizard or are hand coded
by the programmer. It is often found that programmers are able to build reasonably
complicated applications without a clear understanding of how the program works.
Similarly, many programmers are able to tackle various Windows messages using the message
maps. However, when it comes to knowing how a message gets transformed into a call to a
message handler there is utter confusion. We think that it is imperative to understand the working
of the VC++ program and the message maps before we can venture into advanced VC++ topics.
Towards this end we would write a simple program that displays a message in a window. Then we
would examine in detail the working of this program. Here is the program…

Program 1
myapp.h

class myapp : public CWinApp


{
public :

1
2 VC++, COM and Beyond

BOOL InitInstance( ) ;
};

myapp.cpp

#include <afxwin.h>
#include "myframe.h"
#include "myapp.h"

myapp a ;

BOOL myapp::InitInstance( )
{
myframe *p ;
p = new myframe ;
m_pMainWnd = p ;

p -> ShowWindow ( SW_SHOWNORMAL ) ;

return TRUE ;
}

myframe.h

class myframe : public CFrameWnd


{
public :

myframe( ) ;
void OnPaint( ) ;

DECLARE_MESSAGE_MAP( )
};

myframe.cpp

#include <afxwin.h>
#include "myframe.h"

BEGIN_MESSAGE_MAP ( myframe, CFrameWnd )


ON_WM_PAINT( )
END_MESSAGE_MAP( )

myframe::myframe( )
{
Create ( 0, "Draw" ) ;
Chapter 1: How VC++ Programs Work 3

void myframe::OnPaint( )
{
CPaintDC d ( this ) ;
d.TextOut ( 50, 50, "Hello", 5 ) ;
}

Important Stuff

Is it necessary to keep different classes in separate


files?

We are not bound by any rule to do so. However, we are


bound by tradition. Among C++ programmers it is a
tradition to write the class declarations in .h files and
class implementations in .cpp files. We too would follow
this strategy throughout this book. For small programs
this might appear to be an overkill. But in large programs
containing numerous classes it is worthwhile to give
each class its own .h and .cpp files. The same convention
has been followed while storing the files on the CD
accompanying this book.

Once we create the .h and .cpp files we must include the


.cpp files in the current project using the ‘Project | Add
to Project | Files’ menu option.

On execution of this program the window shown in Figure 1-1 gets displayed.

Figure 1-1. A window with a message.


This program has two principal components:
4 VC++, COM and Beyond

(a) An application object (object of myapp class in our program)


(b) A window object (object of myframe class in our program)
Of these, the application object represents the application itself, whereas, the window object
represents the application’s window. The primary duty of an application object is to create a
window. In our program this has been done in the InitInstance( ) function. The primary job of the
window object is to process the messages that the window receives. This processing is done
through various handler functions. One such handler is the OnPaint( ) function in our program.
The application object and the window object can do little by themselves. The two objects work
together in tandem to produce a working Windows application. Our program only shows the
application and the frame window class. Hence you have to take it on faith that all the other
requirements for the program to work are being fulfilled. You must be wondering where exactly
do the windows classes get registered, when does the message loop get started, where is
WinMain( ), etc. All these activities are happening inside the program (otherwise, it would not
run). To understand how the application framework is making these things happen, we must take
a peek at what is happening under the hood. So let’s get started…

Application Framework And MFC


Microsoft Foundation Classes (MFC) provides an Application Framework specifically tailored
for creating applications for Microsoft’s Windows operating system. To create C++ based object-
oriented tools for Windows applications, developers at Microsoft established the AFX group in
1989. AF didn’t sound very catchy, so the letter X was thrown in. The X in AFX doesn’t really
mean anything. The first prototype of application framework that the AFX group came out with,
turned out to be completely unrelated to Windows. Hence this prototype was scrapped and a new
design goal was set up. This time the focus was on creating a simple yet elegant framework for
Windows developers using C++. The priority was to allow developers to utilize their existing
knowledge of Windows and develop real world applications using C++ and object oriented
techniques. In 1992, Microsoft released MFC version 1.2 containing around 60 C++ classes for
Windows application development. There were also general-purpose classes for time, string,
collections, files, persistent storage, memory management, exceptions and diagnostics. To
simplify Windows development, the AFX team grouped the AFX functions into logical units.
While doing this they took advantage of C++ classes and inheritance to make development easier.
For example, all API functions related to device context were gathered in a CDC class. Similarly,
all functions related to a window were pooled into a CWnd class. However, instead of a whole
new abstraction for Windows development, MFC 1.0 turned out to be little more than a thin
wrapper around the Windows API.

Spurred by the acclaim received by MFC 1.0 and the suggestions received from developers, the
AFX group added two features to MFC 2.0:
(a) High-level architecture support
(b) Pre-built components
This simplified Windows development and now the developers could easily build applications
that contained popular features like toolbars, status bars, print-preview and context-sensitive help.
The MFC architecture now provided hooks for the developers to customize and extend the
generic application. However, MFC 2.0 didn’t force the user to use the high-level abstraction.
Chapter 1: How VC++ Programs Work 5

The developers could still access the framework at lower level. If required, the developers could
access Windows API directly. MFC could hide away the messy details of Windows programming
such as registering window classes, window and dialog procedures and message routing. The
primary feature of MFC 2.0 was the new application architecture and the high-level abstraction.
The application architecture classes provided standard implementations of common Windows
features, such as documents and views, printing and command processing. The high-level
abstractions provided a set of pre-built components for common user interface elements, such as
toolbars and status bars. MFC 2.5 was released along with Microsoft Visual C++ 1.5. In this
version of MFC, support was provided for OLE and ODBC. This was followed by MFC 3.0,
which was released in 1994. This version made a major improvement in thread safety. Messaging
Application Interface (MAPI) and WinSock support was added in version 3.1 in 1995. In late
1995, MFC 4.0 was released along with VC++ 4.0. This version greatly enhanced the
development environment with major focus on reusability of code. As of this writing we are using
MFC 6.0. So much for the history of MFC. Let us now get back to our program from the last
section.

What, No WinMain( )?
The way a C++ under DOS/Unix program begins its execution with main( ), similarly a C++
under Windows program begins its execution from WinMain( ). However, surprisingly our
program doesn’t contain WinMain( ). While writing a Visual C++ program we are not required to
write WinMain( ). It is linked into our application by the VC++ compiler. You will find WinMain(
) function in the file APPMODULE.CPP. Here is how it looks like…

_tWinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow )
{
AfxWinMain ( hInstance, hPrevInstance, lpCmdLine, nCmdShow ) ;
}

As you can see from the code, WinMain( ) delegates the processing to a function called
AfxWinMain( ). This function is present in WINMAIN.CPP. Here is the pseudo-code of
AfxWinMain( ).

int AFXAPI AfxWinMain ( HINSTANCE hInstance,


HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow )
{
int nReturnCode = -1 ;

CWinApp* pApp = AfxGetApp( ) ;

// AFX internal initialization


AfxWinInit ( hInstance, hPrevInstance, lpCmdLine, nCmdShow ) ;

// App global initializations (rare)


6 VC++, COM and Beyond

pApp -> InitApplication( ) ;

// Perform specific initializations


if ( !pApp -> InitInstance( ) )
{
if ( pApp -> m_pMainWnd != NULL )
{
pApp -> m_pMainWnd -> DestoryWindow( ) ;
}
nReturncode = pApp -> ExitInstance( ) ;

goto InitFailure ;
}

nRetturnCode = pApp -> Run( ) ;

InitFailure :
AfxWinTerm( ) ;

return nReturnCode ;
}

Before we trace how the control flows in WinMain( ), let’s look at some important structures used
by the framework.

MFC State Information


It’s often useful for an application to maintain certain information throughout the life of the
program. MFC too maintains a great deal of information internally including main window
handles, resources, instance handle etc. Most of the information is stored in a structure called
AFX_MODULE_STATE. There is one important element in this structure—a pointer to a
CWinApp object (CWinApp *m_pCurrentWinApp).

We would not look at other elements of this structure. However, now you know
AFX_MODULE_STATE is a place to look if you need access to some bit-state information about
your application. Some of these members are even accessible through functions like
AfxGetInstanceHandle( ) and AfxGetApp( ). Warning! The structure within
AFX_MODULE_STATE may change in future. Hence, you should exercise caution while using
these structures specially if you are assigning values to the fields within them. Let’s now see how
this state information is utilized during execution of WinMain( ).

Back To WinMain( )
Let’s now take a closer look at AfxWinMain( ). Right at the beginning it calls a function,
AfxGetApp( ). This function returns the address of the global application object (in our program
we have called this object a). This address is assigned to a CWinApp pointer trough the statement,
Chapter 1: How VC++ Programs Work 7

CWinApp *pApp = AfxGetApp( ) ;

This leads to two questions:

(a) How can we get the address of the application object when it has not even been created?

(b) From where does the AfxGetApp( ) function gets to know the address of the application
object?

The answer to the first question is simple. C++ programs construct their global objects before
anything else is doneeven before WinMain( ) is called.

Now the answer to the second question. When we build the object a through the statement,

myapp a ;

the constructor of myapp gets called. This in turn calls the base class’s (CWinApp’s) constructor.
In this constructor if we use this it would contain the address of the global application object.
This address is stored by the constructor in AFX_MODULE_STATE’s data member,
m_pCurrentWinApp. You may recall that the type of m_pCurrentWinApp is CWinApp *. Thus,
what the constructor has managed to do is—set up a pointer to base class’s object with the
address of the derived class object. When we call AfxGetApp( ) it retrieves the address of the
global application object from AFX_ MODULE_STATE’s m_pCurrentWinApp data member.

AfxWinInit( )
Once the global application object’s address is obtained, AfxWinMain( ) proceeds to call
AfxWinInit( ) to initialize the framework. This function takes the same four parameters—the
current instance handle, the previous instance handle, the command-line parameters and the show
command. AfxWinInit( ) sets these parameters into the member variables of the CWinApp class.

Important Stuff

A function that begins with the word Afx does not belong
to any MFC class. Strangely Afx stands for Application
Framework.

The pointer retrieved using AfxGetApp( ) is now used by AfxWinMain( ) to call the
CWinApp::InitApplication( ) function.
8 VC++, COM and Beyond

InitApplication( )
Under 16-bit Windows there was a difference between the first instance of the application and the
subsequent ones (which could be determined by examining hPrevInstance). Hence under 16-bit
Windows, initializations that were necessary for the entire application used to get performed in
InitApplication( ), whereas initializations which were necessary for every new instance of the
application used to get performed in InitInstance( ). However, when Windows moved to 32 bits
the difference between various instances of the same application vanished. Hence in 32-bit
Windows programs all initializations take place in InitInstance( ).

InitInstance( )
When a program begins to execute it is necessary to perform certain initializations. These are
usually done in the InitInstance( ) function. The MFC class CWinApp, as well as its derived
version, myapp, contains the InitInstance( ) function. This function has been defined as virtual in
CWinApp class. It is called from AfxWinMain( ) through the statement,

pApp -> InitInstance( )

Since pApp is a pointer to the base class object and it holds the address of the derived class
object, the call transfers to the derived class implementation of the InitInstance( ) function.

In fact, InitInstance( ) of CWinApp simply returns TRUE. Hence it is necessary for us to override
it in the myapp class. Otherwise, the application would simply terminate without even showing a
window on the screen.

Let us now understand the stuff inside the myapp::InitInstance( ) function.

The InitInstance( ) Stuff


The myapp::InitInstance( ) function is reproduced below:

BOOL myapp::InitInstance( )
{
myframe *p ;
p = new myframe ;
m_pMainWnd = p ;
p -> ShowWindow ( SW_SHOWNORMAL ) ;
return TRUE ;
}

When the statement,

p = new myframe ;
Chapter 1: How VC++ Programs Work 9

is executed, space is allocated for a myframe object on the heap and the myframe class’s
constructor is called. In the constructor we have created a frame window by calling
CFrameWnd::Create( ). The first parameter passed to Create( ) signifies the window class name.
By passing a value 0 for it we are indicating that a default window class be used.The second
parameter passed to it is the title of the window. CFrameWnd::Create( ) in turn calls the
CreateWindowEx( ) API function.

But then did we not learn in C/SDK programming that before a window can be created a window
class must be registered with the OS. This job is done by the PreCreateWindow( ) function which
is called by CFrameWnd::Create( ) before calling the ::CreateWind-owEx( ) function.

Once the window is created and the control comes back from the myframe constructor we display
the window on the screen by calling CFrameWnd::ShowWindow( ) function. Just before the
control goes out of InitInstance( ) we have initialized m_pMainWnd to the myframe object’s
address stored in p. Here m_pMainWnd is a public data member of the CWinApp class. It is
necessary to initialize m_pMainWnd because other functions that may want to access the frame
window object would be able to do so using m_pMainWnd. m_pMainWnd can be accessed
through the expression,

AfxGetApp( ) -> m_pMainWnd ;

Now that the window is up and kicking it is time to interact with it. This is achieved through the
CWinApp::Run( ) function.

Important Stuff

Is it necessary to create the myframe object on the


heap?

Yes. Had it been created using the statement,


myframe p ;
we would have still been able to store the address of the
myframe object in m_pMainWnd through the statement,
m_pMainWnd =&p ;
However, this would be suicidal because when the
control goes out of InitInstance( ) the object would die;
whereas, m_pMainWnd would continue to hold the
address of the location where the object was present. A
10 VC++, COM and Beyond

classic case of a dangling pointer.

MFC’s Message Pump


The GetMessage( )..DispatchMessage( ) loop that we use in C/SDK programming is
implemented in MFC by CWinApp::Run( ) function.

However, MFC handles message routing a bit differently. Instead of a huge switch statement to
filter out a window’s messages, MFC uses Message Maps. It is difficult to understand how
message maps work. We would soon see their internal working.

GetMessage( )..DispatchMessage( ) loop implemented by the Run( ) function gets terminated


when a WM_QUIT is picked up from the message queue.

ExitInstance( ) And AfxWinTerm( )


The way in InitInstance( ) the instance-initializations are done, similarly, ExitInstance( ) is the
place to perform shutdown
Global and
application cleanup.
object is createdThe ExitInstance( ) function is called only if
InitInstance( ) returns a FALSE. On the other hand, AfxWinTerm( ) function is called every time
CWinApp’s
the application is about constructor
to terminate. is called
This function unregisters the window classes.
m_pCurrentWinApp
The ExitInstance( ) function has is assigned the addressas virtual in CWinApp. Hence if you want to
been declared
of the global application object
perform some specific clean up activity you can do so by overriding it in the myapp class.
Execution begins with WinMain( )
The essence of the working of the VC++ program that we discussed in the last few pages is
WinMain( ) calls AfxWinMain( )
caught in Figure 1-2.
AfxGetApp( ) gets the address of the global
application object from m_pCurrentWinApp

AfxWinInit( ) copies hInstance, nCmdShow, etc.


to the data members of the application object

AfxWinMain( ) calls CWinApp::InitApplication( )


AfxWinMain( ) calls InitInstance( ) of myapp class

myframe object is created

Constructor of myframe object gets called

From the constructor CFrameWnd::Create( ) gets called


CFrameWnd::Create( ) calls PreCreateWindow( )

CFrameWnd::Create( ) calls ::CreateWindowEx( ) API function


Control returns from the constructor

m_pMainWnd is intialised to myframe object’s address

CFrameWnd::ShowWindow( ) displays the window

Yes If InitInstance( ) No
returns TRUE

A B
Chapter 1: How VC++ Programs Work 11

AfxWinMain( ) calls Run( ) function


GetMessage( )..DispatchMessage( ) loop is executed
This loop terminates when GetMessage( ) retrieves WM_QUIT

Control returns back from Run( ) to AfxWinMain( )


AfxWinMain( ) calls AfxWinTerm( )
AfxWinMain( ) and WinMain( ) terminates

AfxWinMain( ) calls ExitInstance( )

AfxWinMain( ) calls AfxWinTerm( )


AfxWinMain( ) and WinMain( ) terminates

Figure 1-2. Working of a VC++ program.


That brings us to the end of a long and convoluted journey that began with the creation of global
application object. Let us now move to another intriguing concept called Message Maps.

Message Maps Under The Hood


Be it a simple dialog based application or be it a Doc/View application, message maps are part
and parcel of any MFC program. Using message maps in a program is one of the simplest things
to do in MFC programming. However, understanding how message maps work under the hood is
a totally different cup of tea. It is important for a MFC power programmer to understand how a
WM_PAINT message gets converted into a call to the OnPaint( ) handler. Before we understand
this it would be worthwhile to find out why should one use the message maps at all.

Why Message Maps


Instead of using message maps we can think of using virtual functions to convert a message into a
call to an appropriate handler. To understand how virtual functions can be used, look at the
following code segment:
12 VC++, COM and Beyond

class CFrameWnd
{
virtual void OnPaint( )
{
}
};

class myframe : public CFrameWnd


{
myframe( )
{
Create ( 0, "Draw" ) ;
}

void OnPaint( )
{
}
};

class myapp : public CWinApp


{
public :

BOOL InitInstance( )
{
myframe *p ;
p = new myframe ;
m_pMainWnd = p ;

p -> ShowWindow ( SW_SHOWMAXIMIZED ) ;

return TRUE ;
}
};

myapp a ;

The compiler creates a table called VTABLE for each class that contains virtual functions and for
the classes derived from it. The compiler places the addresses of the virtual functions for the
particular class in the VTABLE. It you don’t redefine a function that was declared virtual in the
base class, the compiler uses the address of the base class version in the derived class’s VTABLE.
When objects of base class or derived class are created the compiler secretly places a pointer
called vpointer (abbreviated as vptr) in the object. This pointer points to the class’s VTABLE.

The vptr is placed at the beginning of the object and is initialized to point to the starting address
of its class’s VTABLE. This initialization is done in the constructor. All of this—setting up the
VTABLE for each class, initializing the vptr, inserting the code for the virtual function call—
Chapter 1: How VC++ Programs Work 13

happens automatically. The VTABLES for the two classes CFrameWnd and myframe are shown
in Figure 1-3.

VTABLE of CFrameWnd VTABLE of myframe

&CFrameWnd::OnPaint &myframe::OnPaint

Figure 1-3. Virtual Tables.

In the 16-bit version of MFC, the MFC window classes were registered with AfxWndProc( ) as
the message handler. In this function all the messages used to get handled. Since the 32-bit
version of MFC, instead of AfxWndProc( ) the MFC window classes are registered with
DefWindowProc( ) as a window procedure. Even now all messages are ultimately routed to
AfxWndProc( ) from where they are dispatched to various CWnd-derived objects. Why all
messages still end up in AfxWndProc( ) has something to do with MFCs message hook
mechanism. Exploring this mechanism is beyond the scope of this book.

Had a virtual function been used for converting a message into a call to the appropriate handler,
the window procedure would have looked like this.

CFrameWnd *p ;
p = AfxGetApp( ) -> m_pMainWnd ;
p -> OnPaint( ) ;

Here, AfxGetApp( ) fetches the address of the global application object (created from class
myapp). m_pMainWnd is a public data member of the CWinApp class. This data member contains
the address of the myframe object assuming that the window is built using the myframe class. In
the statement,

p = AfxGetApp( ) -> m_pMainWnd ;

we are storing the address of a derived class object in a pointer to the base class object. When we
call the OnPaint( ) handler through the statement p -> OnPaint( ) it must be determined whether
the OnPaint( ) handler of CFrameWnd class or one in the myframe class should get called. To
determine this, contents of p are used. p contains address of the myframe object. First two bytes
starting at this address contain the vptr. The vptr points to the VTABLE belonging to myframe
class. From the VTABLE the address of myframe::OnPaint( ) is obtained and the control is
transferred to this address. Thus, it is the derived class’s (myframe’s) OnPaint( ) which gets
called. Had OnPaint( ) not been declared as virtual in CFrameWnd class then through

p -> OnPaint ( ) ;

the CFrameWnd::OnPaint( ) would have been called.


14 VC++, COM and Beyond

This in effect means that for every possible message that CFrameWnd object is going to receive
there must be a virtual function in it. Otherwise we would never be able to override it in a class
derived from CFrameWnd. This would lead to a huge virtual table. Message maps is MFC’s way
of avoiding these lengthy virtual tables.

Another difficulty with virtual tables arises in case of user-defined messages. Suppose we write a
handler, say fun( ) in the myframe class. And now to call it if we say p -> fun( ), an error will
result. This is because fun( ) has not been defined as virtual in the CFrameWnd class. The
difficulty is we do not have the source code of the CFrameWnd class hence cannot make the
virtual declaration. In effect it means that we cannot tackle user-defined messages through the
mechanism of virtual functions.

Let us now understand the mechanism of message maps. Any class derived from CCmdTarget can
contain a message map. What MFC does internally to implement a message map is hidden behind
some rather complex macros. To understand the message map mechanism consider the following
small code snippet:

class myframe : public CFrameWnd


{
void OnPaint( )
{
}
DECLARE_MESSAGE_MAP( )
};
BEGIN_MESSAGE_MAP( )
ON_WM_PAINT( )
END_MESSAGE_MAP( )

The macros DECLARE_MESSAGE_MAP( ), BEGIN_MESSA-GE_MAP( ) and


END_MESSAGE_MAP( ) have been defined in the file ‘afxwin.h’. During preprocessing the
DECLARE_MESSA-GE_MAP( ) macro adds three members to the class declaration:

(a) A private array of structures called _messageEntries[ ]. Each element of this array is a
structure called AFX_MSGMAP_ENTRY. In addition to other elements, this structure
contains a message id and a pointer to a function that should get called when a message with
this id arrives.

(b) A structure called messageMap. This structure contains a pointer to class’s _messageEntries[
] array and a pointer to base class’s messageMap structure.

(c) A virtual function called GetMessageMap( ) that returns messageMap structure's address.

Note that the _messageEntries[ ] array and the messageMap structure are static members of the
class. This means there is one _messageEntries[ ] array and one messageMap structure for all
objects of a class. The BEGIN_MESSAGE_MAP( ) macro contains the implementation of the
GetMessageMap( ) function and the code to initialize the messageMap structure. The macros that
appear between BEGIN_MESSAGE_MAP( ) and END_ME-SSAGE_MAP( ) fill in the
Chapter 1: How VC++ Programs Work 15

_messageEntries[ ] array. Finally, END_MESSAGE_MAP( ) marks the end of the


_messageEntr-ies[ ] array with a NULL entry. Figure on the following page shows all these
expansions at one glance.

Ptr to myframe’s
_messageEntries[ ] array
Ptr to CWnd’s messageMap
structure

Initializes messageMap
structure
Fills
_messageEntries[ ]

Terminates
_messageEntries[ ] array
class myframe : public CWnd

{
void OnPaint( )
{
}
DECLARE_MESSAGE_MAP( )
};

BEGIN_MESSAGE_MAP( )
ON_WM_PAINT( )
END_MESSAGE_MAP( )

With this infrastructure in place let us now understand how a message passed to the application
window gets converted into a call to the appropriate message handler.

Suppose a message, say WM_PAINT, gets posted in the message queue. This message would be
retrieved by the GetMessage( ) function and would be dispatched to the AfxWndProc( ) function
by the DispatchMessage( ) function. Inside the AfxWndProc( ) function, the framework retrieves
the C++ object associated with the window handle using CWnd::FromHandlePermanent( )
function. AfxWndProc( ) then calls that object's (the one retrieved using
FromHandlePermanent( )) WindowProc( ) function. The WindowProc( ) function gets the address
of myframe's messageMap structure. As discussed earlier, this structure contains the address of
_messageEntries[ ] array. The id of the message picked up from the message queue is now
searched in this array. If the entry is found then the handler corresponding to it is immediately
16 VC++, COM and Beyond

called (as you may recollect, the _messageEntries[ ] contains message ids and the names of their
handlers). If the message id is not found in the _messageEntries[ ] array of the myframe class
then the _messageEntries[ ] array of the myframe’s base class is searched. But how can we reach
the _messageEntries[ ] array of base class? This is achieved using the pointer to the base class’s
messageMap structure, which is stored in myframe’s messageMap structure. If the base class
doesn't have the handler for the message the framework ascends another level and consults base
class’s base class, systematically working its way up the inheritance chain until it finds the
message handler or it has reached the father of all classes. If it still cannot find the message
handler then the framework passes the message to Windows for default processing. This entire
process is shown schematically in the following figure.

GetMessage( ) … DespatchMessage( )

AfxWndProc( )

AFX finds C++ object associated with window


handle using FromHandlePermanent( )

AfxWndProc( ) calls object’s WindowProc( )


WindowProc( )
WindowProc( ) calls myframe’s GetMessageMap( )

Using messageMap search _messageEntries[ ] array

No Yes
Foun

Call Default Call function in myframe


Window Procedure or CFrameWnd

Return

Figure 1-4. Working of message maps.


The MFC's message mapping mechanism amounts to an efficient way of connecting messages to
message handlers without using virtual functions. In contrast to virtual tables the amount of
memory used by message maps is proportional to the number of message entries it contains. In
short, message maps is the way of connecting messages to message handlers.

Are Virtual Functions Still Required


We saw in the last section that to avoid lengthy virtual tables for each class in a class hierarchy,
MFC uses Message Maps. However, MFC still makes use of the virtual function mechanism.
When the framework wants that the programmer should get a chance to implement his ideas
before framework does some specific job, it uses the virtual function mechanism.
Chapter 1: How VC++ Programs Work 17

For example, to create a window we call the function CFrame-Wnd::Create( ). This function in
turn calls PreCreateWindow( ) followed by ::CreateWindowEx( ). In CFrameWnd::Create( ) a
CREATESTRUCT structure is created and its elements are set up. If we want to modify some
elements of the CREATESTRUCT structure (window style, for example) we can provide our
implementation of PreCreateWindow( ). Now there are two PreCreateWindow( )’s—one in
CFrameWnd and another in our CFrameWnd-derived class. Since CFrameWnd::PreCreateWin-
dow( ) has been declared as virtual, it is our PreCreateWindow( ) that gets called. If we so desire
we can call the base class implementation of PreCreateWindow( ) from our implementation.
When the control reaches our implementation of PreCreateWindow( ) we can change the window
style by manipulating one of the elements of the CREATESTRUCT structure. This modified
structure is lastly passed to ::CreateWindowEx( ) to create the window.

A doubt may come to your mind—why can’t the same thing be done using message maps instead
of virtual functions? Since there is no Windows message like WM_PRECREATEWINDOW, the
calls to these functions cannot be managed through message maps. Therefore, the only alternative
to implement calls to them is through the virtual function mechanism.

In Chapter 2 you would see a similar mechanism at work when CView::OnPaint( ) calls a
function OnDraw( ) which is defined as virtual in the CView class. Since a CView-derived class
called myview also contains the implementation of OnDraw( ) it is the myview::OnDraw( ) that
gets called.

To help you fix these ideas in your mind I am giving below a program that implements this
concept. To keep things simple the program has been developed as a console application. It has
been suitably commented. Understand it well because this concept is used by the framework at
several places.

#include <iostream.h>
#include <string.h>

struct CREATESTRUCT
{
char windowtitle[30] ;
int width ;
int height ;
};

void CreateWindowEx ( CREATESTRUCT cs )


{
cout << cs.windowtitle << endl ;
cout << cs.width << endl ;
cout << cs.height << endl ;
}

class CFrameWnd
{
public :

Controls returns from


18 VC++, COM and Beyond

Create( )
{
CREATESTRUCT cs ;

strcpy ( cs.windowtitle, "Hello" ) ;


cs.width = 100 ;
cs.height = 80 ;

PreCreateWindow ( cs ) ; // calls myframe::PreCreateWindow( )


CreateWindowEx ( cs ) ; // calls the global CreateWindowEx( )
}

virtual void PreCreateWindow ( CREATESTRUCT &cs )


{
}
};

class myframe : public CFrameWnd


{
public :

myframe( )
{
Create( ) ;
}

void PreCreateWindow ( CREATESTRUCT &cs )


{
strcpy ( cs.windowtitle, "Hi" ) ;
}
};

main( )
{
myframe *p ;
p = new myframe ; // allocates memory, calls constructor
}
Chapter 1: How VC++ Programs Work 19

Important Stuff

What are the series of actions that take place when I


try to close a window?

When we try to close an application by clicking on the


close button, a WM_CLOSE message is sent to our
application. If we do not handle this message then it is
passed on to the default window procedure. The default
window procedure destroys the window by calling the
::DestroyWindow( ) API function. This function places a
WM_DESTROY message in the message queue of our
application. If we do not handle this message once again
the default window procedure gets a shot at it. Following
WM_DESTROY, is another message called
WM_NCDESTROY. In reaction to this message a handler
OnNcDestroy( ) gets called. This handler calls another
function—PostNcDestroy( ). It is in this function that the
::PostQuitMessage( ) function is called. This function
places a message WM_QUIT in the message queue. When
this message is retrieved, the message loop is terminated.
This ultimately leads to termination of the application.
This procedure is outlined in the figure shown below:

✖ WM_CLOSE DefWindowProc( ) DestroyWindow( )

WM_NCDESTROY OnDestroy( ) WM_DESTROY

OnNcDestroy( ) PostQuitMessage( ) WM_QUIT

GetMessage( ) returns 0

Exercise
[A] State True or False:

(a) Virtual functions permit calling of derived class functions using the base class pointer.

(b) There is common VTABLE for all the objects of the class.

(c) There is one vptr per object.


20 VC++, COM and Beyond

(d) Virtual functions permit functions from different classes to be executed from the same
function call.

(e) In a class hierarchy of several levels if you want a function at any level to be called through a
base class pointer then the function must be declared virtual in the base class.

(f) In principle, calls to message handlers for the messages that are received by an application
can be implemented using virtual functions.

(g) Message maps consume more memory than virtual tables.

(h) Message maps work faster than virtual tables.

(i) In 32-bit MFC, the registration of window classes is done in the function AfxWndProc( ).

(j) The WinMain( ) function linked into a VC++ program by the framework simply contains a
call to AfxWinMain( ) function.

[B] Answer the following:

(a) Why has InitApplication( ) lost its importance in the Win32 environment?

(b) What does the AFX_MODULE_STATE structure contain?

(c) Which mechanism is used to call the InitInstance( ) function?

(d) Why is it necessary to create the frame window object on the heap?

(e) Why is it necessary to initialize the m_pMainWnd data member of CWinApp?

(f) What does the DECLARE_MESSAGE_MAP( ) macro expands into? How would you see
this expanded code?

(g) Why is it that for some messages we are required to use the message map mechanism,
whereas, for some other we have to use the virtual function mechanism?

(c) Write a program which displays a message ‘Hello’ in the center of the window. Ensure that
the window does not have a border.

You might also like