MFC - 一文带你从小白到项目应用(全套1)

    文章篇幅可能会比较长,从入门到基本能上项目的全部内容。建议观看的过程中,用电脑跟着学习案例。

    持续输出优质文章是作者的追求,因为热爱,所以热爱。

    最近看动漫被一句鸡汤感动到了,也送给各位朋友:

    只要有一息尚存,战斗便永不停止...

 windows消息机制

如果想要更好的学习掌握MFC,必须要先了解WIndows程序的内部运行机制。

windows编程基础 -- win32程序

        在windows平台下,提供了很多函数可以调用,这些函数是由windows操作系统本身提供的。windows应用程序API函数是通过C语言实现的,所有主要的windows函数都在windows.h头文件中进行了声明。Windows操作系统提供了1000多种API函数。

窗口和句柄

        窗口是windows应用程序中一个非常重要的元素,一个windows应用程序至少要有一个窗口,称为主窗口。窗口是屏幕上的一块矩形区域,是windows应用程序与用户进行交互的接口。利用窗口可以接收用户的输入以及显示输出。

MFC - 一文带你从小白到项目应用(全套1)_第1张图片

        窗口又分为客户区(中间空白的部分)和非客户区(蓝色那部分)。

        客户区是窗口的一部分,应用程序通常在客户区中显示文字或者绘制图形。 应用程序则主要管理客户区的外观及操作

        非客户区也是窗口的一部分,通常将标题栏、菜单栏、系统菜单、最小化框和最大化框、可调边框统称为窗口的非客户区,它们由windows系统来管理,

       总结来说:我们写代码主要控制客户区部分、非客户区部分通常由操作系统控制。

       

       窗口是windows中一个很重要的概念,是一个几部组件部分。窗口有父窗口和子窗口,除了上图所示类型的窗口外,对话框和消息框也是一种窗口。在对话框上通常还包含很多子窗口,这些窗口的形式也按钮、单选按钮、复选按钮、组框、文本编辑等。

       操作窗口是通过一个指针来控制的(可以将窗口近似的理解为一个结构体的一个变量),有一个指针指向这个对象,通过这个指针就可以对这个结构体变量中的成员进行操作,比如大小、背景颜色、按钮的响应等。所以想要操作某一个窗口,就必须先拿到这个窗口的指针,在windows中习惯称为句柄(HWDN)。

        句柄(HANDLE)是一个非常重要的概念,在windows程序中,定义了很多结构体,操作这些结构体变量(也是各种各样的资源)通常情况需要使用句柄。系统在创建这些资源时会为它们分配内存,并返回标识这些资源的标识号。比如:图标句柄、光标句柄、画刷句柄等。

消息与消息队列

        通常情况下,我们写的程序是一行一行执行的,也就是DOS方式的程序设计方式。而windows程序并不是这样的,它是一种事件驱动模式,这中框架模式思想很值得我们了解。

MFC - 一文带你从小白到项目应用(全套1)_第2张图片

         每一个windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放程序创建的窗口的消息。(这些消息比如:鼠标左键按下)

        一个应用程序可以有多个窗口,这些窗口会有一个窗口标记,也就是上面说的窗口句柄,通过这个窗口句柄,操作系统就知道在哪个窗口上进行相应的处理。

        整个框架的流程:

        1.用户在客户区按下了鼠标左键,此时操作系统会感知到用户按下了鼠标左键这个事件。

        2.操作系统将这个事件包装成一个消息,并将这个消息放到消息队列中,等待应用程序从队列中取出消息并进行处理。

        3.应用程序从消息队列中取出消息,然后进行响应

        4.这个响应过程,通常应用程序交给操作系统进行处理,操作系统会调用消息处理函数(回调函数),这个函数被称为窗口过程。

MFC - 一文带你从小白到项目应用(全套1)_第3张图片

         步骤4:整个响应过程就是一个回调

        

 Windows编程模型

        一个完整的win32程序实现的功能就是创建一个窗口,然后在这个窗口中响应键盘、鼠标消息。这个窗口可以有一些子窗口...本质上就是这样的,并不复杂。

        大致步骤:

        1.windows函数定义

        2.创建一个窗口

        3.进行消息循环

        4.编写窗口过程函数

创建一个win32的空项目

MFC - 一文带你从小白到项目应用(全套1)_第4张图片

 MFC - 一文带你从小白到项目应用(全套1)_第5张图片

MFC - 一文带你从小白到项目应用(全套1)_第6张图片

