MFC学习笔记(一):windows编程与MFC的第一个程序

目录

  • 1 Windows编程
    • 1.1Windows编程实现显示视窗
    • 1.2消息与消息队列
  • 2 MFC第一个例子
    • 2.1 MFC简介
    • 2.2第一个例子代码实现
    • 2.3 一些主要的类
      • CFrameWnd 框架窗口类
      • CWinApp应用程序类
    • 2.4 MFC消息映射

1 Windows编程

1.1Windows编程实现显示视窗

之前学了使用windows编程显示界面,在这首先复习一下其主要原理,主要有一下几个步骤:
1、设计窗口:创建WNDCLASS类
2、注册窗口:RegisterClass
3、创建窗口:CreateWindow
4、显示与更新:ShowWindow和UpdateWindow
5、通过循环取消息:GetMessage、TranslateMessage、DispatchMessage
6、处理消息(窗口过程):WindowProc(可自定义名字)

//本例子实现了底层方法实现显示界面的全过程
#include  //底层实现窗口的头文件

//6处理窗口过程
//CALLBACK  代表__stdcall 参数的传递顺序:从右到左 以此入栈,并且在函数返回前 清空堆栈
LRESULT CALLBACK WindowProc(
	HWND hwnd, //消息所属的窗口句柄
	UINT uMsg, //具体消息名称  WM_XXXX 消息名
	WPARAM wParam, //键盘附加消息
	LPARAM lParam  //鼠标附加消息
	)
{
	switch (uMsg)
	{
	case WM_CLOSE:
		//所有xxxWindow为结尾的方法 ,都不会进入到消息队列中,而是直接执行
		DestroyWindow(hwnd); //DestroyWindow 发送另一个消息 WM_DESTROY
		break;
	case  WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_LBUTTONDOWN: //鼠标左键按下
			{
			int xPos = LOWORD(lParam);
			int yPos = HIWORD(lParam);

			char buf[1024];
			wsprintf(buf,TEXT("x = %d,y = %d"), xPos, yPos);

			MessageBox(hwnd, buf, TEXT("鼠标左键按下"), MB_OK);

			break;
			}
	case WM_KEYDOWN: //键盘
		MessageBox(hwnd, TEXT("键盘按下"), TEXT("键盘按下"), MB_OK);
		break;

	case WM_PAINT: //绘图
		{
			PAINTSTRUCT ps; //绘图结构体
			HDC hdc = BeginPaint(hwnd, &ps);

			TextOut(hdc, 100, 100, TEXT("HELLO"), strlen("HELLO"));

			EndPaint(hwnd, &ps);
		}
		break;
	}
	//返回值用默认处理方式
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

//程序入口函数

//WINAPI 代表__stdcall 参数的传递顺序:从右到左 以此入栈,并且在函数返回前 清空堆栈
int WINAPI WinMain(
	HINSTANCE hInstance,  //应用程序实例句柄 
	HINSTANCE hPrevInstance, //上一个应用程序句柄,在win32环境下,参数一般为NULL,不起作用了
	LPSTR lpCmdLine, //char * argv[] 
	int nShowCmd) //显示命令 最大化、最小化 正常
{

	//1、设计窗口
	//2、注册窗口
	//3、创建窗口
	//4、显示和更新
	//5、通过循环取消息
	//6、处理消息 (窗口过程)

	//1、设计窗口
	WNDCLASS wc;
	wc.cbClsExtra = 0; //类的额外的内存 
	wc.cbWndExtra = 0; //窗口额外的内存
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //设置背景
	wc.hCursor = LoadCursor(NULL, IDC_HAND); //设置光标 如果第一个参数为NULL,代表使用系统提供的光标
	wc.hIcon = LoadIcon(NULL, IDI_ERROR); //图标   如果第一个参数为NULL,代表使用系统提供的光标
	wc.hInstance = hInstance;  //应用程序实例句柄  传入WinMain中的形参即可
	wc.lpfnWndProc = WindowProc;  //回调函数  窗口过程
	wc.lpszClassName = TEXT("WIN"); //指定窗口类名称
	wc.lpszMenuName = NULL; //菜单名称
	wc.style = 0; //显示风格 0代表默认风格

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

	//3、创建窗口
	/*
	lpClassName,  类名 
	lpWindowName, 标题名
	dwStyle,  WS_OVERLAPPEDWINDOW 风格
	x,  显示坐标    CW_USEDEFAULT  默认值
	y,
	nWidth, 宽高
	nHeight, 
	hWndParent,  父窗口 NULL
	hMenu,  菜单 NULL
	hInstance,  实例句柄 hInstance
	lpParam) 附加值 NULL 
	*/

	HWND hwnd = CreateWindow(wc.lpszClassName, TEXT("WINDOWS"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	//4、 显示和更新
	ShowWindow(hwnd, SW_SHOWNORMAL);
	UpdateWindow(hwnd);

	//5、 通过循环取消息
	/*
	 HWND        hwnd; 主窗口句柄
	 UINT        message; 具体消息名称
	 WPARAM      wParam; 附加消息 键盘消息
	 LPARAM      lParam; 附加消息 鼠标消息
	 DWORD       time;  消息产生时间
	 POINT       pt;    附加消息  鼠标消息  x y
	*/
	MSG msg;

	while (GetMessage(&msg, NULL, 0, 0))
	{
		/*
		  _Out_ LPMSG lpMsg, 消息
		  _In_opt_ HWND hWnd, 捕获窗口 填NULL代表捕获所有的窗口
		  _In_ UINT wMsgFilterMin,  //最小和最大的过滤的消息  一般填入0
		  _In_ UINT wMsgFilterMax)  //填0代表捕获所有消息
		*/
		//if (GetMessage(&msg, NULL, 0, 0) == FALSE)
		//{
		//	break;
		//}

		//翻译消息  
		//如将WM_PAINT 翻译成计算机可以运行的代码
		TranslateMessage(&msg);

		//不为false
		//分发消息
		DispatchMessage(&msg);
	}
	return 0;
}

1.2消息与消息队列

windows中消息结构体如下所示:

typedef struct tagMSG {
	HWND hWnd; //消息所属的窗口句柄
	UINT message; //消息标识符,是一个数值,对应宏定义中WM_xxx的数值
	WPARAM wParam;//附加消息  键盘消息
	LPARAM lParam; //附加消息  鼠标消息
	DWORD time; //产生消息的时间
	POINT pt;//表示产生这个消息时光标或鼠标的坐标
} MSG;

在windows界面编程中,实现对于消息的处理是以消息队列的方式实现的。
比如当用户是用鼠标点击时,操作系统将点击这个动作包装成一个消息,投递到当前的应用程序消息序列中,等待处理。然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。有一个专门负责处理消息的函数称为窗口过程函数,在上面的例子中WindowProc函数就是窗口过程函数,负责处理传入的消息。如下图所示,为windows消息处理的过程图。
MFC学习笔记(一):windows编程与MFC的第一个程序_第1张图片
建立消息循环的过程:

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

GetMessage函数负责从消息队列中获取消息,当该函数接收到WM_QUIT时函数返回零值。
TranslateMessage函数用于翻译、处理和转换消息并把新消息投放到消息队列中,并且此过程不会影响原来的消息队列。
DispatchMessage用于把收到的消息传到窗口回调函数进行分析和处理。即将消息传递给操作系统,让操作系统调用窗口回调函数,来对信息进行处理。

2 MFC第一个例子

2.1 MFC简介

微软基础类库(Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
MFC把Windows SDK API函数包装成了几百个类,MFC给Windows操作系统提供了面向对象的接口,支持可重用性、自包含性以及其他OPP原则。

2.2第一个例子代码实现

MFC.h:

#include //mfc头文件

class MyApp : public CWinApp  //CWinApp应用程序类
{
public:
	//MFC程序入口
	virtual BOOL InitInstance();
private:
};

class MyFrame: public CFrameWnd //CFrameWnd 窗口框架类
{
public:
	//构造函数
	MyFrame();

	//声明宏 提供消息映射机制
	DECLARE_MESSAGE_MAP()
	afx_msg void OnLButtonDown(UINT, CPoint);
	afx_msg void OnChar(UINT, UINT, UINT);
	afx_msg void OnPaint();
};

MFC.cpp:

#include "MFC.h"//对应类声明的头文件

MyApp app; //全局应用程序对象,有且只有一个
BOOL MyApp::InitInstance()//程序入口地址
{
	//1、创建框架类对象
	MyFrame * frame = new MyFrame;

	//2、显示窗口
	frame->ShowWindow(SW_SHOWNORMAL);
	//3、更新窗口
	frame->UpdateWindow();

	m_pMainWnd = frame;//4、保存指向应用程序主窗口的指针,即保存框架类指针
	return TRUE;//返回正常的初始化

}

//分界宏
BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd)
	ON_WM_LBUTTONDOWN()//鼠标左键按下  
	ON_WM_CHAR()  //键盘
	ON_WM_PAINT() //绘图宏
END_MESSAGE_MAP()

MyFrame::MyFrame()
{
	Create(NULL, TEXT("mfc"));//创建窗口
}

void MyFrame::OnLButtonDown(UINT, CPoint point)
{
	/*
	TCHAR buf[1024];
	wsprintf(buf, TEXT("x = %d, y = %d"), point.x, point.y);
	MessageBox(buf);
	*/

	//mfc中的字符串类型为 CString
	CString str;
	str.Format(TEXT("x = %d,,,,y =%d"), point.x, point.y);

	MessageBox(str);
}

void MyFrame::OnChar(UINT key, UINT, UINT)
{
	CString str;
	str.Format(TEXT("按下了 %c 键"), key);
	MessageBox(str);
}

void MyFrame::OnPaint()
{
	CPaintDC dc(this);

	dc.TextOutA(300, 300, TEXT("hahahaha"));

	//画椭圆
	dc.Ellipse(10,10,100,100);
	dc.Rectangle(100, 100, 200, 200);
}

注意:若运行以上代码,必须右键项目—>属性—>配置属性—>常规,将项目默认值中MFC的使用设置为在共享DLL中使用MFC
执行流程:
1、程序开始时,先实例化应用程序对象(有且只有一个)
2、执行程序的入口函数InitInstance()
3、给框架类MyFrame对象动态分配空间(自动调用它的构造函数),在其构造函数内部,通过CWnd::Create创建窗口
4、框架类对象显示窗口CWnd::ShowWindow
5、框架类对象更新窗口CWnd::UpdateWindow
6、保存框架类对象指针CWinThread::m_pMainWnd

2.3 一些主要的类

CFrameWnd 框架窗口类

CFrameWnd是从CWnd(窗口基类)派生出来的。CFrameWnd模仿框架窗口行为,我们可以把框架窗口作为顶层窗口看待,它是应用程序与外部世界的主要接口。
可以调用类中的CWnd::Create或CWnd::CreateEX函数创建

virtual BOOL Create(
	LPCTSTR lpszClassName,
	LPCTSTR lpszWindowName,
	DWORD dwStyle,
	const RECT& rect,
	CWnd* pParentWnd,
	UINT nID,
	CCreateContext* pContext = NULL
	);

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

CWinApp应用程序类

MFC应用程序的核心就是基于CWinApp类的应用程序对象。CWinApp提供了消息循环来检索消息并将消息调度给应用程序窗口。它还包括可被覆盖的、用来自定义应用程序行为的主要虚函数。
一个MFC程序可以有且仅有一个应用程序对象,此对象必须声明为在全局范围内有效,以便它在程序开始时即在内存中被实例化。

2.4 MFC消息映射

消息映射是一个将消息和成员函数相互关联的表。例如在上面的程序中,我们用鼠标左键点击窗口获取坐标,会产生一个WM_LBUTTONDOWN消息,在MFC的映射入口表达形式为ON_WM_LBUTTONDOWN( ),其对应的函数为afx_msg void OnLButtonDown( UINT, CPoint ),在点击鼠标左键后,就会调用该函数,并执行相应的操作。
消息映射添加到一个类中有如下几步:
1)所操作类中,声明消息映射宏。
2)通过放置标识消息的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的调用之间处理消息。
3)对应消息处理函数分别在类中声明,在类外写详细的步骤。

