Windows消息机制学习笔记(二)—— 窗口与线程

要点回顾

一个GUI线程对应一个消息队列

本篇要解决的问题:

  1. 消息从哪里来?
  2. 消息到哪里去?
  3. 谁来做这些事情?

消息从哪里来?

1)Spy++ 捕捉消息:鼠标消息、键盘消息
2)程序发送消息

实验一:Spy++捕获消息

1)编译并运行以下代码

#include <windows.h>

LRESULT CALLBACK WindowProc(
	IN HWND hwnd,
	IN UINT uMsg,
	IN WPARAM wParam,
	IN LPARAM lParam
){
	
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


int APIENTRY WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nShowCmd
){
	//窗口的类名
	TCHAR className[] = "My First Window";

	//创建一个自己的窗口
	WNDCLASS wndclass = {0};
	wndclass.hbrBackground = (HBRUSH)COLOR_MENU;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.lpszClassName = className;
	wndclass.hInstance = hInstance;

	//注册
	RegisterClass(&wndclass);

	//创建窗口
	HWND hwnd = CreateWindow(
		className,
		TEXT("我的第一个窗口"),
		WS_OVERLAPPEDWINDOW,
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL);

	if(hwnd == NULL)
		return 0;

	//显示窗口
	ShowWindow(hwnd, SW_SHOW);

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

	return 0;
}

运行结果
在这里插入图片描述
2)使用spy++定位窗口
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述3)在窗口中进行任意操作,例如鼠标移动,鼠标点击,键盘敲击等,观察消息列表
在这里插入图片描述

实验二:消息捕获

1)进程A运行以下代码

#include <windows.h>

LRESULT CALLBACK WindowProc(
	IN HWND hwnd,
	IN UINT uMsg,
	IN WPARAM wParam,
	IN LPARAM lParam
){
	switch(uMsg)
	{
	case 0x401:
		MessageBoxA(NULL, "接收到消息", "新消息", MB_OK);
		return false;
	}
	
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


int APIENTRY WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nShowCmd
){
	//窗口的类名
	TCHAR className[] = "My First Window";

	//创建一个自己的窗口
	WNDCLASS wndclass = {0};
	wndclass.hbrBackground = (HBRUSH)COLOR_MENU;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.lpszClassName = className;
	wndclass.hInstance = hInstance;

	//注册
	RegisterClass(&wndclass);

	//创建窗口
	HWND hwnd = CreateWindow(
		className,
		TEXT("我的第一个窗口"),
		WS_OVERLAPPEDWINDOW,
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL);

	if(hwnd == NULL)
		return 0;

	//显示窗口
	ShowWindow(hwnd, SW_SHOW);

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

	return 0;
}

2)进程B运行以下代码

#include <stdio.h>
#include <windows.h>

int main()
{
	HWND hwnd = FindWindow("My First Window", "我的第一个窗口");
	SendMessage(hwnd, 0x401, 0, 0);

	return 0;
}

执行结果
在这里插入图片描述

消息到哪里去?

描述

  1. 当我们使用鼠标某个窗口进行点击与滑动时,都会产生一个消息,消息会进入当前窗口对应线程的消息队列中
  2. 当我们编写程序时,并不会去特地启动两个线程去监控鼠标和键盘,w32k.sys负责了这个事情
    在这里插入图片描述

当初始化w32k.sys这个模块时,会调用一个叫做InitInputImpl的函数
这个函数会启动两个线程,分别用来监控鼠标和键盘,这两个线程都是0环的线程
平时我们的电脑遭遇“死机”时,常常是屏幕动不了,鼠标还能动,这正式由于鼠标是有一个独立的线程在监控它的行动

//FROM ReactOS v3.12
InitInputImpl(VOID)
{
   ...
   Status = PsCreateSystemThread(&RawInputThreadHandle,		//监控鼠标
                                 THREAD_ALL_ACCESS,
                                 NULL,
                                 NULL,
                                 &RawInputThreadId,
                                 RawInputThreadMain,
                                 NULL);
   if (!NT_SUCCESS(Status))
   {
      DPRINT1("Win32K: Failed to create raw thread.\n");
   }

   Status = PsCreateSystemThread(&KeyboardThreadHandle,		//监控键盘
                                 THREAD_ALL_ACCESS,
                                 NULL,
                                 NULL,
                                 &KeyboardThreadId,
                                 KeyboardThreadMain,
                                 NULL);
   if (!NT_SUCCESS(Status))
   {
      DPRINT1("Win32K: Failed to create keyboard thread.\n");
   }
   ...
}

思考w32k.sys如何区分用户点击的是哪个窗口/消息要去哪个线程

窗口在哪?

当调用CreateWindow时,该函数实际上是一个宏,其CreateWindowA实际对应CreateWindowExA函数,CreateWindowW对应CreateWindowExW函数,可在编辑器中跟踪观察

实验:分析CreateWindowExW

函数位于user32.dll
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述总结:窗口在0环被画出(由w32k.sys实现)

窗口对象

描述

  1. 每个窗口对应一个WINDOW_OBJECT结构体,位于0环,包含当前窗口所有信息
  2. 除了大窗口之外,窗口中的每个控件也都是一个窗口,也分别对应一个WINDOW_OBJECT结构体
  3. 一个窗口内包含着许多个窗口,按钮,对话框也都属于窗口,属于同一个线程
  4. 一个线程可以包含多个窗口,但每个窗口只能属于一个线程
//FROM ReactOS v3.12
typedef struct _WINDOW_OBJECT
{
	...
	PWND Wnd;			//包含大量窗口信息
	PTHREADINFO pti;	//线程
	...
}
//FROM ReactOS v3.12
typedef struct _WND
{
    ...
    /* Style. */
    DWORD style;		//包含窗口风格/后一个窗口/前一个窗口/父窗口/子窗口等信息
    ...
} WND, *PWND;

消息进入窗口消息队列的过程

  1. 当使用鼠标在某个窗口上点击时,鼠标监控线程检测到点击的窗口对象
  2. 根据窗口对象成员,找到窗口对应线程
  3. 将消息放入该线程的消息队列中

注意:在3环得到的窗口的句柄只是一个索引(参考句柄表章节)

总结

  1. 窗口在0环创建
  2. 窗口句柄是全局的
  3. 一个线程可以使用多个窗口,但每个窗口只能属于一个线程
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值