windows窗口底层实现

现在在windows上实现窗口程序,大部分都是使用qt和mfc来实现的,这两种框架都是基于c++来实现的,也就是说都是通过面向对象的思想来实现的,但在windows底层都是通过面向过程的思想来实现的,所以今天来分享一下如何通过windows底层的方法来实现一个简单的窗口。

windows底层实现窗口主要分为以下6个步骤:

1、设计窗口

2、注册窗口

3、创建窗口

4、显示更新

5、循环获取消息队列

6、处理消息

首先需要创建一个窗口结构体WNDCLASS,然后需要对窗口的属性进行设置,具体如下

	//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);//图标 
	wc.hInstance = hInstance;//应用程序实力句柄 传入WinMain的形参										
	wc.lpfnWndProc = WindowProc;//回调函数 窗口过程
	wc.lpszClassName = TEXT("WIN");//指定窗口类名称
	wc.lpszMenuName = NULL;//菜单名称
	wc.style = 0;//显示风格 0代表默认风格

第二步:RegisterClass(&wc); 注册窗口

第三步:创建窗口

	//3、创建窗口
	/*
	lpClassName,  类名
	lpWindowName, 标题名
	dwStyle,      风格
	x,            显示坐标
	y,\
	nWidth,       宽高
	nHeight, 
	hWndParent,   父窗口 NULL
	hMenu,        菜单 NULL
	hInstance,    实例句柄
	lpParam       附加值 鼠标
	*/
	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);

第五步:利用死循环一直读取消息队列中的消息

底层中的消息传递如图所示

先由用户触发消息后传递给操作系统,操作系统再将消息放进消息队列中,应用程序一直获取消息队列中的消息,然后再将消息传递给操作系统,操作系统再去调用对应的回调函数(窗口过程)。完成消息的相应。

消息传递主要利用MSG结构体,代码如下

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

		//翻译消息
		TranslateMessage(&msg);
		//不为false
		//分发消息
		DispatchMessage(&msg);
	}

 这边为什么会有个翻译消息的函数呢?

主要是由于用户操作如果是正常点击一个按键的话是不会有问题,但是如果当用户同时点击两个按键,例如同时按下ctrl+c,这时候正常操作应该是复制操作,但是如果不进行翻译的话,就会被系统识别成按下ctrl键和c,明显不是想要的结果,所以需要先进行翻译消息后再分发消息给操作系统。

最后是回调函数,即处理消息

//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] = { 0 };
		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 world"),strlen("hello world"));
		EndPaint(hwnd, &ps);
		break;
	}
	}
	//返回值用默认处理方式
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

这边需要注意的是,如果不进行PostQuitMessage(0);退出消息操作的话,程序会一直存在,因为程序还在处理消息的处理中。

接下来是完整代码的分享

#include <windows.h>

//程序入口函数

//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] = { 0 };
		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 world"),strlen("hello world"));
		EndPaint(hwnd, &ps);
		break;
	}
	}
	//返回值用默认处理方式
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
//WINAPI 代表__stdcall 参数的传递顺序,从右到左 依次入栈
//并且在函数返回前 清空堆栈
int WINAPI WinMain(
	HINSTANCE hInstance,           //应用程序实例句柄
	HINSTANCE hPrevInstance,       //上一个应用程序句柄
	LPSTR lpCmdLine,               //char *argv[]
	int nShowCmd)                  //显示命令
{
	//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);//图标 
	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,      风格
	x,            显示坐标
	y,\
	nWidth,       宽高
	nHeight, 
	hWndParent,   父窗口 NULL
	hMenu,        菜单 NULL
	hInstance,    实例句柄
	lpParam       附加值 鼠标
	*/
	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 (1)
	{
		/*
		_Out_ LPMSG lpMsg,       消息
		_In_opt_ HWND hWnd,      捕获窗口 填NULL代表捕获所有的窗口
		_In_ UINT wMsgFilterMin, 最小和最大的过滤的消息 一般填入0
		_In_ UINT wMsgFilterMax);填0代表捕获所有消息
		*/
		if (GetMessage(&msg,NULL,0,0) == FALSE)
		{
			break;
		}

		//翻译消息
		TranslateMessage(&msg);
		//不为false
		//分发消息
		DispatchMessage(&msg);
	}
	//6、处理消息(窗口过程)

	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值