当前位置:   article > 正文

消息机制(GUI线程讲解)

gui线程

前奏

首先我们来画一个窗口:
在这里插入图片描述

窗口代码

#include<Windows.h>
#include <stdio.h>
#define _WIN32_WINNT 0x0500

typedef struct _Color {

	DWORD r;
	DWORD g;
	DWORD b;
}Color;

typedef struct _WindowClass {
	DWORD x;
	DWORD y;
	DWORD width;
	DWORD hight;
	Color color;
}WindowClass;

void PaintWindwos(HDC hdc, WindowClass* p) {
	HBRUSH hBrush;
	hBrush = (HBRUSH)GetStockObject(DC_BRUSH);
	
	SelectObject(hdc, hBrush);//画刷
	SetDCBrushColor(hdc, RGB(p->color.r, p->color.g, p->color.b));

	MoveToEx(hdc, p->x, p->y, NULL);

	LineTo(hdc, p->x + p->width, p->y);
	LineTo(hdc, p->x + p->width, p->y + p->hight);
	LineTo(hdc, p->x, p->y + p->hight);
	LineTo(hdc, p->x, p->y);
	Rectangle(hdc, p->x, p->y, p->x + p->width, p->y + p->width + 1);

	DeleteObject(hBrush);
}
void main() {
	char cMessage;   //消息
	HWND  hwnd;		//画在哪
	HDC  hdc;		//显卡缓存

	//设置窗口参数:长宽高之类的
	WindowClass wClass;
	wClass.x = 0;
	wClass.y = 0;
	wClass.width = 800;
	wClass.hight = 400;
	wClass.color.r = 0xEF;
	wClass.color.g = 0xEB;
	wClass.color.b = 0xDE;

	//画在哪

	hwnd = GetDesktopWindow();
	//hwnd=FindWindow("notepad.exe",NULL);

	//获取DC设备句柄:可以把DC理解成显卡缓存
	hdc = GetWindowDC(hwnd);
	
	cMessage = getchar();
	for(;;) {
		//画窗口
		PaintWindwos(hdc, &wClass);
	
		//接收消息
		
		switch (cMessage)
		{
		case 'a':
			wClass.color.r += 0x10;
			wClass.color.g += 0x10;
			wClass.color.b += 0x10;
			break;
		case 'b':
			wClass.color.r += 0x20;
			wClass.color.g += 0x20;
			wClass.color.b += 0x20;
			break;

		default:
			break;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

在这里插入图片描述
在这里呢我们可以通过控制台去控制窗口的颜色,那么接下来让我们一起来看看原理:

你能回答这些问题吗?

  1. 什么是窗口句柄?在哪里?有什么用?
  2. 什么是消息?什么是消息队列?消息队列在哪 ?
  3. 什么是窗口过程?窗口过程是由谁调用的?没有消息循环窗口过程会执行吗?
  4. 为什么要有w32k.sys这个模块?
  5. 为什么只有使用图形界面的程序才可以访问KeServiceDescriptorTableShadow?
  6. 界面“卡死”的时候为什么鼠标还可以动?

消息队列

在这里插入图片描述

消息队列在何处呢?

在这里插入图片描述

首先我们假设把消息队列放在用户空间(3环),那么谁又来往用户空间的消息队列存储这些东西呢?
最好的解决方案就是找一个专用进程来监听鼠标和键盘等,再来进行判断是属于哪个进程的消息队列,最后来进行消息分发(Linux解决方案)
弊端:涉及了跨进程通信问题,专用进程传给其它进程。大量时间都花在跨进程。

那么Windows如何解决的呢?

首先普及一下:
kernel32.dll ----------> ntoskrnl.exe(进程,线程,内存管
user32.dll gdi32.dll -----------> win32k.sys(图形界面,消息管理)

Windows已经画好的界面(windows提供的)GUI编程 user32.dll
不用Windows提供的那些界面 GDI编程 gdi32.dll

窗口句柄HWN

针对窗口的句柄表,只有一个,(放在内核中)表是全局的,所有窗口共用的

HDC   hdc;
HPEN hpen;
1.设备对象	画在哪
hwnd    =(HWND)0x0003543;
2.获取设备对象上下文
hdc=GetDC(hwnd);
3.创建画笔	设置线条属性
hpen=CreatePen(PS_S0LID,5,RGB(0xFF,00,00));
4.关联
SelectObject(hdc,hpen);
5.开始画
LineTo(hdc,400,400);//gdi32.dll
6.释放资源
DeleteObject(hpen);
ReleaseDC(hwnd,hdc);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

它把消息队列放在(内核空间)0环中,

微软的解决方案:GUI线程
GUI(自己使用微软提供的窗口函数,比如CreateWindow,CreateButton等创建的图形界面,这种API就叫做GUI)

GDI(如果觉得微软提供的窗口不符合自己所需条件,需要自己画,所用的那些API就叫GDI)

重点:

1.当线程刚创建的时候,都是普通线程:
Thread.ServiceTable -->KeServiceDescriptorTable(SSDT表)
ServiceTable中存储了一张表,叫做系统服务表

2.当线程第一次调用(与图形界面相关的模块 )Win32k.sys时(只要试图正常调用Win32k中的任何一个函数),会调用一个函数:PsConvertToGuiThread(把普通线程转换为GUI线程)

PsConvertToGuiThread主要做几件事:
a.扩充内核栈,必须换成64KB 的大内核栈,因为普通内核栈只有12KB大小。
b.创建一个包含消息队列的结构体,并挂到KTHREAD上(也就是KTHREAD中的Win32Thread)
c.Thread.ServiceTable–>KeServiceDescriptorShadow(SSDTShadow表)
d.把需要的内存数据映射到本进程空间

(SSDT表中只引用了一张表,只有一张系统服务表,也就是ntoskernel,win32k的第二张表它没有。但是SSDTShaow表中既包含了ntoskernel中的函数,又包含了Win32k(与图形界面相关的)中的函数)
解释:
如果是一个普通线程的话,那么ETHRED结构体中成员KTHREAD结构体中有一个Win32Thread成员它是为空(未使用图形界面相关的API)
如果是一个GUI线程的话,那么ETHRED结构体中成员KTHREAD有一个Win32Thread成员它是一个地址值,指向一个结构体,指向一个_THREADINFO结构体,这个结构体里面又有一个成员,存放着消息队列
在这里插入图片描述

在这里插入图片描述

总结:

  1. 消息队列存储在0环,通过KTHREAD.Win32Thread可以找到
  2. 并不是所有线程都要消息队列,只有GUI线程才有消息队列
  3. 一个GUI线程对应1个消息队列
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/854122
推荐阅读
相关标签
  

闽ICP备14008679号