Creating an MFC Application Without the AppWizard

This will describe how to create an MFC application without using the AppWizard. I do not recommend doing this always of course but I think this can be a useful alternative to using a dialog-based application. People often use dialog-based MFC applications when they do not want all the complications of the "MFC Document/View Architecture" yet dialogs have limitations. So in those situations that you want the simplicity of a dialog yet you want the conveniences of a real window you might want to use this solution. You can use the AppWizard to generate a SDI application and yank out and/or override parts you do not need so it is more a matter of personal preference.

There are some MFC experts that would say you are foolish to not use the AppWizard to generate a MFC project initially, as if MFC was designed to only be used in a project generated by the AppWizard. I learned MFC beginning with the first version when the AppWizard was not as powerful as it is now (as if it is really powerful now). I know that there was definitely MFC documentation showing how to make a simple MFC program with only about a dozen lines. It is relatively easy to create a small program that uses MFC without using the AppWizard; the only trick is that it is practically a necessity to have a resource file. Assuming we are not making an application that just has a modal dialog box, then we must have menu and string table resources. Initially our small applications will only have a plain CFrameWnd window without any overrides or handlers or anything like that but I will show how to add a view.

The Project

To create the project:

When the project has been created, in the Project Settings, on the "General" tab, change it from saying not to use MFC to saying to use MFC (for all configurations). You can choose to use MFC in either a static or dynamic library.

The Resource File

The menu can have anything in it; the requirement is that the resource Id for the menu must be IDR_MAINFRAME. The string table can have only one string in it and that string should have a resource Id also of IDR_MAINFRAME. The string is used by CWinApp for the application's title, such as in message boxes.

To create a Resource Script file, use the "File | New" menu and select the tab for "Files". Create a "Resource Script" file. Then:

  1. Insert a String Table into the Resource Script.
  2. Add a string to the String Table with an ID of IDR_MAINFRAME; it's value must be 128 but that value should happen automatically. Set the string to whatever you want for the application title.
  3. Add a menu to the project. Give the menu an id of IDR_MAINFRAME. The menu can consist of a single non-popup item with an id of ID_APP_EXIT and caption of "E&xit".

The Program

Then add to the project a file (named TinyMFC.cpp or whatever is appropriate for you) containing:

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

class CTinyMFCApp : public CWinApp {
	virtual BOOL InitInstance();
};

CTinyMFCApp TinyMFCApp;

BOOL CTinyMFCApp::InitInstance() {
	CRect Rect(100, 100, 500, 500);
CFrameWnd* pMainFrame = new CFrameWnd;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) {
	AfxMessageBox("LoadFrame failed");
	return FALSE;
	}
pMainFrame->ShowWindow(m_nCmdShow);
m_pMainWnd = pMainFrame;
return TRUE;
}

Using ClassWizard

The following explains how to Create an MFC application without the AppWizard and with ClassWizard support.

Creating the Project and Resource Script File

You could use the same project as the one for the TinyMFC project above but I recommend creating a new project. Create it the same way as for the TinyMFC project. Use whatever name you wish to for the project; I am using "WithoutWizard". Something else you can change in the project settings is to specify use of pre-compiled headers. Just specify "StdAfx.h" for the "Through Header". The next thing to do is to create a Resource Script file; it can also be created the same as for the TinyMFC project.

Creating the Header and Implementation Files

