MFC Under the Hood

Environment: Visual C++

Most of the time when you are creating a new MFC application,
you’ll start with the MFC App Wizard. The App Wizard generates a basic
skeleton application which you will flesh out into a full-fledged,
useful program.

Even the skeleton application you get from the App Wizard
contains a lot of very arcane looking code, and there’s a great deal
of hidden code as well. The purpose of this article is to demystify
some of that code. I’ll show you how to build a simple MFC application
without the App Wizard. By the time you are done, the mysterious
looking code that comes from the App Wizard will no longer be so
mysterious, and you’ll be better prepared to modify it to suit your
own purposes.

The hidden code first

Every 32 bit Windows application has two essential program elements:
WinMain and WndProc. Your program will have one
WinMain for the entire program, and one WndProc for each window in
the program. Although MFC creates these for you, you still need to
know a little about them.

WinMain is the function that starts your application. Once
your application is running, the Windows operating system will start
placing your application’s messages in a message queue. WinMain makes
three Windows API calls to get these messages from the operating
system and to process them. First it calls GetMessage to retrieve
a message. Then it calls TranslateMessage to perform any necessary
conversion of the message. Finally, WinMain calls DispatchMessage,
which tells the operating system to send the message to the appropriate
WndProc for handling.

Once WndProc receives a message, it looks through its message
handlers for instructions on what should be done with the message.
Your job as a Windows application programmer, it to write the handlers.

A Simple MFC Application: Less Than 20 Lines of Code

Next we’ll look at a simple, bare-bones MFC application.
MFC provides the WinMain and WndProc. We must provide an MFC-derived
application class and window management class.

Our application class will be derived from the MFC class CWinApp.
CWinApp provides all the member variables and functions to initialize,
start, run and close an application. CWinApp contains a pointer
called m_pMainWnd which will point to an object of our derived window
management class. Each MFC application has one and only one object of
derived directly from CWinApp. In the example below, that class is
called “CMyApp.”

Our window management class will be derived from CFrameWnd. CFrameWnd
has all the member variables and functions to create and manage windows.
Note that when you create an object of our derived windows class, you
have not created an actual window. The new object uses its Create()
function to create windows.

Here’s what happens when we start our program. You can follow along
in the code:



  1. WinMain runs this code: CMyApp app; This creates an object of type
    CMyApp named “app.” App will have all the member variables and functions of
    CWinApp which are needed to start, run and close our application.

  2. Then WinMain calls app’s InitInstance( ) function.
    InitInstance() creates a new CMyWnd object with m_pMainWnd =
    new CMyWnd;

  3. The CMyWnd constructor calls its Create( ) function, which
    creates an instance of the window, but does not display it.

  4. The app’s InitInstance() function then displays the window with
    m_pMainWnd-> ShowWindow(m_nCmdShow);

  5. WinMain calls the app’s Run( ) function, which dispatches
    messages to the rest of the application.

Here is the code. Try it – it works!

     #include <afxwin.h>

//derive my own window class from CFrameWnd
class CMyWin: public CFrameWnd
{
public:
CMyWin( );
DECLARE_MESSAGE_MAP( )
};

//define my window class’ constructor:
CMyWin::CMyWin( )
{
Create(0, “This Text Will Appear in the Title Bar”);
}

//derive my own application class from CWinApp
class CMyApp: public CWinApp
{
public:
virtual BOOL InitInstance( );
};

//define my application class’ InitInstance( )
BOOL CMyApp::InitInstance( )
{
m_pMainWnd = new CMyWin( );
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}

//here is my application’s message map
BEGIN_MESSAGE_MAP(CMyWin, CFrameWnd)
// any messages to be processed by
// CMyWin get listed here.

END_MESSAGE_MAP( )

//declare an instance of application
//class for WinMain to use.

CMyApp app;

Here’s what you get when you compile and run:


Your actual window will be bigger.

Single Document Interface Applications

The code above creates a simple, bare bones application. The code
you will find in a single document interface application is more
complicated, but still works along the same lines. You still have
an application class derived from CWinApp. You still have a window
management class derived from CFrameWnd. In this case, however, the
derived window management class is called CMainFrame.

Your derived application class still has an InitInstance() function,
but the function looks a lot more complicated. Among other things,
it contains something like this:

     CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CMyView));
AddDocTemplate(pDocTemplate);

Here, the application object creates a single document template pointer
and points it to a new single document template object. There are four
parameters passed to the CSingleDocTemplate constructor. The first is
an integer, the resource ID for the IDR_MAINFRAME resource. The next
three parameters pass class information for my document class, frame class
and view class. Then the pointer to this new CSingleDocTemplate is added
to the list of document templates maintained by the application object.
(In an SDI application, there’s only one template.)

IDR_MAINFRAME is a resource containing:


  1. The application icon.

  2. The application’s menu.

  3. The accelerator table that goes with the menu.

  4. A document string.

The document string contains up to seven pieces of information in substrings separated
by ‘\n’ characters:


  1. The title that appears in the frame window’s title bar.

  2. The title assigned to new documents. If omitted, the default is “Untitled.”

  3. A description of the document type in an MDI application. This substring isn’t used in an SDI application.

  4. A description of the document type followed by its default file name extension, e.g., “My Big Program(*.mbp).”

  5. The three letter extension for the document type, e.g., “.mbp”

  6. A name with no spaces that identifies the document type in the registry.

  7. A descriptive name of the document type, e.g., “My Big Program Document.”

Here’s a sample string:

"My Big Program\n\n\nMy Big Program(*.mbp)\n.mbp\nBigProgram\nMBP Document."

Conclusion

So there you have it. Now that you know a little more about what your wizard-generated
code is doing, you can go in and start modifying it. For example, if you want to change
the size and positioning of your application’s main window, you can modify the Create( )
function. Try substituting the following code for the Create( ) function listed above.
You’ll get a much smaller window positioned in the upper left corner of the screen.

    RECT x;
x.top = 30;
x.left = 30;
x.bottom = 300;
x.right = 300;
Create(NULL, “My New Window”, WS_OVERLAPPEDWINDOW, x);

There are a lot of settings and functions you can play with. You might
be the type who never changes the default code. Even so, the more you
know about what’s going on, the better your programs will be.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read