Documente Academic
Documente Profesional
Documente Cultură
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
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 ;
return TRUE ;
}
myframe.h
myframe( ) ;
void OnPaint( ) ;
DECLARE_MESSAGE_MAP( )
};
myframe.cpp
#include <afxwin.h>
#include "myframe.h"
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
On execution of this program the window shown in Figure 1-1 gets displayed.
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…
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( ).
goto InitFailure ;
}
InitFailure :
AfxWinTerm( ) ;
return nReturnCode ;
}
Before we trace how the control flows in WinMain( ), let’s look at some important structures used
by the framework.
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
(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 doneeven 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,
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.
BOOL myapp::InitInstance( )
{
myframe *p ;
p = new myframe ;
m_pMainWnd = p ;
p -> ShowWindow ( SW_SHOWNORMAL ) ;
return TRUE ;
}
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,
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
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.
Yes If InitInstance( ) No
returns TRUE
A B
Chapter 1: How VC++ Programs Work 11
class CFrameWnd
{
virtual void OnPaint( )
{
}
};
void OnPaint( )
{
}
};
BOOL InitInstance( )
{
myframe *p ;
p = new myframe ;
m_pMainWnd = p ;
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.
&CFrameWnd::OnPaint &myframe::OnPaint
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,
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 ( ) ;
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:
(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
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( )
No Yes
Foun
Return
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 ;
};
class CFrameWnd
{
public :
Create( )
{
CREATESTRUCT cs ;
myframe( )
{
Create( ) ;
}
main( )
{
myframe *p ;
p = new myframe ; // allocates memory, calls constructor
}
Chapter 1: How VC++ Programs Work 19
Important Stuff
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.
(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.
(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.
(a) Why has InitApplication( ) lost its importance in the Win32 environment?
(d) Why is it necessary to create the frame window object on the heap?
(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.