MFC基于对话框工程运行逻辑

1、首先,创建一个基于对话框的MFC工程(注:我使用的是VS2010):

MFC基于对话框工程运行逻辑_第1张图片


MFC基于对话框工程运行逻辑_第2张图片


MFC基于对话框工程运行逻辑_第3张图片


MFC基于对话框工程运行逻辑_第4张图片


2、MFC完全兼容Win32 SDK编程,也就是说,直接用系统API是完全可以的。


3、MFC不仅封装了窗口类,还封装了其它的许多类。


4、现在来介绍MFC对话框程序的基本逻辑。


5、可以看到,向导生成的工程有三个类,即CAboutDlg、CxxxApp和CxxxDlg。其中CAboutDlg是关于对话框的,它是独立的,作用就是弹出一个关于对话框,本文就将其忽略了。剩下的就是App类和Dlg类。


6、程序的流程一般有两条线:一是初始化,即程序的执行过程(从何处开始执行,执行函数的调用关系);二是消息响应。


7、消息有两种:一种比较直观,譬如鼠标点击、键盘点击等普通事件的消息;另一种就是定时器消息,这种消息比较隐蔽。


8、实际上,程序定义了一个全局的App,如下图:

MFC基于对话框工程运行逻辑_第5张图片


MFC基于对话框工程运行逻辑_第6张图片


9、有了这个App对象后,就会调用初始化函数InitInstance,初始化实例:

// CMFC_TESTApp 初始化

BOOL CMFC_TESTApp::InitInstance()
{
	// 如果一个运行在 Windows XP 上的应用程序清单指定要
	// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
	//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// 将它设置为包括所有要在应用程序中使用的
	// 公共控件类。
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);

	CWinApp::InitInstance();


	AfxEnableControlContainer();

	// 创建 shell 管理器,以防对话框包含
	// 任何 shell 树视图控件或 shell 列表视图控件。
	CShellManager *pShellManager = new CShellManager;

	// 标准初始化
	// 如果未使用这些功能并希望减小
	// 最终可执行文件的大小,则应移除下列
	// 不需要的特定初始化例程
	// 更改用于存储设置的注册表项
	// TODO: 应适当修改该字符串,
	// 例如修改为公司或组织名
	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

	CMFC_TESTDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: 在此放置处理何时用
		//  “确定”来关闭对话框的代码
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: 在此放置处理何时用
		//  “取消”来关闭对话框的代码
	}

	// 删除上面创建的 shell 管理器。
	if (pShellManager != NULL)
	{
		delete pShellManager;
	}

	// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
	//  而不是启动应用程序的消息泵。
	return FALSE;
}

10、开头几行代码,设置对话框程序可以加载一些控件,暂时可以不管;


11、然后调用基类的初始化函数:CWinApp::InitInstance();


12、SetRegistryKey(_T("应用程序向导生成的本地应用程序"));与注册表有关,暂不介绍;


13、接着创建了一个Dlg类的对象:CMFC_TESTDlg dlg;,并将指针赋给了App类下的一个成员变量:m_pMainWnd = &dlg;


14、所以theAppm_pMainWnd这两个变量很重要,是贯穿整个程序的。


15、全局变量的构造函数比程序的winmain函数先执行。


16、另外,在MFC中winmain函数被封装了,在App和Dlg类中没有WinMain函数,通过断点以及调用堆栈可以看到WinMain函数:

MFC基于对话框工程运行逻辑_第7张图片


17、我们可以看到,在_tWinMain中其实调用了一个全局的函数AfxWinMain,转到这个函数的定义,实际上定义在一个cpp中,如下:

winmain.cpp

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "sal.h"


/
// Standard WinMain implementation
//  Can be replaced as long as 'AfxWinInit' is called first

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow)
{
	ASSERT(hPrevInstance == NULL);

	int nReturnCode = -1;
	CWinThread* pThread = AfxGetThread();
	CWinApp* pApp = AfxGetApp();

	// AFX internal initialization
	if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
		goto InitFailure;

	// App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication())
		goto InitFailure;

	// Perform specific initializations
	if (!pThread->InitInstance())
	{
		if (pThread->m_pMainWnd != NULL)
		{
			TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
			pThread->m_pMainWnd->DestroyWindow();
		}
		nReturnCode = pThread->ExitInstance();
		goto InitFailure;
	}
	nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
	// Check for missing AfxLockTempMap calls
	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
	{
		TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
			AfxGetModuleThreadState()->m_nTempMapLock);
	}
	AfxLockTempMaps();
	AfxUnlockTempMaps(-1);
#endif

	AfxWinTerm();
	return nReturnCode;
}

/


18、我们创建的CxxApp是派生于CWinApp的,这行代码:CWinApp* pApp = AfxGetApp();其实是用父类的指针指向子类。AfxGetApp返回的就是前面说的那个全局的theApp。


19、接着,pThread->InitInstance(),调用了初始化函数,实际上就是前面贴出的App类的InitInstance函数。也就是基类的一个函数,在子类中实现类,那么就调用子类中的这个函数。


20、接13,创建窗口后调用DoModel显示窗口:INT_PTR nResponse = dlg.DoModal();


21、OK,对话框程序的初始化过程即是如此。


22、总结一下程序的执行流程:

(1)定义一个全局的App对象theApp;

(2)调用CxxxApp的构造函数,初始化theApp;(注:这个构造函数是第一个执行的函数)

(3)接着(第二个执行的函数)就是程序的Main函数了,所有其它的函数都是在Main函数之下调用的;

(4)譬如InitInstance函数都是在WinMain函数之后。

(5)在InitInstance函数中首先调用基类ACinApp的InitInstance函数,接着创建一个CxxxDlg的对象,并将地址赋给一个m_pMainWnd(主窗口的指针变量);

(6)接着,将刚刚创建的CxxxDlg对象DoModel显示出来,即是我们看到的那个对话框 。


23、前面总结的第(5)条只是一掠而过,实际上,CxxxDlg对象的创建,以及创建后的消息响应是十分复杂的。就创建一个CxxxDlg对象来说,必须调用该类的构造函数:

CMFC_TESTDlg::CMFC_TESTDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CMFC_TESTDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}


24、加载一个图标:m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);


25、CxxxDlg对象构造之后,DoModel执行后会发送第一个消息,调用CxxxDlg类的OnInitDialog函数:

// CMFC_TESTDlg 消息处理程序

BOOL CMFC_TESTDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

26、在MFC中前面带On的函数,要么直接就是消息映射函数,要么就是被消息映射函数所调用的函数,所以都与消息相关。并且很可能是虚函数,也就是说,消息映射在基类中处理了,然后调用子类中的一个函数。


27、如下,是一个系统命令的消息响应函数:

void CMFC_TESTDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}


28、接着是一个绘图消息的响应函数:

void CMFC_TESTDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

29、最后,是一个图标拖动的消息响应函数:

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFC_TESTDlg::OnQueryDragIcon()
{
	return static_cast(m_hIcon);
}


30、OK,MFC基于对话框的程序基本运行逻辑就是这样。另外,剩下一个最重要的消息循环,因为比较复杂难懂,所以会另写一篇文章。


你可能感兴趣的:(MFC)