MFC - 一文带你从小白到项目应用(全套1)_第7张图片

 MFC - 一文带你从小白到项目应用(全套1)_第8张图片

 编写一个简单的WIN32程序

1.主函数定义(WinMain)

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nShowCmd)
{

	return 0;
}

WINAPI:是一个宏,表示参数从右往左入栈,同时在函数返回前自动清空堆栈

hInstance:(当前应用程序的句柄)一个应用程序的的实例句柄,一个应用程序可以运行多个实例,每一个运行的应用程序都会有一个句柄,通过hInstance传递给WinMain函数

hPrevInstance:当前实例的前一个实例句柄,通常传NULL

IpCmdLine:一个字符串数组,传给给应用程序的命令行参数,相当于 char* argv[]

nShowCmd:窗口显示的方式,最大化/最小化/正常显示/隐藏显示

2.创建一个窗口

        创建一个WIN32窗口,通常有4个步骤: 设计一个窗口类,注册窗口类,创建窗口,显示和更新窗口(Windows.h中有现成的API可以调用)

        步骤1:设计一个窗口类

        这一步可以用来指定在我们这个窗口中的光标的样子、背景色、图标等这些属性。

        也就是说,在创建一个窗口前,必须对给类型的窗口进行设计,指定窗口的特征。在windows中,窗口的特征由WNDCLASS结构体来定义的,我们只需要给WNDCLASS结构体对应的成员进行赋值就可以完成窗口类的设计

        WNDCLASS结构体的定义:

MFC - 一文带你从小白到项目应用(全套1)_第9张图片

style:指定窗口的样式

         CS_HREDRAW ==> 当窗口水平方向上的宽度发生变化时, 将重新绘制整个窗口。如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。

         CS_VREDRAW ==> 当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,那么在垂直方向上调整窗口高度时,将不会重绘窗口。

         CS_NOCLOSE ==> 禁用系统菜单的 Close 命令,这将导致窗口没有关闭按钮。

         CS_DBLCLKS ==> 当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。

lpfnWndProc:指定一个窗口回调函数,是一个函数的指针

        当应用程序收到给某一窗口的消息时,就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己完成,由操作系统来完成。上面说的回调过程。每一个窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。

MFC - 一文带你从小白到项目应用(全套1)_第10张图片

cbClsExtra:类的附加内存,通常数情况下为0

cbWndExtra:窗口附加内存,通常情况下为0

hInstance:当前实例句柄,用WinMain中的形参hInstance为其赋值

hIcon:指定窗口类的图标句柄,设置为NULL,则使用默认图标

hCursor:指定窗口类的光标句柄,设置为NULL,则使用默认图标,也可用如下函数进行赋值:

hbrBackground:指示窗口的背景颜色,可用如下函数进行赋值:

lpszMenuName:指定菜单资源的名字。如果设置为NULL,那么基于这个窗口类创建的窗口将没有默认菜单

lpszClassName:指定窗口类的名字

       步骤2:注册窗口类

        设计完窗口类(WNDCLASS)后, 需要调用RegisterClass函数对其进行注册,注册成功后,才可以创建该类型的窗口。

       步骤3:创建窗口

        设计好窗口类并且将其成功注册之后, 即可用CreateWindow函数产生这种类型的窗口了。

MFC - 一文带你从小白到项目应用(全套1)_第11张图片

 MFC - 一文带你从小白到项目应用(全套1)_第12张图片

lpClassName:指定窗口类的名称,此名字必须和WNDCLASS的lpszClassName成员指定的名称一样。

lpWindowName:指定窗口的名字,即窗口的标题。

dwStyle:指定创建的窗口的样式,常指定为指WS_OVERLAPPEDWINDOW类型,这是一种多种窗口类型的组合类型。

x, y:指定窗口左上角的x,y坐标。如果参数x被设为CW_USEDEFAULT,那么系统为窗口选择默认的左上角坐标并忽略y参数。

nWidthnHeight:指定窗口窗口的宽度,高度。如果参数nWidth被设为 CW_USEDEFAULT,那么系统为窗口选择默认的宽度和高度,参数nHeight被忽略。

hWndParent:指定被创建窗口的父窗口句柄,没有父窗口,则设置NULL。

hMenu:指定窗口菜单的句柄,没有,则设置为NULL。

hInstance:窗口所属的应用程序实例的句柄,用WinMain中的形参hInstance为其赋值。

lpParam:作为WM_CREATE消息的附加参数lParam传入的数据指针。通常设置为NULL。

