学习:窗口创建以及消息处理

Windows 窗口的诞生过程:

定义窗口类结构(WNDCLASS) -> 注册窗口类(RegisterClass) -> 创建窗口(CreateWindow) -> 显示窗口(ShowWindow) -> 更新窗口(UpdateWindow) -> 消息循环(GetMessage -> TranslateMessage ->DispatchMessage)

WNDCLASS结构:

Windows 的窗口总是基于窗口类来创建的,窗口类同时确定了处理窗口消息的窗口过程(回调函数)。

结构原型:

typedef struct tagWNDCLASSW {
    UINT        style;  //指定窗口类型,各种“类风格”(详见下方↓)可以使用按位或操作符组合起来
    WNDPROC     lpfnWndProc; //指定窗口过程(必须是回调函数)
    int         cbClsExtra; //预留的额外空间,一般为 0
    int         cbWndExtra; //预留的额外空间,一般为 0
    HINSTANCE   hInstance; //应用程序的实例句柄
    HICON       hIcon; //为所有基于该窗口类的窗口设定一个图标
    HCURSOR     hCursor; //为所有基于该窗口类的窗口设定一个鼠标指针
    HBRUSH      hbrBackground; //指定窗口背景色
    LPCWSTR     lpszMenuName; //指定窗口菜单
    LPCWSTR     lpszClassName; //指定窗口类名
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;

在创建应用程序窗口之前,必须调用 RegisterClass 函数来注册窗口类。该函数只需要一个参数,即指向 WNDCLASS 窗口类的指针。因为 WNDCLASS 类包含了窗口所拥有的基本属性。

更多的WNDCLASS结构参考:https://fishc.com.cn/forum.php?mod=viewthread&tid=47123&extra=page%3D1%26filter%3Dtypeid%26typeid%3D420


问题:当一个鼠标键盘按下的时候,之后的过程是怎么样的?

个人理解:

知识点:

1、所有的句柄真正存储在内核区,所以线程,窗口对象其实都是存在内核区的,也就是ring0

2、一个线程对应多个窗口对象,而一个窗口对象只能对应一个线程

过程:

1、当CreateWindow进行窗口创建之后,其中伴随产生了线程对象,线程对象中就会进行存储消息队列

2、当键盘按下之后,操作系统获取了该消息,经过一系列的分析之后会找到对应的窗口对象,并且把该消息封装到了MSG这个结构体中放到该窗口对象的线程对象中的消息队列中

3、GetMessage会把该窗口对象中的线程对象中的消息MSG结构体都取出来

4、MSG会先经过TranslateMessage处理,其作用比如在处理按键的时候,将接收到的十六进制转换为字符码char的时候就可以派上用场了

5、然后再经过DispatchMessageDispatchMessage会拿到当前MSG中对应句柄进去ring0ring0通过MSG中对应的句柄调用对应的窗口回调函数CALLBACK FUNC处理

示例代码:

#include<windows.h>

//全局变量声明
HINSTANCE hinst;

//函数声明
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //HINSTANCE为应用程序的句柄
	WNDCLASSEX wcx; //窗口类
	HWND hwnd; //窗口句柄
	MSG msg;  //消息
	BOOL fGotMessage; //是否成功获取消息
	hinst = hinstance; //用来保存当前的应用程序的句柄
	static TCHAR szAppName[] = TEXT("MyWindows");

	//对创建的窗口类进行填充相应的数据结构
	wcx.cbSize = sizeof(wcx); //cxSize转到定义为 该类型为UINT
	wcx.style = CS_HREDRAW | CS_VREDRAW; //样式 大小改变时 重新进行绘制
	wcx.lpfnWndProc = MainWndProc; // 窗口消息处理函数
	wcx.cbWndExtra = 0;	// 不使用类内存
	wcx.cbClsExtra = 0;	// 不使用窗口内存
	wcx.hInstance = hinstance;  //所属的应用程序的实例句柄
	wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); //图标: 默认 指定一个和类相关的图标资源句柄,如果没有指定就用默认的。
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);	 // 光标:默认
	wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 画刷背景:WHITE_BRUSH
	wcx.lpszMenuName = NULL; // 菜单:无
	wcx.lpszClassName = szAppName; // 窗口类的名称
	wcx.hIconSm = (HICON)LoadImage(hinstance, //hIconSm指定一个和类相关的小的图标资源句柄,如果是空,系统会根据hIcon的图标来生成一个合适大小的图标来作为和类相关的小的图标资源句柄
		MAKEINTRESOURCE(5),
		IMAGE_ICON,
		GetSystemMetrics(SM_CXSMICON),
		GetSystemMetrics(SM_CXSMICON),
		LR_DEFAULTCOLOR);

	if (!RegisterClassEx(&wcx)) { //创建窗口类
		return -1;
	}

	//调用CreateWindow API
	hwnd = CreateWindow(szAppName, //窗口类名称
		TEXT("First Window"),  //窗口标题
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, //水平位置:默认
		CW_USEDEFAULT, //垂直位置:默认
		CW_USEDEFAULT, //宽度位置:默认
		CW_USEDEFAULT, //高度位置:默认
		(HWND)NULL, // 父窗口:无
		(HMENU)NULL, //菜单:使用窗口类的菜单
		hinstance,  //应用程序实例句柄
		(LPVOID)NULL); //窗口创建时数据:无

	if (!hwnd) {  //创建窗口失败的处理
		return -1;
	}

	//显示窗口
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	//消息循环,作用就是将我们在窗口上产生的msg进行TranslateMessage解析然后再进行DispatchMessage传输给窗口消息处理函数进行处理
	while ((fGotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && fGotMessage != -1) {
		TranslateMessage(&msg); //翻译作用,比如 在处理按键的时候,将接收到的十六进制转换为字符码char的时候就可以派上用场了
		DispatchMessage(&msg); //传输,根据对应的窗口HWND,找到对应的窗口过程函数,比如这里定义的MainWndProc函数进行处理

		//当DispatchMessage之后,内核中将该句柄对应的窗口过程函数进行调用
	}

	return msg.wParam;
}

/*
MainWndProc
功能:窗口消息处理函数 对所有的消息都使用默认处理函数
*/

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { //这里接收的hwnd,uMsg,wParam,lParam 
	//也就是MSG结构体中的消息,umsg是动作 wParam lParam保存了该动作中的详细信息的,hwnd为该句柄

	switch (uMsg) { //switch对产生的消息进行处理
	case WM_LBUTTONDOWN:
		//MessageBox(NULL, L"hello click", L"hello", MB_OK);
		MessageBox(hwnd, L"hello click", L"hello", MB_OK); //如果这里传入的句柄为NULL,那么就能无限产生,我们必须要点击确定才能产生下一个,所以需要获取当前窗口的句
		return 0;
	case WM_DESTROY: //当进行关闭处理时候 进行ExitThread 结束进程操作
		PostQuitMessage(0);
		return 0;
	case WM_CHAR:
		wchar_t szchar[100];
		wsprintf(szchar,TEXT("%c\n"), wParam,lParam); //格式化字符串
		OutputDebugString(szchar); //调试的时候进行输出格式化的字符串szchar
		return 0;

	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam); //不关心的操作都给windows自己的窗口消息处理函数进行处理
	}

}

效果图:

posted @ 2019-11-20 20:20  zpchcbd  阅读(407)  评论(0编辑  收藏  举报