消息机制(GUI线程讲解)
文章目錄
- 前奏
- 窗口代碼
- 你能回答這些問題嗎?
- 消息隊(duì)列:
- 消息隊(duì)列在何處呢?
- 那么Windows如何解決的呢?
- 重點(diǎn):
- 總結(jié):
前奏
首先我們來畫一個(gè)窗口:
窗口代碼
#include<Windows.h> #include <stdio.h> #define _WIN32_WINNT 0x0500typedef 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; //顯卡緩存//設(shè)置窗口參數(shù):長(zhǎng)寬高之類的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設(shè)備句柄:可以把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;}} }
在這里呢我們可以通過控制臺(tái)去控制窗口的顏色,那么接下來讓我們一起來看看原理:
你能回答這些問題嗎?
消息隊(duì)列:
消息隊(duì)列在何處呢?
首先我們假設(shè)把消息隊(duì)列放在用戶空間(3環(huán)),那么誰又來往用戶空間的消息隊(duì)列存儲(chǔ)這些東西呢?
最好的解決方案就是找一個(gè)專用進(jìn)程來監(jiān)聽鼠標(biāo)和鍵盤等,再來進(jìn)行判斷是屬于哪個(gè)進(jìn)程的消息隊(duì)列,最后來進(jìn)行消息分發(fā)(Linux解決方案)
弊端:涉及了跨進(jìn)程通信問題,專用進(jìn)程傳給其它進(jìn)程。大量時(shí)間都花在跨進(jìn)程。
那么Windows如何解決的呢?
首先普及一下:
kernel32.dll ----------> ntoskrnl.exe(進(jìn)程,線程,內(nèi)存管
user32.dll gdi32.dll -----------> win32k.sys(圖形界面,消息管理)
Windows已經(jīng)畫好的界面(windows提供的)GUI編程 user32.dll
不用Windows提供的那些界面 GDI編程 gdi32.dll
窗口句柄HWN
針對(duì)窗口的句柄表,只有一個(gè),(放在內(nèi)核中)表是全局的,所有窗口共用的
HDC hdc; HPEN hpen; 1.設(shè)備對(duì)象 畫在哪 hwnd =(HWND)0x0003543; 2.獲取設(shè)備對(duì)象上下文 hdc=GetDC(hwnd); 3.創(chuàng)建畫筆 設(shè)置線條屬性 hpen=CreatePen(PS_S0LID,5,RGB(0xFF,00,00)); 4.關(guān)聯(lián) SelectObject(hdc,hpen); 5.開始畫 LineTo(hdc,400,400);//gdi32.dll 6.釋放資源 DeleteObject(hpen); ReleaseDC(hwnd,hdc);它把消息隊(duì)列放在(內(nèi)核空間)0環(huán)中,
微軟的解決方案:GUI線程
GUI(自己使用微軟提供的窗口函數(shù),比如CreateWindow,CreateButton等創(chuàng)建的圖形界面,這種API就叫做GUI)
GDI(如果覺得微軟提供的窗口不符合自己所需條件,需要自己畫,所用的那些API就叫GDI)
重點(diǎn):
1.當(dāng)線程剛創(chuàng)建的時(shí)候,都是普通線程:
Thread.ServiceTable -->KeServiceDescriptorTable(SSDT表)
ServiceTable中存儲(chǔ)了一張表,叫做系統(tǒng)服務(wù)表
2.當(dāng)線程第一次調(diào)用(與圖形界面相關(guān)的模塊 )Win32k.sys時(shí)(只要試圖正常調(diào)用Win32k中的任何一個(gè)函數(shù)),會(huì)調(diào)用一個(gè)函數(shù):PsConvertToGuiThread(把普通線程轉(zhuǎn)換為GUI線程)
PsConvertToGuiThread主要做幾件事:
a.擴(kuò)充內(nèi)核棧,必須換成64KB 的大內(nèi)核棧,因?yàn)槠胀▋?nèi)核棧只有12KB大小。
b.創(chuàng)建一個(gè)包含消息隊(duì)列的結(jié)構(gòu)體,并掛到KTHREAD上(也就是KTHREAD中的Win32Thread)
c.Thread.ServiceTable–>KeServiceDescriptorShadow(SSDTShadow表)
d.把需要的內(nèi)存數(shù)據(jù)映射到本進(jìn)程空間
(SSDT表中只引用了一張表,只有一張系統(tǒng)服務(wù)表,也就是ntoskernel,win32k的第二張表它沒有。但是SSDTShaow表中既包含了ntoskernel中的函數(shù),又包含了Win32k(與圖形界面相關(guān)的)中的函數(shù))
解釋:
如果是一個(gè)普通線程的話,那么ETHRED結(jié)構(gòu)體中成員KTHREAD結(jié)構(gòu)體中有一個(gè)Win32Thread成員它是為空(未使用圖形界面相關(guān)的API)
如果是一個(gè)GUI線程的話,那么ETHRED結(jié)構(gòu)體中成員KTHREAD有一個(gè)Win32Thread成員它是一個(gè)地址值,指向一個(gè)結(jié)構(gòu)體,指向一個(gè)_THREADINFO結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體里面又有一個(gè)成員,存放著消息隊(duì)列
總結(jié):
總結(jié)
以上是生活随笔為你收集整理的消息机制(GUI线程讲解)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。