文章目录
- Windows 32 API是一组用于编写Windows应用程序的底层API。API代表应用程序编程接口,它们是使用应用程序和操作系统之间进行交互的接口。
- Windows 32 API包含了大量的函数和数据类型,用于控制Windows操作系统的各种方面,包括图形用户界面、文件系统访问、进程管理、线程管理、网络编程等等。这些API还提供了许多各种各样的工具,如事件处理、剪贴板操作、消息传递等。
开发工具和库
编译工具
- 编译器CL.EXE 将源代码编译成目标代码 .obj。
- 链接器LINK.EXE 将目标代码、库链接生成最终文件。
- 资源编译器RC.EXE (.rc)将资源编译,最终通过链接器存入最终文件。
库和头文件
- Windows库
- kernel32.dll - 提供了核心的API,例如进程、线程、内存管理等等。
- user32.dll - 提供了窗口、消息等API。
- gdi32.dll - 绘图相关的API。
- 头文件
- windows.h - 所有windows头文件的集合
- windef.h - windows数据类型
- winbase.h - kernel32的API
- wingdi.h - gdi32的API
- winuser.h - user32的API
- winnt.h - UNICODE字符集支持
消息基础
消息的概念和作用
- 消息组成( windows平台下)
typedef struct tagMSG {
HWND hwnd; // 窗口句柄
UINT message; // 消息ID
WPARAM wParam; // 消息的两个参数
LPARAM lParam;
DWORD time; // 消息产生的时间
POINT pt; // 消息产生时的鼠标位置
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
- 消息的作用
- 当系统通知窗口工作时,就采用消息的方式派发给窗口。
窗口处理函数
- 每个窗口都必须具有窗口处理函数。
LRESULT CALLBACK WindowProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息ID
WPARAM wParam, // 消息参数
LPARAM lParam // 消息参数
);
- 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。在窗口处理函数中,不处理的消息,使用缺省窗口处理函数。例如:DefWindowProc。
消息相关函数
// 从调用线程的消息队列中检索消息。(阻塞函数)
BOOL GetMessage(
[out] LPMSG lpMsg, // 存放获取到的消息BUFF
[in, optional] HWND hWnd, // 窗口句柄
[in] UINT wMsgFilterMin, // 获取消息的最小ID
[in] UINT wMsgFilterMax // 获取消息的最大ID
);
lpMsg - 当获取到消息后,将消息的参数存放到MSG结构中。
hWnd - 获取到hWnd所指定窗口的消息。
wMsgFilterMin和wMsgFilterMax - 只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围。
PeekMessage - 以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续伐码。
BOOL PeekMessage(
LPMSG lpMsg, // 指向接收消息信息的MSG结构的长指针
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg // 指定如何处理消息
);
// 将虚拟键消息转换为字符消息。
BOOL TranslateMessage(
[in] const MSG *lpMsg // 要翻译的消息地址
);
检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行。
LRESULT DispatchMessage(
[in] const MSG *lpMsg // 要派发的消息
);
将消息派发到该消息所属窗口的窗口处理函数上。(调用WndProc函数)
LRESULT SendMessage( // 发送消息,会等候消息处理的结果。
[in] HWND hWnd, // 消息发送的目的窗口
[in] UINT Msg, // 消息ID
[in] WPARAM wParam,
[in] LPARAM lParam
);
BOOL PostMessage( // 投递消息,消息发出后立刻返回,不等候消息执行结果。
[in, optional] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
BOOL InvalidateRect( // 窗口无效区域:需要重新绘制的区域。
[in] HWND hWnd,
[in] const RECT *lpRect,
[in] BOOL bErase // 重绘前是否先擦除
);
发送消息
- 系统消息:ID范围0~0x03FF
由系统定义好的消息,可以在程序中直接使用。 - 用户自定义消息:ID范围0x0400~0x7FFF(31743)。
由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理。
自定义消息宏:WM_USER
常用消息宏
WM_DESTORY
产生时间: 窗口被销毁时的消息。
附带信息: wParam: 没用
lParam: 没用
一般用法: 常用于在窗口被销毁之前,做相应的善后处理,例如资源、内存等。
WM_SYSCOMMAND
产生时间: 当用户从窗口菜单(以前称为系统或控制菜单)中选择命令或用户选择最大化按钮、最小化按钮、恢复按钮或关闭按钮时,窗口会收到此消息。
附带信息: wParam: 具体点击的位置,例如关闭SC_CLOSE等。
lParam: 鼠标光标的位置。
LOWORD(lParam) ; //水平位置
HIWORD(lParam); //垂直位置
WM_CREATE
产生时间: 在窗口创建成功但还未显示时。
附带信息: wParam: 为0。
lParam: 为CREATESTRUCT类型的指针。
通过这个指针可以获取CreatWindowEx中的全部12个参数的信息。
一般用法: 常用于初始化窗口的参数、资源等等,包括创建子窗口等。
WM_SIZE
产生时间: 在窗口的大小发生变化后。
附带信息: wParam: 窗口大小变化的原因。
lParam:窗口变化后的大小。
LoWORD(IParam) //变化后的宽度
HIWORD(IParam) //变化后的高度
一般用法∶常用于窗口大小变化后,调整窗口内各个部分的布局。
WM_QUIT
产生时间: 程序员发送。
附带信息: wParam: PostQuitMessage函数传递的参数。
lParam: 0。
一般用法: 用于结束消息循环,当GetMessage收到这个消息后,会返的FALSE,结束while处理,退出消息循环。
WM_PAINT
产生时间: 当系统或其他应用程序请求绘制应用程序窗口的一部分时,将发送WM_PAINT消息。
附带信息: wParam: 0。
lParam: 0。
专职用法: 用于绘图。
消息分类
键盘消息
键盘消息分类
- WM_KEYDOWN:按键被按下时产生
- WM_KEYUP:按键被放开时产生
- WM_SYSKEYDOWN:系统键按下时产生比如 ALT、F10
- WM_SYSKEYUP:系统键放开时产生
- 附带信息∶
- WPARAM - 按键的 Virtual Key
- LPARAM - 按键的参数,例如按下次数
- 附带信息∶
字符消息
- TranslateMessage 在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息。
- 附带信息:
- WPARAM - 输入的字符的ASCII字符编码值
- LPARAM - 按键的相关参数
- 附带信息:
鼠标消息
鼠标消息分类
- 基本鼠标消息
- WM_LBUTTONDOWN - 鼠标左键按下
- WM_LBUTTONUP - 鼠标左键抬起
- WM_RBUTTONDOWN - 鼠标右键按下
- WM_RBUTTONUP - 鼠标右键抬起
- WM_MOUSEMOVE - 鼠标移动消息
附带信息:
WPARAM: 其他按键的状态,例如Ctrl/Shift等。
lPARAM: 鼠标的位置,窗口客户区坐标系。
LOWORD(lParam): X坐标位置
HIWORD(lParam): Y坐标位置
一般情况鼠标按下/抬起成对出现。在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息。
- 双击消息
- WM_LBUTTONDBLCLK - 鼠标左键双击
- WM_RBUTTONDBLCLK - 鼠标右键双击
附带信息(同上)
消息产生顺序
以左键双击为例:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
使用时需要在注册窗口类的时候添加CS_DBLCLKS风格。
- 滚轮消息
- WM_MOUSEWHEEL - 鼠标滚轮消息
附带信息:
wParam:
LOWORD - 其他按键的状态。
HIWORD – 滚轮的偏移量,通过正负值表示表示滚动方向。(正: 向前滚动,负: 向后滚动)
lParam:鼠标当前的位置,屏幕坐标系。
LOWORD - X坐标
HIWORD - Y坐标
使用:通过偏移量,获取滚动的方向和距离。
定时器消息
定时器消息介绍
- 产生时间:在程序中创建定时器,当到达时间间隔时,GetMessage会向程序发送一个WM_TIMER消息。定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000毫秒到达消息。
- 附带信息:
- wParam:定时器ID
- lParam:定时器处理函数的指针
创建和销毁定时器
- 创建定时器
UINT_PTR SetTimer(
[in, optional] HWND hWnd, // 与计时器相关联的窗口句柄
[in] UINT_PTR nIDEvent, // 非零计时器标识符
[in] UINT uElapse, // 时间间隔
[in, optional] TIMERPROC lpTimerFunc
);
创建成功,返回非0。
- 销毁定时器
BOOL KillTimer(
[in, optional] HWND hWnd,
[in] UINT_PTR uIDEvent // 非零计时器标识符
);
消息队列
消息队列概念
- 消息队列是用于存放消息的队列。
- 消息在队列中先入先出。
- 所有窗口程序都具有消息队列。
- 程序可以从队列中获取消息。
消息队列的分类
- 系统消息队列 - 由系统维护的消息队列。存放系统产生的消息,例如鼠标、键盘等。
- 程序消息队列 - 属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。
消息和消息队列的关系
- 关系
- 当鼠标、键盘产生消息时,会将消息存放到系统消息队列。
- 系统会根据存放的消息,找到对应程序的消息队列。
- 将消息投递到程序的消息队列中。
- 根据消息和消息队列之间使用关系,将消息分成两类∶
- 队列消息 – 消息的发送和获取,都是通过消息队列完成。
- 非队列消息 – 消息的发送和获取,是直接调用消息的窗口处理完成。
- 队列消息:消息发送后,首先放入系统队列,然后通过消息循环,从本进程队列当中获取。
- GetMessage - 从消息队列中获取消息
- PostMessage - 将消息投递到消息队列
- 常见队列消息:WM_PAINT、键盘、鼠标、定时器
- 非队列消息:消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
- SendMessage - 直接将消息发送给窗口的处理函数
- 常见消息:WM_CREATE、WM_SIZE等。
资源
菜单资源
菜单资源使用
- 添加菜单资源
- 加载菜单资源
- 注册窗口类时设置菜单
- 创建窗口传参设置菜单
- 在主窗口WM_CREATE消息中利用SetMenu函数设置菜单
HMENU LoadMenuW( // 加载菜单资源
[in, optional] HINSTANCE hInstance, // 加载的菜单资源的模块句柄
[in] LPCWSTR lpMenuName // 菜单资源的名称 或 资源标识符
);
WM_COMMAND
:当用户从菜单中选择命令项、控件向其父窗口发送通知消息或翻译加速键击时,将发送此消息。
- 附带信息
Message Source | wParam (high word) | wParam (low word) | lParam |
---|---|---|---|
Menu | 0 | Menu identifier (IDM_*) | 0 |
Accelerator | 1 | Accelerator identifier (IDM_*) | 0 |
Control | Control-defined notification code | Control identifier | Handle to the control window |
图标资源
- 添加资源:注意图标的大小,一个图标文件中,可以有多个不同大小的图标。
// 加载
HICON LoadIconW(
[in, optional] HINSTANCE hInstance, // 模块实例的句柄
[in] LPCTSTR lpIconName // 要加载的图标资源的名称
);
- 设置:注册窗口类时
光标资源
- 添加光标的资源
- 光标的大小默认是32x32像素,每个光标有HotSpot,是当前鼠标的热点。
- 加载资源
HCURSOR LoadCursorA(
[in, optional] HINSTANCE hInstance, // 模块实例的句柄
[in] LPCSTR lpCursorName // 要加载的光标资源的名称
);
- 设置资源
- 在注册窗口时,设置光标
- 使用SetCursor设置光标
HCURSOR SetCursor(
[in, optional] HCURSOR hCursor // 光标的句柄
);
WM_SETCURSOR
产生时间:如果鼠标导致光标在窗口内移动且未捕获鼠标输入,则发送到窗口。
附带信息:wParam - 当前使用的光标句柄
lParam - LOWORD:当前区域的代码(Hit-Test code ) 例:HTCLIENT / HTCAPTION...
- HIWORD:当前鼠标消息ID
字符串资源
- 添加字符串资源
- 添加字符串表,在表中增加字符串
- 字符串资源的使用
int LoadStringA(
[in, optional] HINSTANCE hInstance, // 资源模块句柄
[in] UINT uID, // 字符串ID
[out] LPSTR lpBuffer, // 存放字符串BUFF
[in] int cchBufferMax // 字符串BUFF长度
); // 成功返回字符串长度,失败返回0
加速键资源
- 添加:资源添力加加速键表,增加命令ID对应的加速键。
- 使用
HACCEL LoadAcceleratorsA( // 加载指定的加速器表
[in, optional] HINSTANCE hInstance, // 模块的句柄
[in] LPCSTR lpTableName // 快捷键表的名称
);
int TranslateAcceleratorA( // 翻译加速键
[in] HWND hWnd, // 要翻译其消息的窗口的句柄
[in] HACCEL hAccTable, // 加速器表的句柄
[in] LPMSG lpMsg // 一个指向MSG结构的指针
); // 成功返回非0,否则返回0
绘图
绘图基础
- 绘图设备 DC(Device Context):是一种对象,设定了图形输出的特性与属性。
- HDC:绘图设备句柄
- GDI(Graphics Device Interface):图形设备接口
- 颜色的使用
- COLORREF - 实际DWORD
- 赋值使用RGB宏
- 例:nColor = RGB(255, 0, 0);
- 获取RGB值
- GetRValue/GetGValue/GetBValue
绘图对象
画笔
- 画笔的作用
- 颜色、线型、线粗
- 画笔的使用
1、创建画笔
HPEN CreatePen(
[in] int iStyle, // 画笔的样式
[in] int cWidth, // 画笔的线宽
[in] COLORREF color // 颜色
);
注:PS_SOILD - 实心线,可以支持多个像素宽其他线型只能是一个像素宽。
2、将画笔应用到DC中
HGDIOBJ SelectObject(
[in] HDC hdc, // 绘图设备句柄
[in] HGDIOBJ h // GDI绘图对象句柄,画笔、画刷等句柄
); // 返回原来的GDI绘图对象句柄
**注意保存原来DC当中画笔。**
3、绘图
4、取出DC中的画笔
将原来的画笔,使用SelectObject函数,放入到设备DC中,就会将我们创建的画笔取出。
5、释放画笔
BOOL DeleteObject(
[in] HGDIOBJ ho
);
只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出。
画刷
- 画刷的作用
- 画刷-封闭图形的填充的颜色、图案
- HBRUSH - 画刷句柄
- 画刷的使用
1、创建画刷
CreateSolidBrush - 创建实心画刷
CreateHatchBrush - 创建纹理画刷
2、将画刷应用到DC中
SelectObject
3、绘图
4、将画刷从DC中取出
SelectObject
5、删除画刷
DeleteObject
- 其他
- 可以使用GetStockObject函数获取系统维护的画刷、画笔等。如果不使用画刷填充,需要使用NULL_BRUSH参数,获取不填充的画刷。GetStockObject返回的画刷不需要DeleteObject。
文本绘制
- 文字的绘制
TextOut() // 将文字绘制在指定坐标位置。
int DrawText( // 在指定的矩形中绘制格式化文本
[in] HDC hdc, // DC句柄
[in, out] LPCTSTR lpchText, // 字符串
[in] int cchText, // 字符数量
[in, out] LPRECT lprc, // 绘制文字的矩形框
[in] UINT format // 绘制的方式
);
- 文字颜色和背景
- 文字颜色:SetTextColor()
- 文字背景色:SetBkColor()
- 文字背景模式:SetBkMode()
字体
- 字体相关
- Window常用的字体为TrueType格式的字体文件
- 字体名–标识字体类型
- HFONT-字体句柄
- Window常用的字体为TrueType格式的字体文件
- 字体使用
1、创建字体
HFONT CreateFontA(
[in] int cHeight, // 字体高度
[in] int cWidth,
[in] int cEscapement, // 字符串倾斜角度
[in] int cOrientation, // 字符旋转角度
[in] int cWeight, // 字体粗细
[in] DWORD bItalic,
[in] DWORD bUnderline,
[in] DWORD bStrikeOut, // 删除线
[in] DWORD iCharSet, // 字符集
[in] DWORD iOutPrecision, // 输出精度
[in] DWORD iClipPrecision, // 剪切精度
[in] DWORD iQuality, // 输出质量
[in] DWORD iPitchAndFamily, // 匹配字体
[in] LPCSTR pszFaceName // 字体名称(看字体文件的第一行)
);
2、应用字体到DC SelectObject
3、绘制文字 DrawText/TextOut
4、取出字体 SelectObject
5、删除字体 DeleteObject
位图
位图绘制
- 位图相关
- 光栅图形 – 记录图像中每一点的颜色等信息。
- 矢量图形 – 记录图像算法、绘图指令等。
- HBITMAP-位图句柄
- 位图的使用
1、在资源中添加位图资源
2、从资源中加载位图LoadBitmap
3、创建一个与当前DC相匹配的DC (内存DC)
HDC CreateCompatibleDC(
[in] HDC hdc // 当前DC句柄,可以为NULL(使用屏幕DC)
);
4、将位图放入匹配的内存DC中 SelectObject
5、成像 (1:1)
BOOL BitBlt( // 将对应于像素矩形的颜色数据从指定的源设备上下文到目标设备上下文
HDC hdcDest, // 目的DC
int nXDest, // 目的左上X坐标
int nYDest,
int nWidth, // 目的宽度
int nHeight,
HDC hdcSrc, // 源DC
int nXSrc, // 源左上X坐标
int nYSrc,
DWORD dwRop // 成像方法
);
BOOL StretchBlt( // 将位图从源矩形复制到目标矩形,必要时拉伸或压缩位图以适合目标矩形的尺寸。
HDC hdcDest, // 目标设备上下文的句柄
int nXOriginDest, // 目标矩形左上角的 x 坐标
int nYOriginDest,
int nWidthDest, // 目标矩形的宽度
int nHeightDest,
HDC hdcSrc, // 源设备上下文的句柄
int nXOriginSrc, // 源矩形左上角的 x 坐标
int nYOriginSrc,
int nWidthSrc, // 源矩形的宽度
int nHeightSrc,
DWORD dwRop // 成像方法
);
6、取出位图 SelectObject
7、释放位图 DeleteObject
8、释放匹配的DC DeleteDC
对话框
普通窗口和对话框窗口的区别:
普通窗口:自定义函数 调用 缺省函数
WndProc(…) {
…
DefWindowProc(…);
}
对话框窗口:缺省函数 调用 自定义函数。
缺省函数(…) {
…
自定义函数(…);
…
}
对话框原理
- 对话框的分类
- 模式对话框 - 当对话框显示时,会禁止其他窗口和用户交互操作。
- 无模式对话框 - 在对话框显示后,其他窗口仍然可以和用户交互操作。
- 对话框基本使用
1、对话框窗口处理函数
2、注册窗口类(不使用)
3、创建对话框
4、对话框的关闭 - 对话框处理函数(并非真正的对话框窗口处理函数)
INT CALLBACK DialogProc(
HWND hwndDlg, // 窗口句柄
UINT uMsg, // 消息ID
WPARAM wParam,
LPARAM lParam
);
返回TRUE - 缺省处理函数不需要处理。
返回FALSE - 交给缺省处理函数处理。
不需要调用缺省对话框窗口处理函数。
模式对话框
- 创建对话框
INT DialogBox(
HINSTANCE hInstance, // 应用程序实例句柄
LPCTSTR lpTemplate, // 对话框资源ID
HWND hWndParent, // 对话框父窗口
DLGPROC lpDialogFunc // 自定义函数
);
DialogBox是一个阻塞函数,只有当对话框关闭后,才会返回,继续执行后续代码。
返回值是通过EndDialog设置。
- 对话框的关闭
BOOL EndDialog(
[in] HWND hDlg, // 要销毁的对话框的句柄
[in] INT_PTR nResult // 关闭的返回值
);
关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow等函数。nResult是DialogBox函数退出时的返回值。
- 对话框的消息
- WM_INITDIALOG - 对话框创建之后显示之前,通知对话框窗口处理函数,可以完成自己的初始化相关的操作。
无模式对话框
- 创建对话框
HWND CreateDialog(
HINSTANCE hInstance,
LPCTSTR lpTemplate,
HWND hWndParent,
DLGPROC lpDialogFunc
);
非阻塞函数,创建成功返回窗口句柄,需要使用ShowWindow函数显示对话框。
- 对话框的关闭
- 关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框。
动静态库
静态库
动态库
线程
线程基础
- Windows线程是可以执行的代码的实例。(系统是以线程为单位调度程序。一个程序当中可以有多个线程,实现多任务的处理。
- Windows线程的特点:
- 线程都具有1个ID
- 每个线程都具有自己的内存栈
- 同一进程中的线程使用同一个地址空间。
- 线程的调度:
将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。
线程轮询:线程A->线程B->线程A…
创建线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性
SIZE_T dwStackSize, // 线程栈的大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数的函数地址
__drv_aliasesMem LPVOID lpParameter, // 传递给线程处理函数的参数
DWORD dwCreationFlags, // 线程的创建方式
LPDWORD lpThreadId // 创建成功,返回线程的ID
);
// 线程处理函数
DWORD WINAPI ThreadProc(
LPVOID lpParameter // 创建线程时,传递给线程的参数
);
线程挂起/销毁
// 挂起
DWORD SuspendThread(
HANDLE hThread
);
// 唤醒
DWORD ResumeThread(
HANDLE hThread
);
// 结束指定线程
BOOL TerminateThread(
[in, out] HANDLE hThread, // 要终止的线程的句柄
[in] DWORD dwExitCode // 线程的退出代码
);
// 结束函数所在的线程
void ExitThread(
[in] DWORD dwExitCode
);
线程相关操作
// 获取当前线程的ID
DWORD GetCurrentThreadId()
// 获取当前线程的句柄
HANDLE GetCurrentThread()
// 等候单个句柄有信号
DWORD WaitForSingleObject(
[in] HANDLE hHandle, // 句柄BUFF的地址
[in] DWORD dwMilliseconds // 等候时间
);
// 同时等候多个句柄有信号
DWORD WaitForMultipleObjects(
[in] DWORD nCount, // 句柄数量
[in] const HANDLE *lpHandles, // 对象句柄数组
[in] BOOL bWaitAll, // 等候方式
[in] DWORD dwMilliseconds // 等候时间
);
bWaitAll - 等候方式:
TRUE - 表示所有句柄都有信号,才结束等候。
FASLE - 表示句柄中只要有1个有信号,就结束等候。
当线程处于执行状态时,线程句柄无信号;当线程结束后,线程句柄有信号。