Yashwant Kanitker - Vc++, Com And Beyond

  • October 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Yashwant Kanitker - Vc++, Com And Beyond as PDF for free.

More details

  • Words: 5,666
  • Pages: 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 #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 #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 objectoriented 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 and cleanup. The ExitInstance( ) function is called only if Global application object is created InitInstance( ) returns a FALSE. On the other hand, AfxWinTerm( ) function is called every time CWinApp’s constructor is called the application is about to terminate. This function unregisters the window classes. m_pCurrentWinApp is assigned the addressas virtual in CWinApp. Hence if you want to The ExitInstance( ) function has 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 A

If InitInstance( ) returns TRUE

No B

Chapter 1: How VC++ Programs Work

11

A 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

B 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 Call Default Window Procedure

Yes

Foun

Call function in myframe 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::PreCreateWindow( ) 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 #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( )

WM_NCDESTROY OnNcDestroy( )

DestroyWindow( )

OnDestroy( ) PostQuitMessage( )

WM_DESTROY 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.

Related Documents

Vc
June 2020 10
Vc
November 2019 37
Vc
April 2020 14
Vc
October 2019 25