返回值:如果窗口创建成功,CreateWindow函数将返回系统为该窗口分配的句柄,否则,返回NULL。

MFC - 一文带你从小白到项目应用(全套1)_第13张图片

        步骤4:显示和更新窗口

        显示窗口函数原型:

        更新窗口函数原型:

         示例:

 3.消息循环

        我们需要编写一个消息循环,不断地从消息队列中取出消息,并进行响应。在Windows程序中,消息是由MSG结构体来表示的。

        MSG结构体的定义如下

MFC - 一文带你从小白到项目应用(全套1)_第14张图片

 hWnd:消息所属的窗口。我们通常开发的程序都是窗口应用程序,一个消息一般都是与某个窗口相关联的。例如,在某个活动窗口中按下鼠标左键,产生的按键消息就是发给该窗口的。

message:消息的标识符,是由一个数值来表示的,不同的消息对应不同的数值。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是 WM_CHAR。

wParam: 指定消息的附加信息,如键盘按下会触发WM_KEYDOWN消息,但是,具体按下哪个按键需要wParam区分。

time:标识一个消息产生时的时间。

pt:表示产生这个消息时光标或鼠标的坐标。

        取消息:

    要从消息队列中取出消息,我们需要调用GetMessage()函数。       MFC - 一文带你从小白到项目应用(全套1)_第15张图片

lpMsg:指向一个消息结构体(MSG)

hWnd:指定接收属于哪一个窗口的消息。通常我们将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。

wMsgFilterMin:指定消息的最小值。

wMsgFilterMax:指定消息的最大值。如果wMsgFilterMin和wMsgFilterMax都设置为0, 则接收所有消息。

返回值:GetMessage函数接收到除 WM_QUIT 外的消息均返回非零值。对于WM_QUIT消息,该函数返回零。如果出现了错误,该函数返回-1,例如,当参数hWnd是无效的窗口句柄或lpMsg是无效的指针时。

        建立消息循环

MFC - 一文带你从小白到项目应用(全套1)_第16张图片TranslateMessage:用于翻译、处理和转换消息并把新消息投放到消息队列中,并且此过程不会影响原来的消息队列。

DispatechMessage:用于把收到的消息传到窗口回调函数进行分析和处理。

4.窗口过程处理函数

        在完成上述步骤后,剩下的工作就是编写一个窗口过程函数,用于处理发送给窗口的消息。窗口过程函数的名字可以随便取, 如WinProc, 但函数定义的形式必须和下面声明的形式相同:

      MFC - 一文带你从小白到项目应用(全套1)_第17张图片

 示例:

LRESULT CALLBACK WinProc(
	HWND hWnd,		//信息所属的窗口句柄
	UINT uMsg,		//消息类型
	WPARAM wParam,	//附加信息(如键盘按键)
	LPARAM lParam	//附加信息(如鼠标点击坐标)
	)
{
	switch (uMsg)
	{
	case WM_KEYDOWN: //键盘按下
		//……
		break;
	case WM_LBUTTONDOWN: //鼠标右键按下
		//……
		break;
	case WM_PAINT: //绘图事件
		//……
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_CLOSE: 
		DestroyWindow(hWnd); 
		break;
	default:
		//以windows默认方式处理
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
		break;
	}

	return 0;
}

DefWindowProc函数:DefWindowProc函数调用默认的窗口过程,对应用程序没有处理的其他消息提供默认处理

WM_CLOSE:对WM_CLOSE消息的响应并不是必须的,如果应用程序没有对该消息进行响应,系统将把这条消息传给DefWindowProc函数而 DefWindowProc函数则调用DestroyWindow函数来响应这条WM_CLOSE消息

WM_DESTROY:DestroyWindow函数在销毁窗口后,会给窗口过程发送 WM_DESTROY消息,我们在该消息的响应代码中调用PostQuitMessage函数。PostQuitMessage函数向应用程序的消息队列中投递一条WM_QUIT消息并返回。WinMain函数中,GetMessage 函数只有在收到WM_QUIT消息时才返回0,此时消息循环才结束,程序退出。传递给 PostQuitMessage函数的参数值将作为WM_QUIT消息的wParam参数,这个值通常用做WinMain函数的返回值。


完整代码:

MFC - 一文带你从小白到项目应用(全套1)_第18张图片

#include 


LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nShowCmd)
{

	// 1.设计一个窗口类
	WNDCLASS wc;                                             // 窗口类变量
	wc.cbClsExtra = 0;                                       // 类附加内存
	wc.cbWndExtra = 0;                                       // 窗口附加对象
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  // 设置背景色
	wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_HELP);        // 帮助光标
	wc.hIcon = (HICON)LoadIcon(NULL, IDI_ERROR);             // 警告图标
	wc.hInstance = hInstance;                                // 应用程序实例
	wc.lpfnWndProc = WindowProc;                             // 窗口过程函数名字
	wc.lpszClassName = TEXT("MyWin");                        // 类的名字
	wc.lpszMenuName = NULL;                                  // 没有菜单
	wc.style = 0;                                            // 默认风格

	// 2.注册窗口类
	RegisterClass(&wc);

	// 3.创建窗口
	HWND hWnd = CreateWindow(TEXT("MyWin"), TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	// 4.显示和更新窗口
	ShowWindow(hWnd, SW_SHOWNORMAL);
	UpdateWindow(hWnd);

	// 5,建立消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.lParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

	switch (uMsg)
	{
	case WM_KEYDOWN:              // 键盘按下
		MessageBox(hWnd, TEXT("键盘按下"), TEXT("键盘"), MB_OK);
		break;
	case WM_LBUTTONDOWN:          // 鼠标左键按下
		MessageBox(hWnd, TEXT("鼠标左键按下"), TEXT("鼠标"), MB_OK);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}

}

 MFC入门

        MFC(Microsoft Foundation Classese)是微软公司提供一个类库,以C++形式(上面使用的WIN32是C风格的接口)封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包括大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。

        MFC把Windows SDK API函数包装成了几百个类,MFC给Windows操作系统提供了面向对象的接口,支持可重用性、自包含性以及其他OPP原则。MFC通过编写类来封装窗口、对话框以及其他对象,引入某些关键的虚函数(覆盖这些虚函数可以改变派生类的功能)来完成,并且MFC设计者使类库带来的总开销降到了最低。

第一个MFC程序

        编写MFC程序需要包含#include头文件:

MFC - 一文带你从小白到项目应用(全套1)_第19张图片         程序的执行流程:

         1.程序开始时,先实例化应用程序对象(有且只有一个)

         2.执行程序入口函数InitInstance()

         3.给框架类MyFrame对象动态分配空间(自动调用它的构造函数)

         4.管家类对象显示窗口 CWnd::ShowWindows

         5.框架类对象更新窗口 CWnd::UpdateWindow

         6.保存框架类对象指针 CWinTread::m_pMainWnd

         代码分析:

         1.CFrameWnd框架窗口类

         CFrameWnd是从CWnd(窗口基类)派生出来的。CFrameWnd模仿框架窗口行为,我们

可以把框架窗口作为顶层窗口看待,它是应用程序与外部世界的主要接口。

          如果想创建一个窗口,可以在此类中调用CWnd::Create或CWnd::CreateEX函数

MFC - 一文带你从小白到项目应用(全套1)_第20张图片

         一般情况下,前6个参数使用默认值。lpszClassName指定了窗口基于WNDCLASS类的名称,为此将其设定为NULL将创建一个基于已注册的WNDCLASS类的默认框架窗口。lpszWindowName参数指定将在窗口的标题栏出现的文字。

        2.CWinApp应用程序类

        MFC应用程序的核心就是基于CWinApp类的应用程序对象。CWinApp提供了消息循环来检索消息并将消息调度给应用程序窗口。它还包括可被覆盖的、用来自定义应用程序行为的主要虚函数。

        一个MFC程序可以有且仅有一个应用程序对象,此对象必须声明为在全局范围内有效,以便它在程序开始时即在内存中被实例化。

        3.InitInstance函数

        CWinApp::InitInstance函数是一个虚函数,其默认操作仅有一条语句:

        InitInstance的目的是给应用程序提供一个自身初始化的机会,其返回值决定了框架接下来要执行的内容,如果返回FALSE将关闭应用程序,如果初始化正常返回TRUE以便允许程序继续进行。此函数是MFC应用程序的入口

        4.m_pMainWnd成员变量

        在CWinApp中有一个名为CWinTread::m_pMainWnd的成员变量。该变量是一个CWnd类型的指针,它保存了应用程序框架窗口对象的指针。也就是说,是指向CFramWnd对象(框架窗口类对象)的指针。

       消息映射机制:

       消息映射是一张将消息和成员函数相互关联的表。比如,框架窗口接收到了一个鼠标左击消息,MFC将搜索该窗口的消息映射,如果存在一个处理WM_LBUTTONDOWN消息的处理程序,就会去调用。

       消息映射的步骤:

       1.所操作类中,声明消息映射宏