Now we create the header and implementation files. I will assume you are familiar with enough of the basics to understand how this works but there might be some things I should explain better. A lot of this can be done a bit differently depending on your preferences and requirements. I am using the standard filenames StdAfx.h and StdAfx.cpp to ensure use of the ClassWizard, although I am not sure how important that is. At least it allows use of precompiled headers in a standard manner, right?

  1. Create the StdAfx.h header file with the following:
    #pragma once
    
    #define VC_EXTRALEAN
    #include <afxwin.h>
  2. Create the StdAfx.cpp file with the following:
    #include "stdafx.h"
  3. Create the header file for your program. Assuming the project is named "WithoutWizard", the header file would be "WithoutWizard.h". Put the following in it:
    #pragma once
    
    class CWithoutWizardApp : public CWinApp {
    public:
    	CWithoutWizardApp() {};
    
    // Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CWithoutWizardApp)
    public:
    //}}AFX_VIRTUAL
    
    // Implementation
    
    //{{AFX_MSG(CWithoutWizardApp)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    };
    
    //{{AFX_INSERT_LOCATION}}
  4. Create the main cpp file for your program. Assuming the project is named "WithoutWizard", the cpp file would be "WithoutWizard.cpp". Put the following in it:
    #include "stdafx.h"
    #include "resource.h"
    #include "WithoutWizard.h"
    
    BEGIN_MESSAGE_MAP(CWithoutWizardApp, CWinApp)
    //{{AFX_MSG_MAP(CWithoutWizardApp)
    //}}AFX_MSG
    END_MESSAGE_MAP()
    
    CWithoutWizardApp WithoutWizardApp;

Override InitInstance

Notice that unlike TinyMFC, we do not have even an InitInstance for our application. We will now add that.

Save all unsaved files in the project. Then try to show the ClassWizard. There will be a message box saying that the ClassWizard file does not exist and it will offer to build it for you; say yes of course. Then use the ClassWizard to add an override for InitInstance for your application.

Add Code That Does Something

Add something to the InitInstance such as:

#ifdef _AFXDLL
	Enable3dControls(); // Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(8); // Load standard INI file options (including MRU)

CFrameWnd *pFrameWnd;
pFrameWnd = new CFrameWnd;
if (!pFrameWnd->LoadFrame(IDR_MAINFRAME)) {
	AfxMessageBox("Frame not created");
	return FALSE;
	}
m_pMainWnd = pFrameWnd;
pFrameWnd->ShowWindow(m_nCmdShow);
pFrameWnd->UpdateWindow();

Build the project and test it. This will provide a minimal application.

Making Something Useful

You can use the ClassWizard to add a class derived from CFrameWnd and use that class for your frame, so you can customize the window as needed. You would then have a simple application that could be used for many things. The application would probably be especially useful if you only needed to use GDI with the window.

Making a View

Since we are using CFrameWnd::LoadFrame to create a frame window, it is quite easy to add a view. First derive a class from a view. (You can use CFormView, but if you do then you need to create the dialog box template for it before creating a class derived from CFormView.) Then specify the derived class in an instance of CCreateContext, then pass that to LoadFrame. So your InitInstance could be something like:

BOOL CWithoutWizardApp::InitInstance() {
#ifdef _AFXDLL
	Enable3dControls();	// Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();	// Call this when linking to MFC statically
#endif
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(8);	// Load standard INI file options (including MRU)

CFrameWnd *pFrameWnd;
pFrameWnd = new CFrameWnd;
if(!pFrameWnd) {
	AfxMessageBox("Window not made");
	return FALSE;
	}
CCreateContext CreateContext;
CreateContext.m_pNewViewClass = RUNTIME_CLASS(CWithoutWizardView);
CreateContext.m_pCurrentFrame = pFrameWnd;
if (!pFrameWnd->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		NULL, &CreateContext)) {
	AfxMessageBox("Frame Load Error");
	return FALSE;
	}
m_pMainWnd = pFrameWnd;
pFrameWnd->InitialUpdateFrame(NULL, TRUE);	// Will do ShowWindow
return CWinApp::InitInstance();
}

Additional Notes

I have summarized what CFrameWnd::LoadFrame does in case that helps.

Also, I am not sure if it would work to use CWnd::Create to create a plain window instead of using CFrameWnd::LoadFrame and getting a frame window. I tried but could not get it to work. It is easy enough to try but until I do let me just say that one thing that is important is that the main window object must be allocated in the heap (with "new"). That is because MFC will destroy the main window for you and will use "delete" for the object. It does this for the object that m_pMainWnd points to. If m_pMainWnd  is NULL then InitInstance will exit the application, so it does not work to do that.

Since we are operating with a stripped-down MFC we are missing a few conveniences. One problem is: where is the view? How can the applicaton class communicate with the view? The following is one way for the application to find the view:

CView * CWithoutWizardApp::FindView() {
	const CString ClassName("CWithoutWizardView");
	CRuntimeClass* pRuntimeClass;
	CWnd* pWnd=NULL;
pWnd = m_pMainWnd->GetDescendantWindow(AFX_IDW_PANE_FIRST);
if (!pWnd)
	return NULL;
pRuntimeClass = pWnd->GetRuntimeClass();
if (pRuntimeClass->m_lpszClassName == ClassName)
	return (CView *)pWnd;
return NULL;
}

Notice that using CRuntimeClass to check the class is not necessary and could be removed; at least it is not necessary for a release build. So FindView can be used as in the following:

pWithoutWizardView = (CWithoutWizardView*)FindView();
if (pWithoutWizardView) {
	pWithoutWizardView->OnUpdate(NULL, 1, &MessageText);
}

To use OnUpdate in that manner, put the following in your view class:

friend class CWithoutWizardApp;

References

See Wrapping Windows - An Introduction to MFC for some help understanding MFC and for other samples similar to here.


See my Visual C++ Programmer Stuff page for more C++ stuff.