窗口的本质
- 前置基础
1)虚拟内存
● 每个进程 4GB 虚拟地址:0x00000000 ~ 0xFFFFFFFF
● 用户空间:0x00000000 ~ 0x7FFFFFFF(低 2GB,进程私有)
● 内核空间:0x80000000 ~ 0xFFFFFFFF(高 2GB,所有进程共享)
2)模块划分
● 内核模块(内核空间)
○ ntoskrnl.exe:进程、线程、内存管理核心
○ win32k.sys:图形界面、窗口消息、GDI 底层实现,所有进程共享
● 用户模块(用户空间,每个进程独立)
○ kernel32.dll:进程线程内存基础 API
○ user32.dll:窗口、控件、消息
○ gdi32.dll:图形设备接口
3)API 调用链路
用户程序 → DLL 接口 (kernel32/user32/gdi32) → 内核模块真正实现 - 句柄对比:HANDLE 与 HWND
| 类型 | 维护者 | 句柄表 | 跨进程 | 生命周期 | 用途 |
|---|---|---|---|---|---|
| HANDLE | 各个内核对象 | 进程私有句柄表 | ❌无效 | 进程关闭自动销毁 | 进程、线程、互斥体、事件等内核对象 |
| HWND | win32k.sys | 全局共享句柄表 | ✅全系统有效 | 窗口销毁才失效 | 窗口、控件 |
重点:HWND 是全局的,A 进程拿到的窗口句柄,B 进程可以直接使用
4. GDI 绘图核心概念
1)设备对象 (HWND) 绘图目标,代表 “画在哪里”,可以是窗口、桌面;Spy++ 可以抓取 HWND
2)设备上下文 HDC(Device Context)
内存绘图缓冲区,所有 GDI 绘图必须依赖 HDC
● 获取:HDC GetDC(HWND hwnd)
● 释放:ReleaseDC(HWND hwnd, HDC hdc)
● 双缓冲:先内存绘图,再输出到屏幕
3)GDI 图形对象(选入 DC 使用)
| 对象 | 作用 | 创建函数 |
|---|---|---|
| Pen 画笔 | 线条颜色、粗细、线型 | CreatePen() |
| Brush 画刷 | 区域填充颜色图案 | CreateSolidBrush() / GetStockObject() |
| Font 字体 | 文字样式 | CreateFont() |
| Bitmap 位图 | 图片 | CreateBitmap() |
SelectObject(HDC hdc, HGDIOBJ hgdiobj)
● 将 GDI 对象选入 DC,返回旧对象句柄
● DC 同一时刻每类对象只能有一个生效
● 绘图完成要恢复旧对象,销毁自定义 GDI 对象,否则发生GDI 资源泄漏
5. 画笔 CreatePen
HPENCreatePen(intstyle,intwidth,COLORREF color);线型:
○ PS_SOLID 实线
○ PS_DASH虚线、PS_DOT点线、PS_DASHDOT点划线
虚线 / 点线,宽度必须≤1
宽度:逻辑像素,填 0 代表强制 1 像素
颜色:COLORREF RGB(r,g,b),r/g/b 范围 0‑255(FF)
示例:CreatePen(PS_SOLID,5,RGB(255,0,0)); //5像素红色实线
6. GetStockObject 获取系统预置 GDI 对象
HGDIOBJGetStockObject(intnIndex);常用参数:
● BLACK_BRUSH黑色画刷、WHITE_BRUSH白色画刷
● NULL_BRUSH / HOLLOW_BRUSH空心画刷
● DC_BRUSH可动态改色的画刷,配合SetDCBrushColor()
系统预置对象不需要必须 DeleteObject,写 DeleteObject 也无害
7. 关键绘图 API
1)画线
MoveToEx(hdc,x1,y1,NULL);//设置起点LineTo(hdc,x2,y2);//画到终点2)绘制矩形
Rectangle(hdc,left,top,right,bottom);● 边框:由当前 DC 的画笔控制
● 内部填充:由当前 DC 的画刷控制
坐标原点 (0,0):绘图目标窗口左上角
标准 GDI 绘图完整示例代码
#include<windows.h>voidDrawDemo(HWND hwnd){//1.获取DCHDC hdc=GetDC(hwnd);//2.创建5像素红色实线画笔HPEN hPen=CreatePen(PS_SOLID,5,RGB(255,0,0));//3.获取系统可变色画刷HBRUSH hBrush=(HBRUSH)GetStockObject(DC_BRUSH);//4.把对象选入DC,保存旧对象!HGDIOBJ oldPen=SelectObject(hdc,hPen);HGDIOBJ oldBrush=SelectObject(hdc,hBrush);//修改画刷颜色为黄色SetDCBrushColor(hdc,RGB(255,255,0));//绘制400*400矩形Rectangle(hdc,0,0,400,400);//========资源恢复与释放【重中之重】========//恢复DC原来的对象SelectObject(hdc,oldPen);SelectObject(hdc,oldBrush);//销毁自定义GDI对象DeleteObject(hPen);//GetStockObject获取的对象可不删除//DeleteObject(hBrush);//释放设备上下文ReleaseDC(hwnd,hdc);}注意:直接在窗口外部调用绘图,窗口被遮挡、最小化再还原时,绘制内容会消失
原因:窗口内容是实时绘制,窗口重绘消息WM_PAINT到来会刷新界面,真正绘图要写在 WM_PAINT 消息内部
总结
- 窗口本质:窗口没有永久保存图像,所有像素都是 GDI 实时绘制出来。窗口被遮挡后内容丢失,需要响应WM_PAINT重绘
- HDC用完必须ReleaseDC;自己 Create 出来的 GDI 对象必须DeleteObject,否则 GDI 泄漏
- SelectObject一定要保存旧对象,结束前还原回 DC,不能直接选入新对象不管
- HWND全局有效;普通HANDLE只属于本进程
- 画笔控制线条边框;画刷控制填充,画矩形、椭圆这类闭合图形两者同时生效