对于上面的例子,要对按下的键盘中的键值进行观察
1)在MFC.h头文件中的CFrameWnd窗口框架类中声明宏

class MyFrame: public CFrameWnd //窗口框架类
{
public:
	MyFrame();

	//声明宏 提供消息映射机制
	DECLARE_MESSAGE_MAP()
};

2)对于键盘的按下,产生的消息类型为WM_CHAR(),则先设置消息映射分界宏,向其中加入该消息在MFC中相应的映射入口。
在MFC.cpp中加入分界宏和映射入口

//分界宏
BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd)//(子类名,父类名)
	
	ON_WM_CHAR()  //键盘
	
END_MESSAGE_MAP()

3)在类中声明消息映射的函数原型,即在MFC.h中的CFrameWnd窗口框架类中声明函数原型:

class MyFrame: public CFrameWnd //窗口框架类
{
public:
	MyFrame();

	//声明宏 提供消息映射机制
	DECLARE_MESSAGE_MAP()
	//声明消息映射的函数原型
	afx_msg void OnChar(UINT, UINT, UINT);

};

在类外进行具体实现,即在MFC.cpp中实现函数原型的具体操作:

void MyFrame::OnChar(UINT key, UINT, UINT)
{
	CString str;//CString类型为MFC中的字符串类型
	str.Format(TEXT("按下了 %c 键"), key);//xx.Format为CString类型的初始化方式
	MessageBox(str);
}

你可能感兴趣的:(MFC学习)