其实MFC的原理很简单,不要被AppWizard生成的代码吓住了。MFC程序是从CWinApp类和InitInstance()函数开始的。
MFC的原理:
1. 开发者定义一个基于CWinApp的类,初始化它的唯一的对象,内置构造函数会向MFC注册这个对象。
2. MFC会在自身初始化完毕后调用该对象的InitInstance()函数,开发者可以重写这个函数来对程序进行初始化:
(1) InitInstance()可以初始化主窗口m_pMainWnd,也可以进行其它的操作,如显示对话框。
(2) InitInstance()函数初始化并设置主窗口后返回TRUE进入消息循环,等待WM_QUIT退出;如不设置主窗口或返回FALSE,则直接退出。
3. CWinApp可以调用其它类(如CDialog或CFrameWnd)来实现程序的功能,通过继承重写这些类,可以对它们进行编程。
4. 通过BEGIN_MESSAGE_MAP和END_MESSAGE_MAP等宏定义,将各种菜单命令、Windows消息、控件通知映射到成员函数。
而以上这些,基本都可以由Visual C++各种向导自动化地完成。
MFC并不是没有WinMain函数,而是在程序库的内部实现了WinMain函数。MFC也不是没有WndProc函数,实际上也是在内部实现,并通过某种机制,将消息映射到各MFC类的成员函数。
MFC通过message map宏定义来处理消息,其中:
1. CWinApp等继承自CCmdTarget的类均可以收到WM_COMMAND消息——通常是菜单命令
2. CFrameWnd和CDialog等继承自CWnd的类可以收到WM_COMMAND命令消息,WM_NOTIFY控件通知,以及其它窗口消息。
最简单的MFC程序只有25行,简单吧:
<code class="lang-cpp">// 最简单的MFC程序
#include "stdafx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CSimpleProg1App : public CWinApp
{
public:
CSimpleProg1App() {
}
virtual BOOL InitInstance() {
return FALSE; // 直接退出
}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CSimpleProg1App, CWinApp)
END_MESSAGE_MAP()
CSimpleProg1App theApp; // 唯一的一个CSimpleProg1App对象</code>
基于对话框的最简单的程序:
<code class="lang-cpp">// SimpleProg1.h : PROJECT_NAME 应用程序的主头文件
//
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
#define IDD_SIMPLEPROG1_DIALOG 102 // 对话框资源
// CSimpleProg1App:
// 有关此类的实现,请参阅 SimpleProg1.cpp
//
class CSimpleProg1App : public CWinApp
{
public:
CSimpleProg1App();
// 重写
public:
virtual BOOL InitInstance();
// 实现
DECLARE_MESSAGE_MAP()
};
extern CSimpleProg1App theApp;</code>
<code class="lang-cpp">// SimpleProg1.cpp : 定义应用程序的类行为。
//
#include "stdafx.h"
#include "SimpleProg1.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CSimpleProg1App
BEGIN_MESSAGE_MAP(CSimpleProg1App, CWinApp)
END_MESSAGE_MAP()
// CSimpleProg1App 构造
CSimpleProg1App::CSimpleProg1App()
{
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CSimpleProg1App 对象
CSimpleProg1App theApp;
// CSimpleProg1App 初始化
BOOL CSimpleProg1App::InitInstance()
{
CDialog dlg(IDD_SIMPLEPROG1_DIALOG);
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}</code>
上面的是基于对话框的程序,用Win32API编写对话框程序同样比较简单,但是用Win32API编写框架窗口就太困难了——要自己注册窗口类,自己创建窗口,自己显示窗口,自己编写消息循环,还要自己编写WndProc函数,太麻烦了。如果你使用MFC,就会和对话框一样简单:
<code class="lang-cpp">// SimpleProg2.h : SimpleProg2 应用程序的主头文件
//
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
#define IDD_ABOUTBOX 100 // “关于”对话框资源
#define IDR_MAINFRAME 128 // 与主框架有关的菜单、标题文字、图标等资源
// CSimpleProg2App:
// 有关此类的实现,请参阅 SimpleProg2.cpp
//
class CSimpleProg2App : public CWinApp
{
public:
CSimpleProg2App();
// 重写
public:
virtual BOOL InitInstance();
// 实现
public:
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
extern CSimpleProg2App theApp;</code>
<code class="lang-cpp">// SimpleProg2.cpp : 定义应用程序的类行为。
//
#include "stdafx.h"
#include "SimpleProg2.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CSimpleProg2App
BEGIN_MESSAGE_MAP(CSimpleProg2App, CWinApp)
ON_COMMAND(ID_APP_ABOUT, &CSimpleProg2App::OnAppAbout)
END_MESSAGE_MAP()
// CSimpleProg2App 构造
CSimpleProg2App::CSimpleProg2App()
{
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CSimpleProg2App 对象
CSimpleProg2App theApp;
// CSimpleProg2App 初始化
BOOL CSimpleProg2App::InitInstance()
{
// 若要创建主窗口,此代码将创建新的框架窗口
// 对象,然后将其设置为应用程序的主窗口对象
CFrameWnd* pFrame = new CFrameWnd;
if (!pFrame)
return FALSE;
m_pMainWnd = pFrame;
// 创建并加载框架及其资源
pFrame->LoadFrame(IDR_MAINFRAME,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
NULL);
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
// CSimpleProg2App 消息处理程序
// 用于运行对话框的应用程序命令
void CSimpleProg2App::OnAppAbout()
{
CDialog aboutDlg(IDD_ABOUTBOX);
aboutDlg.DoModal();
}</code>
可以发现这里根本没有Win32程序繁杂的窗口类注册、消息循环和WndProc函数——因为MFC已经帮我们实现了。我们要做的只是创建窗口并显示它。这里还定义了一个ID_APP_ABOUT“关于”菜单命令并在CWinApp里实现了它。ID_APP_ABOUT的值是系统内置的,所以不用手工定义。
200字以内,仅用于支线交流,主线讨论请采用回复功能。