MFC - 一文带你从小白到项目应用(全套1)_第21张图片

       2.通过放置标识的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的调用之间处理消息。

MFC - 一文带你从小白到项目应用(全套1)_第22张图片

         3.对应消息处理函数分别在类中声明,类外定义

MFC - 一文带你从小白到项目应用(全套1)_第23张图片

Widnows字符集

MFC - 一文带你从小白到项目应用(全套1)_第24张图片

        多字节字符集(8位的ANSI字符集)

        在Windows98以及以前的版本使用8位ANSI字符集,它类似于我们程序员熟悉的ASCII字符集。

            宽字符集(16位的Unicode字符集)

        在WindowsNT中和Windows2000后开始使用16位的Unicode字符集,它是ANSI字符集的一个超集。Unicode适用于国际市场销售应用程序,因为它包含了各种各样来自非U.S.字母表的字符,比如:中文、日文、韩文、西欧等

         TEXT(_T)宏

         MFC中的TEXT宏可以自动适应字符类型,如果定义了预处理器程序符号_UNICODE,那么编译器将使用Unicode字符,如果没有定义该预处理程序符号,那么编译器将使用ANSI字符。

 

         TCHAR类型

          如果定义了_UNICODE符号TCHAR将变成wchar_t类型。如果没有定义_UNICODE符号,TCHAR将变成普通古老的char类型。

用向导生成一个MFC应用程序 

MFC - 一文带你从小白到项目应用(全套1)_第25张图片

MFC - 一文带你从小白到项目应用(全套1)_第26张图片MFC - 一文带你从小白到项目应用(全套1)_第27张图片

MFC - 一文带你从小白到项目应用(全套1)_第28张图片

MFC - 一文带你从小白到项目应用(全套1)_第29张图片

MFC - 一文带你从小白到项目应用(全套1)_第30张图片

 类视图

MFC - 一文带你从小白到项目应用(全套1)_第31张图片

MFC - 一文带你从小白到项目应用(全套1)_第32张图片

 文档/视图结构体系

         MFC应用程序框架结构的基础是文档/视图体系结构,这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据,把数据本身与它的显示分离开。

        数据的存储和加载由文档类来完成,数据的显示和修改则由视图类来完成。MFC在类CDocumentCView中为视图提供了基础结构。

         CWinApp、CFrameWnd和其他类与CDocument和CView合作,把所有的片段连在一起。CView类也派生于CWnd类,框架窗口是视图窗口的一个父窗口。

        主框架窗口(CFrameWnd)是整个应用程序外框架所包含的部分,而视图类窗口只是主框架中空白的地方。

MFC - 一文带你从小白到项目应用(全套1)_第33张图片

 消息处理添加

        在主框架类中添加WM_LBUTTONDOWN消息的响应函数:

MFC - 一文带你从小白到项目应用(全套1)_第34张图片

从类视图中找到CMainFrame(继承自CFrameWnd),选择此类然后从属性面板中找到消息按钮,在消息列表中找到WM_LBUTTONDOWN消息,添加。

工程文件增加的改变:

1.在框架类头文件中添加了鼠标左键消息函数的函数声明

MFC - 一文带你从小白到项目应用(全套1)_第35张图片

2.在框架类cpp文件中添加了消息映射宏

MFC - 一文带你从小白到项目应用(全套1)_第36张图片 3.在框架类cpp文件中添加了处理鼠标左键消息的函数定义

MFC - 一文带你从小白到项目应用(全套1)_第37张图片

        我们再此OnLButtonDown函数中添加一个MessageBox消息,鼠标左键按下弹出一个提示框,然后执行程序。我们会惊奇的发现程序并未如我们所愿弹出消息框。

        因为,框架窗口是视窗口的父窗口,那么视类窗口就应该始终覆盖在框架类窗口之上。就好比框架窗口是一面墙,视类窗口就是墙纸,它始终挡在这面墙前边。也就是说,所有操作,包括鼠标单击、鼠标移动等操作都只能有视类窗口捕获。

        我们通过上述同样的操作在视图类中添加鼠标响应消息,看程序运行的结果

MFC - 一文带你从小白到项目应用(全套1)_第38张图片

你可能感兴趣的:(c++基础,mfc,windows,c++)