Win32 高级编程
Win32 高级编程
应用程序分类
应用程序分类
- 控制台程序Console
- DOS程序,本身没有窗口,通过Windows DOS窗口执行
- 窗口程序
- 拥有自己的窗口,可以与用户交互
- 库程序
- 存放代码、数据的程序,执行文件可以从中取出代码执行和获取数据
- 静态库程序:扩展名LIB,在编译链接程序时,将代码放入到执行文件中
- 动态库程序:扩展名DLL,在执行文件执行时从中获取代码
应用程序对比
- 入口函数
- 控制台程序—— main
- 窗口程序—— WinMain
- 动态库程序—— DllMain
- 静态库程序—— 无入口函数
- 文件存在方式
- 控制台程序、窗口程序—— EXE文件
- 动态库程序—— DLL文件
- 静态库文件—— LIB文件
开发工具和库
编译工具
- 编译器CL.EXE:将源代码编译成目标代码 .obj
- 链接器 LINK.EXE:将目标代码、库链接生成最终文件
- 资源编译器 RC.EXE:(.rc)将资源编译,最终通过链接器存入最终文件
库和头文件
- Windows库
- kernel32.dll —— 提供了核心的API,例如进程、线程、内存管理等
- user32.dll ——提供了窗口、消息等API
- gdi32.dll —— 绘图相关的API
- 路径:C:\Windows\System32
- 头文件
- windows.h —— 所有windows头文件的集合
- windef.h —— windows数据类型
- winbase.h —— kernel32的API
- wingdi.h —— gdi32的API
- winuser.h —— user32的API
- winnt.h —— UNICODE字符集支持
相关函数
int WINAPI WinMain(
HINSTANCE hInstance, // 当前程序的实例句柄
HINSTANCE hPrevInstance, // 当前程序前一个实例句柄
LPSTR lpCmdLine, // 命令行参数字符串
int nCmdShow // 窗口的显示方式
);
int MessageBox(
HWND hWnd, // 父窗口句柄
LPCTSTR lpText, // 显示在消息框中的文字
LPCTSTR lpCaption, // 显示在标题栏中的文字
UINT uType // 消息框中的按钮
); // 返回点击的按钮的ID
程序编译过程
- 编译环境准备
- VCVARS32.BAT
- 编译程序 —— CL
- CL.EXE -c xxx.c
- 链接程序 —— LINK
- LINK.EXE xxx.obj xxx.lib
- 执行
CL.EXE
.c/.cpp ---------------->|
| LINK.EXE
|----------------------> .exe
RC.EXE |
.rc -------------------->|
第一个Windows窗口
窗口创建过程
- 定义WinMain函数
- 定义窗口处理函数(自定义,处理消息)
- 注册窗口类(向操作系统写入一些数据)
- 创建窗口(内存中创建窗口)
- 显示窗口(绘制窗口的图像)
- 消息循环(获取/翻译/派发消息)
- 消息处理
注册窗口类
窗口类的概念
- 窗口类是包含了窗口的各种参数信息的数据解构
- 每个窗口都具有窗口类,基于窗口类创建窗口
- 每个窗口类都具有一个名称,使用前必须注册到系统
窗口类的分类
- 系统窗口类
- 系统已经定义好的窗口类,所有应用程序都可以直接使用
- 应用程序全局窗口类
- 由用户自己定义,当前应用程序所有模块都可以使用
- 应用程序据部窗口类
- 由用户自己定义,当前应用程序中本模块可以使用
系统窗口类
- 不需要注册,直接使用窗口类即可,系统已经注册好了
- 例如:
- 按钮 —— BUTTON
- 编辑框 —— EDIT
全局及局部窗口类
- 注册窗口类的函数
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass // 窗口类的数据
); // 注册成功后,返回一个数字标识
- 注册窗口类的结构体
typeof struct _WNDCLASS {
UNIT style; // 窗口类的风格
WNDPROC lpfnWndProc; // 窗口处理函数
int cbClsExtra; // 窗口类的附加数据buff的大小
int cbWndExtra; // 窗口的附加数据buff大小
HINSTANCE hInstance; // 当前模块的实例句柄
HICON hIcon; // 窗口图标句柄
HCURSOR hCursor; // 鼠标的句柄
HBRUSH hbrBackground; // 绘制窗口背景的画刷句柄
LPCTSTR lpszMenuName; // 窗口菜单的资源ID字符串
LPCTSTR lpszClassName; // 窗口类的名称
} WNDCLASS, *PWNDCLASS;
-
style窗口类风格
- 应用程序全局窗口类注册,需要在窗口类的风格中增加CS_GLOBALCLASS
- 例如:
WNDCLASS wce = {0}; wce.style = ...|CS_GLOBALCLASS;- 应用程序窗口类:在注册窗口类时,不添加CS_GLABALCALSS风格
- CS_HREDRAW —— 当窗口水平变化时,窗口重新绘制
- CS_VREDRAW —— 当窗口垂直变化时,窗口重新绘制
- CS_DBLCLKS —— 允许窗口接收鼠标双击
- CS_NOCLOSE —— 窗口没有关闭按钮
窗口创建
消息的概念和作用
- CreateWindow/CreateWindowEx
HWND CreateWindowEx(
DWORD dwEsStyle, // 窗口的扩展风格
LPCTSTR lpClassName, // 已经注册的窗口类名称
LPCTSTR lpWindowName, // 窗口标题栏名称
DWORD dwStyle, // 窗口的基本风格
int x, // 窗口左上角水平坐标位置
int y, // 窗口左上角垂直坐标位置
int nWidth, // 窗口的宽度
int nHeight, // 窗口的高度
HWND hWndParant, // 窗口的父窗口句柄
HMENU hMenu, // 窗口菜单句柄
HINSTANCE hInstance, // 应用程序实例句柄
LPVOID lpParam // 窗口创建时附加参数
); // 创建成狗返回窗口句柄
- 系统根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2,如果未找到执行3
- 比较局部窗口类与创建时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回。如果不相等,继续执行3.
- 在应用程序全局窗口类,如果找到,执行4,如果未找到执行5
- 使用找到的窗口类的信息,创建窗口返回
- 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败
创建子窗口
- 创建时要设置父窗口句柄
- 创建风格要增加WS_CHILD | WS_VISIBLE
消息基础
消息的概念和作用
- 消息的组成(windows平台下)
- 窗口句柄
- 消息ID
- 消息的两个参数(两个附带信息)
- 消息产生的时间
- 消息产生时的鼠标位置
- 消息的作用
- 当系统通知窗口工作时,就采用消息的方式派发给窗口
窗口处理函数
- 每个窗口都必须具有窗口处理函数
LPESULT CALLBACK WindowProc(
HWND hWnd, // 窗口的句柄
UNIT uMsg, // 消息ID
WPARAN wParam, // 消息参数
LPARAM lParam // 消息参数
);
- 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。在窗口处理函数中,不处理的消息,使用缺省窗口处理函数。例如:
DefWindowProc
浅谈消息相关函数
- GetMessage —— 获取消息
BOOL GetMessage(
LPMSG lpMsg, // 存放获取到的消息BUFF
HWND hWnd, // 窗口句柄
UINT wMsgFilterMin, // 获取消息的最小ID
UINT wMsgFilterMax // 获取消息的最大ID
);
lpMsg—— 当获取到消息后,将消息的参数存放到MSG结构中hWnd—— 获取到hWnd所指定窗口的消息wMsgFilterMin和wMsgFilterMax—— 只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围
- TranslateMessage —— 翻译消息,将按键消息翻译成字符消息
BOOL TranslateMessage(
CONST MSG * lpMsg // 要翻译的消息地址
);
- 检查消息是否是按键消息,如果不是按键消息,不做任何处理,继续执行
- DispatchMessage —— 派发消息
LRESULT DispatchMessage(
CONST MSG *lpMsg // 要派发的消息
);
- 将消息派发到该消息所属窗口的窗口处理函数上
创建消息
WM_DESTORY
- 产生时间:窗口被销毁时的消息
- 附带信息:
wParam:为0lParam:为0
- 一般用法:常用在窗口被销毁之前,做相应的善后处理,例如资源、内存等等
WM_SYSCOMMAND
- 产生时间:当点击窗口的最大化、最小化、关闭等
- 附带信息:
wParam:具体点击的位置,例如关闭SC_CLOSE等lParam:鼠标光标的位置LOWORD(lParam); // 水平位置HIWORD(lParam); // 垂直位置
- 一般用法:常用在窗口关闭时,提示用户处理
WM_CREATE
- 产生时间:在窗口创建成功但还未显示时
- 附带信息:
wParam:为0lParam:为CREATESTRUCT类型的指针。通过这个指针可以获取CreateWindowEx中的全部12个参数的信息。
- 一般用法:常用于初始化窗口的参数、资源等等,包括创建子窗口等
WM_SIZE
- 产生时间:在窗口的大小发生变化后
- 附带信息:
wParam:窗口大小变化的原因lParam:窗口变化后的大小LOWORD(lParam); // 变化后的宽度HIWORD(lParam); // 变化后的高度
- 一般用法:常用于窗口大小变化后,调整窗口内各个部分的布局
WM_QUIT
- 产生时间:程序员发送
- 附带信息:
wParam:PostMessage函数传递的参数lParam:0
- 一般用法:用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环
消息循环原理
消息循环的阻塞
- GetMessage —— 从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,会等候下一条消息。
- PeekMessage —— 以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。
BOOL PeekMessage(
LPMSG lpMsg, // message ingormation
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // 溢出标识,PM_REMOVE/PM_NOREMOVE
);
发送消息
- SendMessage —— 发送消息,会等候消息处理的结果
- PostMessage —— 投递消息,消息发出后立刻返回,不等侯消息执行结果
BOOL SendMessage/PostMessage(
HWND hWnd, // 消息发送的目的窗口
UINT Msg, // 消息ID
WPARAM wParam, // 消息参数
LPARAM lParam // 消息参数
);
消息分类
- 系统消息 —— ID范围 0 ~ 0x03FF
- 由系统定义好的消息,可以在程序中直接使用
- 用户自定义消息 —— ID范围 0x0400 ~ 0x7FFF (31743)
- 由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理。
- 自定义消息宏:WM_USER
消息队列
消息队列的概念
- 消息队列是用于存放消息的队列
- 消息在队列中先入先出
- 所有窗口程序都具有消息队列
- 程序可以从消息队列中获取消息
消息队列的分类
- 系统消息队列 —— 由系统维护的消息队列,存放系统产生的消息,例如鼠标、键盘等
- 程序消息队列 —— 属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护
消息和消息队列的关系
- 消息和消息队列的关系
- 当鼠标、键盘产生消息时,会将消息存放到系统消息队列
- 系统会根据存放的消息,找到对应程序的消息队列
- 将消息投递到程序的消息队列中
- 根据消息和消息队列之间的使用关系,将消息分成两类:
- 队列消息:消息的发送和获取,都是通过消息队列完成
- 非队列消息:消息的发送和获取,是直接调用消息的窗口处理完成
- 队列消息:消息发送后,首先放入系统消息队列,然后通过消息循环,从队列当中获取
- GetMessage:从消息队列中获取消息
- PostMessage:将消息投递到消息队列
- 常见队列消息:WM_PAINT、键盘、鼠标、定时器
- 非队列消息:消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
- SendMessage:直接将消息发送给窗口处理函数,并等候处理结果
- 常见非队列消息:WM_CRATE、WM_SIZE等
深谈GetMessage
- 在程序(线程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND, ID范围),不满足条件就不会取出消息,否则从队列取出消息返回
- 如果程序(线程)消息队列没有消息,向系统消息队列获取属于本程序的消息,如果系统队列的当前消息属于本程序,系统会将消息转发到程序的消息队列中
- 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要重新绘制的区域,产生WM_PAINT消息,取得消息返回处理
- 如果没有重新绘制的区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行
- 如果没有到时的定时器,整理程序的资源、内存等等
- GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权
- 注意:GetMessage如果获取到的是WM_QUIT,函数会返回FALSE
WM_PAINT消息
- 产生时间:当窗口需要绘制的时候
- 附带信息:
wParam:0lParam:0
- 专职用法:用于绘图
- 窗口无效区域:需要重新绘制的区域
BOOL InvalidateRect(
HWND hWnd, // 窗口句柄
CONST RECT* lpRect, // 区域的矩形坐标
BOOL bErase // 重绘前是否擦除
);
-
消息处理步骤
- 开始绘图
HDC BeginPaint( HWND hWnd, // 绘图窗口 LPPAINTSTRUCT lpPaint // 绘图参数的BUFF ); // 返回绘图设备句柄HDC- 正式绘图
- 结束绘图
HDC EndPaint( HWND hWnd, // 绘图窗口 CONST PAINTSTRUCT *lpPaint // 绘图参数的指针BeginPaint返回 );
键盘消息
键盘消息分类
- WM_KEYDOWN:按键按下时产生
- WM_KEYUP:按键被放开时产生
- WM_SYSKEYDOWN:系统键按下时产生,比如ALT、F10
- WM_SYSKEYUP:系统键放开时产生
- 附带信息:
wParam:按键的Virtual KeylParam:按键的参数,例如按下次数
字符消息(WM_CHAR)
- TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息
- 附带信息:
wParam:输入的字符的ASCII字符编码值lParam:按键的相关参数
鼠标消息
鼠标消息分类
- 基本鼠标消息
- WM_LBUTTONDOWN:鼠标左键按下
- WM_LBUTTONUP:鼠标左键抬起
- WM_RBUTTONDOWN:鼠标右键按下
- WM_RBUTTONUP:鼠标右键抬起
- WM_MOUSEMOVE:鼠标移动消息
- 双击消息
- WM_LBUTTONDBLCLK:鼠标左键双击
- WM_RBUTTONDBLCLK:鼠标右键双击
- 滚轮消息
- WM_MOUSEWHEEL:鼠标滚轮消息
鼠标基本消息
- 附带信息:
wParam:其他按键的状态,例如Ctrl/Shift等lParam:鼠标的位置,窗口客户区坐标系LOWORD(lParam); // X坐标位置HIWORD(lParam); // Y坐标位置
- 一般情况鼠标按下/抬起成对出现。在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息
鼠标双击消息
- 附带信息:
wParam:其他按键的状态,例如Ctrl/Shift等lParam:鼠标的位置,窗口客户区坐标系LOWORD(lParam); // X坐标位置HIWORD(lParam); // Y坐标位置
- 消息产生顺序(以左键双击为例)
- WM_LBUTTONDOWN
- WM_LBUTTONUP
- WM_LBUTTONDBLCLK
- WM_LBUTTONUP
- 使用时需要在注册窗口类的时候添加CS_DBLCLKS风格
鼠标滚轮消息
- 附带信息:
wParam:LOWORD(wParam);// 其他按键的状态HIWORD(wParam); // 滚轮的偏移量,通过正负值表示滚动方向- 正:向前滚动 负:向后滚动
lParam:鼠标当前的位置,屏幕坐标系LOWORD(lParam); // X坐标HIWORD(lParam); // Y坐标
- 使用:通过偏移量,获取滚动的方向和距离
定时器消息
定时器消息介绍
- 产生时间:在程序中创建定时器,当到达时间间隔时,定时器会向程序发送一个WM_TIMER消息。定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非100毫秒到达消息。
- 附带信息:
wParam:定时器IDlParam:定时器处理函数的指针
创建销毁定时器
- 创建定时器
UINT_PTR SetTimer(
HWND hWnd, // 定时器窗口句柄
UINT_PTR nIDEvent, // 定时器ID
UINT uElapse, // 时间间隔
TIMERPROC lpTimerFunc // 定时器处理函数指针(一般不使用,为NULL)
); // 创建成功,返回非0
- 关闭定时器
BOOL KillTimer(
HWND hWnd, // 定时器窗口句柄
UINT_PTR uIDEvent // 定时器ID
);
菜单资源
菜单分类
- 窗口的顶层菜单
- 弹出式菜单
- 系统菜单
- HMENU类型表示菜单,ID表示菜单项
资源相关
- 资源脚本文件:
*.rc - 编译器:RC.EXE
CL.EXE
.c/.cpp ---------------->|
| LINK.EXE
|----------------------> .exe
RC.EXE |
.rc -------------------->|
菜单资源的使用
- 添加菜单资源
- 加载菜单资源
- 注册窗口类时设置菜单
- 创建窗口传参设置菜单
- 在主窗口
WM_CREATE消息中利用SetMenu函数设置菜单
// 加载菜单资源
HMENU LoadMenu(
HINSTANCE hInstance, // handle to module
LPCTSTR lpMenuName // menu name or resource identifier
);
命令消息(WM_COMMAND)处理
- 附带信息
wParam:HIWORD:对于菜单为0LOWORD:菜单项的ID
lParam:对于菜单为0
图标资源、光标资源
图标资源
- 添加资源
- 注意图标的大小,一个图标文件中,可以有多个不同大小的图标
- 加载
HICON LoadIcon(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpIconName // name string or resource identifier
); // 成功返回HICON句柄
- 设置
- 注册窗口类
光标资源
- 添加光标的资源
- 光标的大小默认是32X32像素,每个光标只有一个HotSpot,是当前光标的热点
- 加载资源
HCURSOR LoadCursor(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpCursorName // name or resource identifier
); // hInstance可以为NULL,获取系统默认的Cursor
- 设置资源
- 在窗口注册时,设置光标
- 使用
SetCursor设置光标
HCURSOR SetCursor(
HCURSOR hCursor // handle to cursor
);
WM_SETCURSOR消息参数wParam:当前使用的光标句柄lParam:LOWORD:当前区域的代码(Hit-Test code)HTCLIENT/HTCAPTION...
HIWORD:当前鼠标消息ID
字符串资源
- 添加字符串资源
- 添加字符串表,在表中增加字符串
- 字符串资源的使用
int LoadString(
HINSTANCE hInstance, // handle to resource module
UINT uID, // 字符串ID
LPTSTR lpBuffer, // 存放字符串的BUFF
int nBufferMax // 字符串BUFF长度
); // 成功返回字符串长度,失败返回0
加速键资源
-
添加:资源添加加速键表,增加命令ID对应的加速键
-
使用:
- 加载加速键表
HACCEL LoadAccelerators( HINSTANCE hInstance, // handle to module LPCTSTR lpTableName // 加速键表名 ); // 返回加速键表句柄- 翻译加速键
int TranslateAccelerator( HWND hWnd, // 处理消息的窗口句柄 HACCEL hAccelTable, // 加速键表句柄 LPMSG lpMsg // 消息 ); // 如果是加速键,返回非零 -
在
WM_COMMAND中相应消息,消息参数wParam:LOWORD:为1表示加速键,为0表示菜单HIWORD:命令ID
lParam:为0
绘图编程
绘图基础
-
绘图设备DC(Device Context),绘图上下文/绘图描述表
-
HDC:DC句柄,表示绘图设备
-
GDI:Windows graphics device interface(win32提供的绘图API)
-
颜色
- 计算机使用红、绿、蓝
- R:0~255
- G:0~255
- B:0~255
- 每一个点颜色是3个字节24位保存 0 - $$2^{24}-1$$
- 16位:5,5,6
- 32位:8,8,8 绘图或透明度
- 计算机使用红、绿、蓝
-
颜色的使用
- COLORREF:实际是DWORD
- 例如:
COLORREF nColor = 0;
-
赋值使用RGB宏
- 例如:
nColor = RGB(0, 0, 255);
- 例如:
-
获取RGB值:
GetRValue/GetGValue/GetBValue- 例如:
BYTE nRed = GetRValue(nColor);
- 例如:
基本图形绘制
- SetPixel设置指定点的颜色
COLORREF SetPixel(
HDC hdc, // DC句柄
int X, // X坐标
int Y, // Y坐标
COLORREF crColor // 设置的颜色
); // 返回点原来的颜色
- 线的使用(直线、弧线)
MoveToEx:指定窗口当前点LintTo:从窗口当前点到指定点绘制一条直线- 当前点:上一次绘图时的最后一点,初始为(0,0)点
- 封闭图形:能够用画刷填充的图形
Rectangle/Ellipse
GDI绘图对象
画笔
-
画笔的作用
- 线的颜色、线型、线粗
HPEN:画笔句柄
-
画笔的使用
- 创建画笔
HPEN CreatePen( int fnPenStyle, // 画笔的样式 int nWidth, // 画笔的粗细 COLORREF crColor // 画笔的颜色 ); // 创建成功返回句柄PS_SOLID:实心线,可以支持多个像素宽,其他线型只能是一个像素宽- 将画笔应用到DC中
HGDIOBJ SelectObject( HDC hdc, // 绘图设备句柄 HGDIOBJ hgdiobj // GDI绘图对象句柄,画笔句柄 ); // 返回原来的GDI绘图对象句柄- 绘图
- 取出DC中的画笔
- 将原来的画笔,使用SelectObject函数,放入到设备DC中,就会将我们创建的画笔取出
- 释放画笔
BOOL DeleteObject( HGDIOBJ hObject // GDI绘图对象句柄,画笔句柄 ); // 只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出
画刷
- 画刷相关
- 画刷:封闭图形的填充的颜色、图案
HBRUSH:画刷句柄
- 画刷的使用
- 创建画刷
CreateSolidBrush:创建实心画刷CreateHatchBrush:创建纹理画刷
- 将画刷应用到DC中
SelectObject
- 绘图
- 将画刷从DC中取出
SelectObject
- 删除画刷
DeleteObject
- 创建画刷
- 其他
- 可以使用
GetStockObject函数获取系统维护的笔刷、画笔等 - 如果不使用画刷填充,需要使用
NULL_BRUSH参数,获取不填充的笔刷 GetStockObject返回的画刷不需要DeleteObject
- 可以使用
位图
位图绘制
-
位图相关
- 光栅图形:记录图像中每一点的颜色等信息
- 矢量图形:记录图像算法、绘制指令等
- HBITMAP:位图句柄
-
位图的使用
- 在资源中添加位图资源
- 从资源中加载位图LoadBitmap
- 创建一个与当前DC相匹配的DC(内存DC)
HDC CreateCompatibleDC( HDC hdc // 当前DC句柄,可以为NULL(使用屏幕DC) ); // 返回创建好的DC句柄- 将位图放入匹配的DC中——SelectObject
- 成像(1:1)
BOOL BitBlt( HDC hdcDest, // 目的DC int nXDest, // 目的左上X坐标 int nYDest, // 目的左上Y坐标 int nWidth, // 目的宽度 int nHeight, // 目的高度 HDC hdcSrc, // 源DC int nXSrc, // 源左上X坐标 int nYSrc, // 源左上Y坐标 DWORD dwRop // 成像方法 SRCCOPY );- 缩放成像
BOOL StretchBlt( HDC hdcDest, // handle to destination DC int nXOriginDest, // x-coord of destination upper-left corner int nYOriginDest, // y-coord of destination upper-left corner int nWidthDest, // width of destination rectangle int nHeightDest, // height of destiantion redtangle HDC hdxSrc, // handle to source DC int nXOriginSrc, // x-coord of source upper-left corner int nYOriginSrc, // y-coord of source upper-left corner int nWidthSrc, // width of source rectangle int nHeightSrc, // height of source redtangle DWORD dwRop // raster operation code );- 取出位图——
SelectObject - 释放位图——
DeleteObject - 释放匹配的DC——
DeleteDC
文本绘制
- 文字的绘制
TextOut:将文字绘制在指定坐标位置
int DrawText(
HDC hdc, // DC句柄
LPCTSTR lpString, //字符串
int nCount, // 字符数量
LPRECT lpRect, // 绘制文字的矩形框
UINT uFormat // 绘制的方式
); // DT_VCENTR/DT_BOTTOM只适用于DT_SINGLELINE,和DT_WORDBREAK冲突
- 文字颜色和背景
- 文字颜色:
SetTextColor - 文字背景色:
SetBkColor(只适用于OPAQUE模式(不透明模式)) - 文字背景模式:
SetBkMode(OPAQUE/TRANSPARENT)
- 文字颜色:
字体
-
字体相关
- Windows常用的字体为TrueType格式的字体文件
- 字体名:标识字体类型
HFONT:字体句柄
-
字体的使用
- 创建字体
HFONT CreateFont( int nHeight, // 字体高度 int nWidth, // 字体宽度 int nEscapement, // 字符串倾斜角度 int nOrientation, // 字体旋转角度 int fnWeight, // 字体粗细 DWORD fdwItalic, // 斜体 DWORD fdwUnderline, // 下划线 DWORD fdwStrikeOut, // 删除线 DWORD fdwCharSet, // 字符集 DWORD fdwOutputPrecision, // 输出精度 DWORD fdwClipPrecision, // 剪切精度 DWORD fdwQuality, // 输出质量 DWORD fdwPitchAndFamily, // 匹配字体 LPCTSTR lpszFace // 字体名称 );- 应用字体到DC——
SelectObject - 绘制文字——
DrawText/TextOut - 取出字体——
SelectObject - 删除字体——
DeleteObject
对话框
对话框原理
-
对话框的分类
- 模式对话框:当对话框显示时,会禁止其他窗口和用户交互操作
- 无模式对话框:在对话框显示后,其他窗口仍然可以和用户交互操作
-
对话框基本使用
- 对话框窗口处理函数
- 注册窗口类(不使用)
- 创建对话框
- 对话框的关闭
-
对话框窗口处理函数(并非真正的对话框窗口处理函数,真正的对话框窗口处理函数在系统内部)
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( HWND hDlg, // 关闭的对话框窗口句柄 INT_PTR nResult // 关闭的返回值 );- 关闭模式对话框,只能使用
EndDialog,不能使用DestoryWindow等函数 nResult是DialogBox函数退出时的返回值
- 关闭模式对话框,只能使用
-
对话框的消息
WM_INITDIALOG:对话框创建之后显示之前,通知对话框窗口处理函数,可以完成自己的初始化相关操作
无模式对话框
-
创建对话框
HWND CreateDialog( HINSTANCE hInstance, // 应用程序实例句柄 LPCTSTR lpTemplate, // 模板资源ID HWND hWndParent, // 父窗口 DLGPROC lpDialogFunc // 自定义函数 );- 非阻塞函数,创建成功后返回窗口句柄,需要使用
ShowWindow函数显示对话框
- 非阻塞函数,创建成功后返回窗口句柄,需要使用
-
对话框的关闭
- 关闭时使用
DestoryWindow销毁窗口,不能使用EndDialog关闭对话框
- 关闭时使用
静态库
静态库特点
- 运行不存在
- 静态库源码被链接到调用程序中
- 目标程序的归档
C语言静态库
- C静态库的创建
- 创建一个静态库项目
- 添加库程序,源文件使用C文件
- C静态库的使用
- 库路径设置:可以使用
pragma关键字设置 #pragma comment(lib, "../lib/clib.lib")
- 库路径设置:可以使用
C++语言静态库
- C++静态库的创建
- 创建一个静态库项目
- 添加库程序,源文件使用CPP文件
- C++静态库的使用
- 库路径设置:可以使用
pragma关键字设置 #pragma comment(lib, "../lib/cpplib.lib")
- 库路径设置:可以使用
动态库
动态库特点
- 动态库特点
- 运行时独立存在
- 源码不会链接到执行程序
- 使用时加载(使用动态库必须使动态库执行)
- 与静态库的比较
- 由于静态库是将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所以代码体积会增大。动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小。
- 静态库发生变化后,新的代码需要重新链接嵌入到执行程序中。动态库发生变化后,如果库中函数的定义(或地址)未变化,其他使用DLL的程序不需要重新链接。
动态库的创建
-
创建动态库项目
-
添加库程序
-
库程序导出:提供给使用者库中的函数等信息
- 声明导出:使用
_declspec(dllexport)导出函数- 注意:动态库编译链接后,也会有LIB文件,是作为动态库函数映射使用,与静态库不完全相同
- 模块定义文件
.def - 例如:
LIBRARY DLLFunc //库 EXPORTS // 库导出表 DLL_Mul @1 // 导出的函数 - 声明导出:使用
动态库的使用
- 隐式链接(操作系统负责使动态库执行)
- 头文件和函数原型
- 可以在函数原型的声明前,增加
_declspec(dllimport)
- 可以在函数原型的声明前,增加
- 导入动态库的LIB文件
- 在程序中使用函数
- 隐式链接的情况,dll文件可以存放的路径:
- 与执行文件同一目录下
- 当前工作目录
- Windows目录
- Windows/System32目录
- Windows/System
- 环境变量PATH指定目录
- 头文件和函数原型
-
显式链接(程序自己负责使动态库执行)
- 定义函数指针类型
typedef - 加载动态库
HMODULE LoadLibrary( LPCTSTR lpFileName // 动态库文件名或全路径 ); // 返回DLL的实例句柄- 获取函数地址
FARPROC GetProcAddress( HMODULE hModule, // DLL句柄 LPCSTR lpProcName // 函数名称 ); // 成功返回函数地址- 使用函数
- 卸载动态库
BOOL FreeLibrary( HMODULE hModule // DLL的实例句柄 ); - 定义函数指针类型
动态库中封装类
- 在类名称前增加_declspec(dllexport)定义,例如:
class _declspec(dllexport) CMath{
}
- 通常使用预编译开关切换类的导入导出定义,例如:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) // DLL
#else
#define EXT_CLASS _declspec(dllimport) // 使用者
#endif
class EXT_CLASS CMatn{
}
线程
线程基础
- windows线程是可以执行的代码的实例。系统是以线程为单位调度程序。一个程序当中可以有多个线程,实现多任务的处理。
- Windows线程的特点:
- 线程都具有一个ID
- 每个线程都具有自己的内存栈
- 同一进程中的线程使用同一个地址空间
- 线程的调度:
- 操作系统将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程
- 线程轮询:线程A -> 线程B -> 线程A ......
创建线程
- 创建线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性
SIZE_T dwStackSize, // 线程栈的大小
LPTHREAD_START_ROUTINE lpStartAssress, // 线程处理函数的函数地址
LPVOID lpParameter, // 传递给线程处理函数的参数
DWORD dwCreationFlags, // 线程的创建方式
LPDWORD lpThreadId // 创建成功,返回线程的ID
); // 创建成功,返回线程句柄
- 定义线程处理函数
DOWRD WINAPI ThreadProc(
LPVOID lpParameter // 创建线程时,传递给线程的参数
);
线程挂起/销毁
- 挂起
DWORD SuspendThread(
HANDLE hThread // 线程句柄
);
- 唤醒
DWORD ResumeThread(
HANDLE hThread // 线程句柄
);
- 结束指定线程
BOOL TerminateThread(
HANDLE hThread, // 线程句柄
DWORD dwExitCode // exit code
);
- 结束函数所在的线程
VOID ExitThread(
DWORD dwExitCode // exit code for this thread
);
线程相关操作
- 获取当前线程的ID
GetCurrentThreadId();
- 获取当前线程的句柄
GetCurrentThread();
- 等候单个句柄有信号
VOID WaitForSingleObject(
HANDLE handle, // 句柄BUFF的地址
DWORD dwMilliseconds // 等候时间 INFINITE
);
-
同时等候多个句柄有信号
DWORD WaitForMultipleObjects( DWORD nCount, // 句柄数量 CONST HANDLE *lpHandles, // 句柄BUFF的地址 BOOL bWaitAll, // 等候方式 DWORD dwMilliseconds // 等候时间 INFINITE );- bWaitAll:等候方式
- TRUE:表示所有句柄都有信号,才结束等候
- FALSE:表示句柄中只要有一个有信号,就结束等候
- bWaitAll:等候方式
线程同步
原子锁
- 相关问题
- 多个线程对同一个数据进行原子操作,会产生结果丢失。比如执行++运算时。
- 错误代码分析:
- 当线程A执行g_value++时,如果线程切换时间正好是在线程A将值保存到g_value之前,线程B继续执行g_value++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_value上,线程B进行的加法操作被覆盖。
- 使用原子锁函数
InterlockedIncrementInterlockedDecrementInterlockedCompareExchangeInterlockedExchange- ...
- 原子锁的实现:直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问
互斥
-
相关的问题
- 多线程下代码或资源的共享使用
-
互斥的使用
- 创建互斥
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性 BOOL bInitialOwner, // 初始的拥有者 TRUE/FALSE LPCTSTR lpName // 命名 ); // 创建成功返回互斥句柄- 等候互斥
WaitFor... // 互斥的等候遵循谁先等候谁先获取- 释放互斥
BOOL ReleaseMutex( HANDLE hMutex // 互斥句柄 );- 关闭互斥句柄
CloseHandle();
事件
-
相关问题
- 程序之间的通知的问题
-
事件的使用
- 创建事件
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 BOOL bManualReset, // 事件重置(复位)方式,TRUE手动,FALSE自动 BOOL bInitialState, // 事件初始状态,TRUE有信号 LPCTSTR lpName // 事件命名 ); // 创建成功返回事件句柄- 等候事件——
WaitForSingleObject/WaitForMultipleObjects - 触发事件(将事件设置成有信号状态)
BOOL SetEvent( HANDLE hEvent // 事件句柄 );- 复位事件(将事件设置成无信号状态)
BOOL ResetEvent( HANDLE hEvent // 事件句柄 );- 关闭事件——
CloseHandle();
-
小心事件的死锁
信号量
-
相关的问题
- 类似于事件,解决通知的相关问题。但提供一个计数器,可以设置次数。
-
信号量的使用
- 创建信号量
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性 LONG lInitialCount, // 初始化信号量数量 LONG lMaximumCount, // 信号量的最大量 LPCTSTR lpName // 命名 ); // 创建成功返回信号量句柄- 等候信号量
- WaitFor... 每等候通过一次,信号量的信号减1,直到为0阻塞
- 给信号量指定计数值
BOOL ReleaseSemaphore( HANDLE hSemaphore, // 信号量句柄 LONG lReleaseCount, // 释放数量 LPLONG lpPreviousCount // 返回释放前原来信号量的数量,可以为NULL );- 关闭句柄——
CloseHandle();

浙公网安备 33010602011771号