标签:time button .cpp ane rlock 语法 逆时针 优雅 像素
Lesson01
win32
Window 32位 编程
1.Windows编程基础
2.Windows的字符
3.窗口处理
4.消息处理
5.绘图
6.对话框
7.控件
Win32编程:
Windows API:函数接口
一、Windows编程基础
1.Windows应用程序分类
1.1 控制台程序
DOS程序,本身没有窗口,通过WINDOWS下的DOS窗口执行。
1.2 窗口程序
拥有自己的窗口,通过窗口可以和用户进行交互
1.3 库程序
1.3.1 静态库程序
存放代码,数据的程序,其他执行文件从中获取数据和代码
1.3.2 动态库程序
存放代码,数据的程序,其他执行文件从中获取数据和代码
2.对比
2.1 入口函数
控制台程序 - main
窗口程序 - WinMain
动态库程序 - DllMain
静态库程序 - 无入口函数
2.2 文件存在方式
控制台程序 *.exe
窗口程序 *.exe
动态库程序 *.dll
静态库程序 *.lib
2.3 执行方式
控制台程序 - 在DOS中执行
窗口程序 - 拥有自己的窗口,在自己的窗口中执行
动态库程序 - 本身无法执行,由其他可执行的程序(*.exe)或动态库(*.dll)调用
静态库程序 - 执行不存在,嵌入其他可执行程序(.exe)或动态库中
二、Windows下得开发环境
1. Visual stdio C++ 6.0
vc1.5-vc6.0(98前后)-vs2005-vs2008-vs2010-vs2013-vs2015
2. win30-win31-win32-windows95-98-2000- xp|2003 server - vista|win7 | 2008 server
- win8 - win10
3.VC++编译工具
1.编译器 CL.EXE
将源代码编译成目标代码
2. 链接器 LINK.EXE
将目标代码,库文件链接生成最终文件
3.资源管理器 RC.EXE
将资源编译,最终通过链接器存入最终文件
4.头文件
windows.h - 绝大多数头文件集合
windef.h - windows的数据类型
winbase.h - kernel32.dll的头文件
wingdi.h - gdi32.dll的头文件
winuser.h - user32.dll的头文件
winnt.h - 提供了UNICODE字符集的支持
5. 库文件
kernel32.dll - 提供了核心的API,例如:进程、线程、内存管理等
gdi32.dll - 绘图相关的API
user32.dll - 提供窗口,消息API
三 Hello World 程序
int _tWinMain(HINSTANCE hInstance,
//应用程序实例句柄
HINSTANCE hPrevInstance,
//当前应用程序,前一个程序的实例句柄
LPTSTR lpCmdLine,
//命令行参数
int nCmdShow);
//显示方式
hPrevInstance - win32下一般为NULL,
win32以前的程序用到的,现在一般不适用,历史遗留问题。
int MessageBox(
HWND hWnd,
//父窗口句柄
LPCTSTR lpText,
//提示框中信息
LPCTSTR lpCaption,
//提示框标题栏的信息
UINT uType
//提示框的风格
); //返回的是点击按钮
Lesson02
rc.exe
.rc ——————–>.res |
| link.exe
|——————->.exe/.lib/.dll(机器语言)
cl.exe |
.c/.cpp ——————–>汇编语言(.obj)|
编译程序 - cl
cl.exe /? 显示cl命令的帮助
/c 只编译不链接
链接程序 - link
link.exe xxx.obj user32.lib uuid.lib
执行看结果
四、 第一个窗口程序
1.入口函数(WinMain)
2.定义窗口的处理函数 - 用于处理窗口中的各种消息
3.注册窗口类 - 将窗口类注册到系统中
4.创建窗口 - 将窗口创建(并未显示,在内存中创建窗口,内存中有了窗口的数据)
5.显示窗口 - 将窗口显示在显示器上(根据内存中的数据将窗口绘制出来)
6.消息循环 - 提取消息/翻译消息/派发消息
7.消息处理 - 在第2步中的窗口处理函数处理消息
Windows字符处理
ASC - 7位代表一个字符 128(0-127)
ASCII - 8位代表一个字符 256 前128与ASC相同
CODEPAGE - 后面的128个分给其它国家语言使用
汉语936 英语437
1.winodws字符的编码方式
DBCS编码 (DOUBLE BYTE CHARACTER SET)
由1个或2个字节表示一个字符
A 我 是 程 序 员
01 0203 0405 0607 0809 0A0B
0102 0304 0506 0708 090A 0B
UNICODE编码(三套编码方式,他们分别是UTF-8 UTF-16 UTF-32)
WINDOWS平台下,采用2个字节表示一个字符
A 我 是 程 序 员
0001 0203 0405 0607 0809 0A0B
Lesson03
2.字符的使用
1.宽字节字符
char - 每一个字符占1个字节
wchar_t - 每一个字符占2个字节
wchar_t - 实际是unsigned short类型
定义时需要增加"L",通知编译器按照双字节编译字符串,采用
UNICODE编码。函数需要使用支持宽字节字符串的函数 例如:
wchar_t* wpszText = L"Hello WideChar";
wprintf(L"%s",wprintf);
int nlen = wcslen(wpszText);
2.TCHAR:根据环境不同用UNICODE宏开关切换TCHAR的定义,
如果定义了UNICODE那么TCHAR定义为wchar_t,如果
没有定义UNICODE那么TCHAR定义为char
#ifdef UNICODE //#ifdef #ifndef 具有本文件内向上溯源性
typedef wchar_t TCHAR,*PTHCAR;
#define __TEXT(quote) L##quote //L"HELLO"
#else
typedef char TCHAR,*PTCHAR;
#define __TEXT(quote) quote
#endif
TCHAR* pszText = __TEXT("HELLO");
如果定义了UNICODE宏
wchar_t* pszText = L"HELLO";
如果没有定义UNICODE宏
char* pszText = "HELLO";
3.UNICODE字符打印
wprintf 对UNICODE字符打印支持不完善
在WINDOWS下使用WriteConsole
BOOL WINAPI WriteConsole(
_In_ HANDLE hConsoleOutput,
//标准输出句柄
_In_ const VOID *lpBuffer,
//输出字符串的BUFF
_In_ DWORD nNumberOfCharsToWrite,
//准备输出字符的个数
_Out_ LPDWORD lpNumberOfCharsWritten,
//实际输出字符格式
_Reserved_ LPVOID lpReserved
//备用
);
获取标准句柄
HANDLE GetStdHandle(
DWORD nStdHandle
//标准句柄类型
); //返回标准句柄
五、WINODWS窗口的注册和创建
1.窗口的创建过程
1.1 入口函数 -WinMain
1.2 定义窗口处理函数 -WindowProc
1.3 注册窗口类 -RegisterClass
1.4 创建窗口 -CreateWindow
1.5 显示窗口 -ShowWindow/UpateWindow
1.6 消息循环 -GetMessage/TranslateMessage/DispatchMessage
市场人员 需求分析师 调度/项目经理
1.7 消息处理
2.窗口类
窗口类就是一个结构体,它包含了窗口各种参数信息的一个数据结构。
每一个窗口必须有窗口类,基于窗口类,才可以创建窗口。
每一个窗口都具有一个类名,使用前必须将类名注册到操作系统中
3.窗口类的分类
3.1 系统窗口类 - 系统已定义好的窗口类,所有程序无须注册,直接使用即可。
3.2 应用程序全局窗口类 - 由用户自己定义,当前应用程序的所有模块均可以使用
3.3 应用程序局部窗口类 - 由用户自己定义,当前应用程序的本模块可以使用
4.系统窗口类的使用
不需要注册(不需要使用RegisterClass)直接使用系统以及定义好的窗口类名称即可。
比如:"BUTTON" - 按钮
"EDIT" - 文本编辑框
5.应用程序全局窗口类的使用
5.1 注册窗口类
RegisterClass/RegisterClassEx
WNDCLASSEX 结构体
typedef struct tagWNDCLASSEX {
UINT cbSize; //结构体的大小, sizeof(WNDCLASSEX)
UINT style; //窗口风格
WNDPROC lpfnWndProc; //窗口处理函数
int cbClsExtra; //窗口类附加数据缓冲区大小,字节为单位
int cbWndExtra; //窗口的附加数据,字节为单位
HINSTANCE hInstance; //应用程序实例句柄
HICON hIcon; //窗口大图标句柄
HCURSOR hCursor; //鼠标光标
HBRUSH hbrBackground; //窗口背景颜色
LPCTSTR lpszMenuName; //菜单
LPCTSTR lpszClassName; //窗口类名称(最好不是汉语)
HICON hIconSm; //窗口的小图标
} WNDCLASSEX, *PWNDCLASSEX;
应用程序全局窗口类,在注册时需要增加CS_GLOBALCLASS 例如:
WNDCLASSEX wce = {0};
wce.style = ....|CS_GLOBALCLASS;
6. 应用程序局部窗口类
在注册使用,不添加CS_GLOBALCLASS风格
7. 窗口类的风格
CS_GLOBALCLASS - 应用程序全局窗口类(一般不用)
CS_BYTEALIGNCLIENT - 窗口客户区水平位置8
倍数对齐。
CS_BYTEALIGNWINDOW - 窗口水平位置8倍数对齐
CS_HREDRAW - 窗口水平位置移动,重绘窗口。
CS_VREDRAW - 窗口垂直位置移动,重绘窗口。
CS_CLASSDC - 该类型窗口,都使用同一个DC.
CS_PARENTDC - 该类型窗口,使用父窗口的DC.
CS_OWNDC - 该类型窗口,使用自己的DC.
CS_SAVEBITS - 允许窗口保存成位图(BMP)
CS_DBLCLKS - 允许窗口接收鼠标双击消息。
CS_NOCLOSE - 窗口没有关闭按钮。
Lesson04
8. 窗口类的查找过程
1. 系统会根据传入的窗口类名,在应用程序局部窗口类中查找。
如果找到了执行2
如果未找到执行3
2.比较局部窗口类与窗口窗口时传入的HINSTANCE变量。
如果发现相等,创建和注册在同一模块中,创建窗口并返回
如果发现不相等继续执行3
3.在应用程序全局窗口类中查找。
如果找到执行4
如果未找到执行5
4.使用找到的窗口类,创建窗口,并返回。
5.在系统窗口类中查找,
如果找到创建窗口并返回,
如果未找到创建窗口失败
六。创建窗口
CreateWindow/CreateWindowEx
HWND WINAPI CreateWindowEx(
In DWORD dwExStyle,
//窗口的拓展风格
In_opt LPCTSTR lpClassName,
//窗口类名
In_opt LPCTSTR lpWindowName,
//窗口标题名称名
In DWORD dwStyle,
//窗口的风格
In int x,
//窗口左上角的x坐轴标
In int y,
//窗口左上角的y坐轴标
In int nWidth,
//窗口的宽度
In int nHeight,
//窗口的高度
In_opt HWND hWndParent,
//父窗口句柄
In_opt HMENU hMenu,
//窗口的菜单句柄
In_opt HINSTANCE hInstance,
//应用程序实例句柄
In_opt LPVOID lpParam
//窗口创建时的附加数据(参数)
); //执行成功,返回窗口句柄
2 子窗口的创建
2.1 创建时要设置父窗口句柄
2.2 创建子窗口要设置WS_CHILD | WS_VISIBLE
3 窗口类和窗口的附加数据
int cbClsExtra; //窗口类附加数据的BUFF大小,建议是4的倍数
int cbWndExtra; //窗口附加数据BUFF大小,建议是4的倍数
SetClassLong //设置窗口类附加数据
DWORD WINAPI SetClassLong(
_In_ HWND hWnd, //窗口句柄
_In_ int nIndex, //0-? 内存索引
_In_ LONG dwNewLong //要设置的数据 (4字节)
);
GetClassLong //获取窗口类附加数据
DWORD WINAPI GetClassLong(
_In_ HWND hWnd,
_In_ int nIndex
);//返回设置的附加数据
SetWindowLong //设置窗口附加数据
LONG WINAPI SetWindowLong(
_In_ HWND hWnd,
_In_ int nIndex,
_In_ LONG dwNewLong
);
GetWindowLong //获取窗口附加数据
LONG WINAPI GetWindowLong(
_In_ HWND hWnd,
_In_ int nIndex
);
可以提供窗口类和窗口存放自己数据空间,基本不用
七 消息机制
1.程序的执行机制
过程驱动 - 程序的执行过程按照事先安排好的顺序执行
事件驱动 - 程序的执行时无序的,用户可以根据需要随机
触发相应的事件
win32 窗口程序采用的事件驱动方式也就是消息机制
2.消息机制
当系统通知窗口工作时,就是采用消息的范式派发给窗口
2.1 消息的定义
每个消息都有一个ID,同时还附带了两个消息的参数
typedef struct tagMSG {
HWND hwnd; //窗口的句柄
UINT message; //消息的ID
WPARAM wParam; //消息的附加数据1
LPARAM lParam; //消息的附加数据2
DWORD time; //消息产生的时间
POINT pt; //消息产生时鼠标的位置
} MSG;
2.2 窗口处理函数
LRESULT CALLBACK WndProc(
HWND hWnd, //窗口句柄
UINT nMsg, //消息ID
WPARAM wParam, //消息附加参数1
LPARAM lParam) //消息附加参数2
当系统通知窗口时,会调用窗口处理函数,同时将
消息的ID和参数传递给窗口处理函数。
在窗口处理函数中,不处理的消息调用缺省处理函数
DefWindowProc
2.3 消息相关的函数
2.3.1 GetMessage - 获取消息
BOOL WINAPI GetMessage(
_Out_ LPMSG lpMsg, //获取的消息的BUFF
_In_opt_ HWND hWnd, //窗口句柄
_In_ UINT wMsgFilterMin, //获取的消息最小ID
_In_ UINT wMsgFilterMax //获取的消息最大ID
);
wMsgFilterMin和wMsgFilterMax只能获取由这两个值,指定的消息
范围,如果都为0,表示没有范围
2.3.2 TranslateMessage - 翻译消息
将按键消息(可以字符的按键消息需要翻译),翻译成字符消息
BOOL TranslateMessage(
CONST MSG* lpMsg
//要翻译的消息的地址
);
首先检查是否是可见字符的按键消息,如果不是,直接返回
2.3.3 DispatchMessage - 派发消息
LONG DispatchMessage(
CONST MSG* lpMsg
//准备派发的消息的地址
);
Lesson05
3 常见的几个消息
3.1 WM_DESTROY -窗口销毁时的消息
无消息参数,常见与窗口销毁之前的善后处理,例如:资源,内存的回收等
3.2 WM_SYSCOMMAND -系统命令消息
当点击 最小化/最大化/关闭按钮等命令时,收到消息。常用于才窗口关闭时,
提示用户处理
WPARMA - 具体的命令,例如:SC_CLOSE
LPARAM
LOWORD - 鼠标的X轴坐标
HIWORD - 鼠标的Y轴坐标
低两个字节: int a = 10000;
short b = a & 0xffff;
高两个字节: short c = a>>16;
3.3 WM_CREATE -窗口创建成功但还未显示之前
常见于窗口的参数初始化,资源创建包括子窗口的创建
WPARAM - 不使用
LPARAM - 是CREATESTRUCT结构体的指针,保存了CreateWindowEx函数的12个参数
3.4 WM_SIZE -窗口的大小发生变化后,收到这个消息
常用于窗口大小变化后,调整窗口内的布局
WPARAM - 窗口大小变化的原因
LPARAM
LOWORD - 窗口大小变化后客户区的宽度
HIWORD - 窗口大小变化后客户区的高度
3.5 WM_QUIT - 用于结束消息循环
WPARAM -退出码,PostQuitMessage函数的参数指定
LPARAM -不使用
当GetMessage收到这个消息后,会返回FALSE,结束while循环(使消息循环退出)
该消息不会出现在窗口处理函数中
4. 消息的获取和发送
4.1 消息的获取
GetMessage - 从系统中获取消息,将消息从系统中移除,阻塞函数,当系统
中没有消息,这个函数阻塞等候下一条消息。
PeekMessage - 已查看的方式从系统获取消息,可以不将消息移除,非阻塞函数
BOOL WINAPI PeekMessage(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax,
_In_ UINT wRemoveMsg
//移除表示
);
4.2 消息发送
BOOL WINAPI PostMessage(
_In_opt_ HWND hWnd,
//窗口句柄
_In_ UINT Msg,
//消息ID
_In_ WPARAM wParam,
//消息参数1
_In_ LPARAM lParam
//消息参数2
);
PostMessage -投递消息,消息发出去后立即返回,不等候消息的处理结果
LRESULT WINAPI SendMessage(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
SendMessage -发送消息,消息发出去后,等候处理完,才返回.
5. 消息的分类
1. 系统消息 - ID范围(0-0x0FF) 1024
由系统定义好的消息,所有程序直接可以使用
2.用户自定义消息 - ID范围(0x0400-0x7FFF)(31743)
由用户自己定义,满足用户自己的需求,用户自定义的消息,需要用户自己
发送,并负责处理
定义宏 WM_USER 代表0x0400,自定义消息ID时
#define WM_MYMESSAGE WM_USER+n
3.应用程序消息 - ID范围(0x8000 - 0xBFFF)
用于程序之间的通信(目前用不到,多见底层驱动程序)
定义宏 WM_APP 代表0x8000
4.系统注册消息 - ID范围(0xC000-0xFFFF)
在系统注册并生成消息,然后在各个应用程序中使用
八. 消息和消息队列
1. 消息队列
用于存放消息的队列,消息在队列中先进先出,所有窗口都有消息队列,程序从消息队列
中提出消息
2. 消息队列的类型
2.1 系统的消息队列
由系统维护的消息队列,存放系统产生的消息。(鼠标,键盘)
2.2 程序的消息队列
属于程序(主线程)的消息队列,由程序本身负责维护。
3.消息队列的关系
3.1 当鼠标和键盘产生消息时,先放入系统消息队列
3.2 系统会根据存放消息,找到对应的窗口信息
3.3 按照对应的窗口信息,将消息转发给相应的进程的消息队列中
4.队列消息和非队列消息
4.1 根据消息和消息队列的使用关系
4.1.1 队列消息
将消息存入消息队列,消息的发送和获取都是通过队列完成
4.1.2 非队列消息
消息不进入队列,直接调用窗口处理函数
4.2 队列消息
- 先把消息放入队列,通过消息循环从队列中获取消息并且发送给窗口处理函数
GetMessage/PeekMessage -取队列消息
PostMessage -向队列中投递消息
4.3 非队列消息
- 消息直接找对应的窗口处理函数,并调用处理函数,完成处理
SendMessage - 发送非队列消息,直接窗口处理函数,等待处理结果。
SendMessage(...)
{
//...
return WndProc(...);
}
Lesson 6
5 消息的获取
5.1.1 消息循环
5.1.1 GetMessage/PeekMessage 从程序的消息队列中获取消息
5.1.2 TranslateMessage
-检查GetMessage/PeekMessage函数获取到的消息,
如果是可见字符按键消息,产生一个字符消息(WM_CHAR),并且将
字符消息放入消息队列
5.1.3 DispatchMessage
-根据取出的消息,找到对应的窗口处理函数,
完成消息处理
5.2 GetMessage/PeekMessage 执行机制
1 先去程序(线程)的消息队列中检查有没有消息,
如果有消息,检查指定条件(窗口句柄,ID范围)是否满足,如果满足
条件取出消息进行处理
2 如果程序(线程)的消息队列中没有消息,向系统的消息队列中获取本程序的
消息,系统将消息转发给程序的消息队列,然后GetMessage/PeekMessage再从
程序的消息队列中获取消息
(系统队列中的消息无法直接获取,需要转发到程序的消息队列中在获取)
3 如果系统的消息队列中也没有和本程序相关的消息,检查有没有需要重新绘制的
区域,如果有发送WM_PAINT 消息,放入队列,取出并处理
4 如果没有需要重新绘制的区域,检查有没有到时间的定时器,如果有发送WM_TIMER消息
放入队列,取出并处理
5 如果以上都没有,整理内存和资源等
6 GetMessage 阻塞等候下一条消息
PeeKMessage 会返回FALSE,交出程序的控制权
注:GetMessage函数,如果获取到WM_QUIT 返回FALSE
九、WM_PAINT
1. WM_PAINT - 窗口中,有需要重新回去的区域,就会产生WM_PAINT消息
窗口需要重新绘制的区域叫窗口无效区域
UpdateWindow/RedrawWindow 刷新整个窗口
BOOL InvalidateRect(
_In_ HWND hWnd, //窗口句柄
_In_ const RECT *lpRect,//定义窗口无效区域(可以为NULL)
_In_ BOOL bErase //擦除标识
);
2. 绘图步骤(WM_PAINT)
2.1 BeginPaint -开始绘图
HDC BeginPaint(
_In_ HWND hwnd, //窗口句柄
_Out_ LPPAINTSTRUCT lpPaint //绘图结构体指针(程序员基本不用)
); //返回绘图设备句柄
2.2 绘图(需要使用HDC)
2.3 EndPaint -结束绘图
BOOL EndPaint(
_In_ HWND hWnd, //窗口句柄
_In_ const PAINTSTRUCT *lpPaint //绘图结构体指针(程序员基本不用)
);
WM_PAINT的处理过程:
如果有无效区域(无效区域标识),再合适的时机产生WM_PAINT消息,在处理
函数中对这个消息进行处理,处理时先调用BeginPaint(...),得到绘图设备句柄
的同时去掉无效区域标识,然后用HDC完成绘图,最后利用EndPaint结束绘图
十 键盘消息
WM_KEYDOWN -按键按下时产生的消息(按住不松可以产生多次WM_KEYDOWN)
WM_KEYUP -按键被放开产生的消息 (一次按键只能产生一次)
WPARAM -虚拟键码
LPARAM -按键的参数,比如按键的次数等
WM_CHAR -字符消息 (TranslateMessage)
WPARAM - 字符编码
LPARAM - 按键的参数
TranslateMessage(&nMsg)
{
//是否是按键消息
if (nMsg.message != WM_KEYDOWN )
return;
//通过nMsg.wParam(虚拟键码)判断是否是可见字符
if ( 不是可见字符)
return;
if(是可见字符)
{
//判断capslock是否处于打开状态
if(打开)
...
PostMessage(nMsg.hWnd,WM_CHAR,0x41,...);
else
...
PostMessage(nMsg.hWnd,WM_CHAR,0x61,...);
}
}
WM_KEYDOWN/UP按键消息的WPARAM 表示按键的虚拟键码
WM_CHAR字符消息的WPARAM 表示的是输入字符(字符ASCII编码)
六 鼠标消息
1.鼠标基本消息
鼠标左键按下 - WM_LBUTTONDOWN
鼠标左键弹起 - WM_LBUTTONUP
鼠标右键按下 - WM_RBUTTONDOWN
鼠标右键弹起 - WM_RBUTTONUP
鼠标移动 - WM_MOUSEMOVE
WPARAM -按键的状态 例如:Ctrl/Shift
LPARAM
LOWORD -鼠标光标的x轴坐标(客户区坐标系)
HIWORD -鼠标光标的y轴坐标
Lesson 7
双击消息 需要CS_DBLCLKS风格
鼠标左键双击 - WM_LBUTTONDBLCLK
鼠标右键双击 - WM_RBUTTONDBLCLK
WPARAM -按键的状态 例如:Ctrl/Shift
LPARAM
LOWORD -鼠标光标的x轴坐标
HIWORD -鼠标光标的y轴坐标
鼠标滚轮消息 - WM_MOUSEWHEEL
WPARAM
LOWORD -按键的状态 例如:Ctrl/Shift
HIWORD -滚轮滚动偏移量(120倍数)
正数代表向前滚,负数代表向后滚
LPARAM 鼠标当前位置,屏幕坐标系
LOWORD -鼠标光标的x轴坐标
HIWORD -鼠标光标的y轴坐标
七 定时器Timer (WM_TIMER)
1.可以在程序中设置定时器,每隔一定的时间间隔会向
消息队列中发送一个WM_TIMER消息。
定时器时间间隔的单位毫秒 但是准确率很差
每隔一定时间间隔,自动发送WM_TIMER消息到窗口处理函数,
间隔单位毫秒,但不是一个准确的定时器,而且由于优先级较低
有可能根本不产生。
2.消息的参数
WPARAM - 定时器ID
LPARAM - 定时器处理函数的指针
3.定时器的使用
3.1 创建并启动定时器
UINT WINAPI SetTimer(
HWND hWnd,
//窗口句柄
UINT_PTR nIDEvent,
//定时器ID
UINT uElapse,
//定时器的时间间隔(ms)
TIMERPROC lpTimerFunc
//定时器的处理函数(可以为NULL)
);//如果定时器创建成功,返回非0
定时器的处理函数可以自己定义,也可以使用窗口处理函数
3.2 定时器处理函数
VOID CALLBACK TimerProc(
HWND hwnd,
//窗口句柄
UINT uMsg,
//WM_TIMER
UINT_PTR idEvent,
//定时器ID
DWORD dwTime
//当前系统时间
);
3.3 定时器每隔uElapse 毫秒产生一个WM_TIMER消息,这个消息将由
TimerProc或者WindowsProc来处理
3.4 关闭定时器
KillTimer -销毁定时器
BOOL KillTimer(
HWND hWnd,
//窗口句柄
UINT uIDEvent
//定时器ID
);
练习:
GetClientRect -得到窗口客户区的大小
BOOL WINAPI GetClientRect(
HWND hWnd,
//窗口句柄
LPRECT lpRect
//RECT结构体的指针
);
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
Ellipse -绘制圆
BOOL Ellipse(
HDC hdc,
//绘图设备句柄(BeginPaint函数的返回值)
int nLeftRect,
//左上角的X坐标
int nTopRect,
//左上角的Y坐标
int nRightRect,
//右下角的X坐标
int nBottomRect
//右下角的Y坐标
);
Lesson8
八.菜单的分类
1.1 窗口顶层菜单
1.2 弹出式菜单
1.3 系统菜单
HMENU - 菜单句柄
每一个参与消息的菜单项都对应一个ID
2.窗口顶层菜单
2.1 CreateMenu -创建菜单
HMENU WINAPI CreateMenu();
2.2 AppendMenu/InsertMenu - 添加菜单项
BOOL WINAPI AppendMenu(
HMENU hMenu,
//菜单句柄
UINT uFlags,
//菜单项的风格
UINT_PTR uIDNewItem,
//菜单的ID或者(弹出式)下拉菜单的句柄
LPCTSTR lpNewItem
//菜单项的文字信息
);
uFlags:
MF_POPUP - 菜单项要有弹出式菜单,菜单项id为(弹出式)下拉菜单(子菜单)的句柄
MF_SEPARATOR - 菜单项分割线
MF_STRING - 菜单被点击之后,发出消息WM_COMMAND
MF_MENUBARBREAK -对于弹出式菜单,放到新栏中,栏间有分隔线
2.3 显示菜单
BOOL WINAPI SetMenu(
HWND hWnd,
//窗口句柄
HMENU hMenu
//菜单句柄
);
3. 弹出式菜单
3.1 CreatePopupMenu - 创建弹出式菜单
HMENU WINAPI CreatePopupMenu();
3.2 AppendMenu/InsertMenu -添加菜单项
4. 菜单项的消息
点击菜单项产生WM_COMMAND消息
WPARAM
HIWORD - 对于菜单为0
LOWORD - 指明被点击的菜单项的ID
LPARAM -对于菜单为NULL
可以根据LOWORD(wParam)确定菜单项的ID,然后根据ID处理消息
5. 菜单的状态
5.1 在增加菜单项时,设置菜单项的状态MF_XXX(如:MF_CHECKED MF_GRAYED)
5.2 可以使用特定的函数改变菜单项的状态
5.2.1 CheckMenuItem -修改勾选状态
DWORD CheckMenuItem(
HMENU hmenu,
//菜单的句柄
UINT uIDCheckItem,
//菜单项的ID(或者索引)
UINT uCheck
//菜单项的状态
);
5.2.2 EnableMenuItem -修改可用状态
DWORD EnableMenuItem(
HMENU hmenu,
//菜单的句柄
UINT uIDCheckItem,
//菜单项的ID(或者索引)
UINT uCheck
//菜单项的状态
);
注:分割线占一个位置
以上两个函数,再使用时,菜单项标识
可以使用位置(索引)或者ID
MF_BYCOMMAND 通过id定位
MF_BYPOSITION 通过位置索引
5.3 WM_INITMENUPOPUP
在菜单被激活但还未显示之前,产生的消息
WPARAM - 菜单句柄(弹出即将显示的菜单句柄)
LPARAM
LOWORD - 被点击的顶层菜单项的位置(索引)
HIWORD - 窗口菜单的标识
窗口菜单:系统菜单和窗口的顶层菜单
弹出式菜单不属于窗口菜单
6. 系统菜单
1. GetSystemMenu - 获取系统菜单句柄
HMENU WINAPI GetSystemMenu(
HWND hWnd,
//窗口句柄
BOOL bRevert
//重置标识
);
TRUE: 把当前的系统菜单重置为默认的系统菜单
FLASE: 不做重置,返回当前的系统菜单句柄
2. 系统菜单的修改
BOOL DeleteMenu(
HMENU hMenu,
//菜单句柄
UINT uPosition,
//菜单的ID或者位置(索引)
UINT uFlags
//MF_BYCOMMAND/MF_BYPOSITION
);
AppendMenu
3. 系统菜单的消息
WM_SYSCOMMAND
WPARAM
LOWORD 菜单项的ID
7. 右键弹出菜单
1.使用
1.1 创建弹出菜单 CreatePopupMenu
AppenMenu/InsertMenu
1.2 显示弹出式菜单 TrackPopupMenu
BOOL WINAPI TrackPopupMenu(
HMENU hMenu,
//弹出式菜单句柄
UINT uFlags,
//菜单风格
int x,
//菜单x轴位置 (屏幕坐标)
int y,
//菜单y轴位置 (屏幕坐标)
int nReserved,
//保留参数,必须为0
HWND hWnd,
//窗口句柄
const RECT *prcRect
//忽略
);
TrackPopupMenu 是一个阻塞函数,在选择菜单项之前程序不会继续执行
窗口(客户区)坐标转换屏幕坐标
ClientToScreen
BOOL ClientToScreen(
HWND hWnd,
//窗口句柄
LPPOINT lpPoint
//点的坐标(输入输出参数)
客户区坐标系下的坐标
屏幕坐标系下的坐标
);
屏幕坐标转换成窗口客户区坐标
ScreentToClient
BOOL ClientToScreen(
HWND hWnd,
//窗口句柄
LPPOINT lpPoint
//点的坐标(输入输出参数)
屏幕坐标系下的坐标
客户区坐标系下的坐标
);
1.3 菜单项的命令处理
WM_COMMAND 与窗口顶层菜单一样
1.4 WM_CONTEXTMENU专职处理右键菜单,在WM_RBUTTONUP之后产生
WPARAM -指向窗口句柄
LPARAM
LOWORD -光标的x轴坐标(屏幕坐标系)
HIWORD -光标的y轴坐标(屏幕坐标系)
Lesson 9
九、资源的使用
1. 资源相关
资源脚本文件 .rc文件
rc.exe 将.rc文件编译生成.res文件
资源可以给程序设定菜单、工具栏、快捷键、位图、对话框、
程序版本信息、多种语言的STRING表等…
注:使用资源必须建立.rc(资源)脚本文件
2.菜单资源的使用
2.1 添加菜单资源(VS中添加)
2.2 使用菜单资源
2.2.1 注册窗口类时加载菜单 基于窗口类创建的所有窗口菜单都一样
MAKEINTRESOURCE - 将数字形式的资源ID转换为字符串形式的资源ID
MAKEINTRESOURCE(IDR_MENU1)
2.2.2 加载菜单资源,设置到窗口 基于同一窗口类可以挂载不同的菜单
HMENU LoadMenu(
HINSTANCE hInstance,
//应用程序实例句柄
LPCTSTR lpMenuName
//字符串形式的菜单资源ID
(利用MAKEINTRESOURCE转换)
);
将硬盘上的.rc脚本文件中关于lpMenuName资源的数据加载到hInstance指明的内存中,
并获取菜单句柄
3.图标资源的使用
3.1 添加图标资源(VS中添加)
3.2 使用图标资源,加载图标
HICON LoadIcon(
HINSTANCE hInstance,
//应用程序实例句柄
LPCTSTR lpIconName
//字符串形式的菜单资源ID
(利用MAKEINTRESOURCE转换)
);
3.3 设置图标
3.3.1 注册窗口类时,设置图标
建议大小图标一致,用户体验好
3.3.2 使用WM_SETICON消息
wParam = (WPARAM)(BOOL) fType;
ICON_BIG/ICON_SMALL 大图标/小图标
lParam = (LPARAM)(HICON) hicon;
要设置的图标的句柄
3.4 绘制图标到窗口
BOOL DrawIcon(
HDC hDC,
//绘图设备句柄
int X,
//x轴坐标(客户区坐标系)
int Y,
//y轴坐标(客户区坐标系)
HICON hlcon
//图标句柄
);
4. 光标资源的使用
4.1 在VS中添加
光标的大小默认是32*32像素,每个光标都有HotSpot,是当前鼠标的热点
Windows下光标的文件.cur(静态)为后缀和.ani(动态)为后缀
系统光标目录:C:\Windows\Cursors
4.2 加载光标资源
HCURSOR WINAPI LoadCursor(
HINSTANCE hInstance,
//应用程序实例句柄
LPCTSTR lpCursorName
//字符串形式的菜单资源ID
(利用MAKEINTRESOURCE转换)
);
4.2.1 注册窗口类时
4.2.2 使用SetCursor设置光标
HCURSOR SetCursor(
HCURSOR hCursor
//设置的新的光标句柄
);//返回原来的光标句柄
4.3 WM_SETCURSOR 消息
产生: 鼠标移动导致光标的移动,并且捕获鼠标的移动,就产生WM_SETCURSOR
WPARAM -当前使用的光标句柄
LPARAM
LOWORD - 当前区域的代码(hit-test code)
(HTCLIENT 代表客户区 HTCAPTION 非客户区-标题栏)
HIWORD - 当前鼠标消息ID
GetCursorPos - 得到鼠标光标位置
BOOL WINAPI GetCursorPos(
LPPOINT lpPoint
//存放鼠标位置buffer,屏幕坐标系下的坐标
);
GetClientRect -获取窗口边界,窗口客气区坐标系
ScreenToClient -将屏幕坐标系下的坐标转换为客户区坐标系下的坐标
从光标文件中加载光标资源
HCURSOR LoadCursorFromFile(
LPCTSTR IpFileName
//光标资源文件名
);
Lesson10
5. 字符串资源
2.1 添加字符串资源
字符串表,在表中添加字符串
2.2 字符串资源的使用
int LoadString(
HINSTANCE hInstance,
//应用程序实例句柄
UINT uID,
//资源ID
LPTSTR lpBuffer,
//接收字符串的BUFF
int cchBufferMax
//BUFF的大小
); //成功返回字符窗的长度,失败返回0
6 快捷键(加速键)资源
6.1 添加快捷键资源
添加加速键表,增加命令ID对应的加速键
一般加速键可以和菜单项的ID命令对应
6.2 快捷键(加速键)的使用
6.2.1 加载快捷键(加速表)表
HACCEL LoadAccelerators(
HINSTANCE hInstance,
//应用程序实例句柄
LPCTSTR lpTableName
//字符串形式的加速键资源ID
); //返回加速键句柄
6.2.2 处理加速键消息
int TranslateAccelerator(
HWND hWnd,
//窗口句柄
HACCEL hAccTable,
//加速键表句柄
LPMSG lpMsg
//消息
); //如果是加速键返回非0
6.2.3 TranslateAccelerator 函数的执行机制
TranslateAccelerator(hwnd,hAccTable,&nMsg)
{
if ( nMsg.message != WM_KEYDOWN || nMsg.message != WM_SYSCOMMAND )
return 0;
根据消息附带的参数(nMsg.wParam)虚拟键码(CTRL+Y)
将(CTRL+Y)去hAccTable加速键表中查找
if(找到了)
{
PostMessage(hwnd,WM_COMMAND, L(ID_NEW) H(1),NULL);
return 1;
}else{
return 0;
}
}
6.2.4 快捷键(加速键)产生WM_COMMAND
WPARAM
HIWORD - 加速键为1 菜单为0
LOWORD - 加速键ID 菜单的ID
LPARAM -未使用
6.2.5 加速键的命令ID与菜单项的命令ID无关
但是为了代码复用最好定义成一样的ID
十、绘图
1. 绘图相关
绘图设备 (Device Context - DC 也叫绘图上下文)
BeginPaint - 获取绘图设备
HDC hdc = BeginPaint(hWnd,...);
TextOut(hdc,100,100,"dfsass",...);
Ellipse(hdc,100,100,200,200,...);
EndPaint - 回收绘图设备
HDC -DC句柄 表示绘图设备
2. GDI -windows graphics device interface WIN32提供的绘图API
3. 颜色
3.1 颜色的表示(RGB)
计算机使用红,绿,蓝每种颜色的取值范围
R 代表红色 0 ~ 255
G 代表绿色 0 ~ 255
B 代表蓝色 0 ~ 255
每个点的颜色是3个字节(24位)保存取值范围(0 ~ 2^24 - 1)
R G B
16位 5 5 6
32位 8 8 8 多出的8位用于透明度(一般是3D绘图用)
Direct3D
openGL
3.2 颜色的使用
COLORREF -实际是DWORD
COLORREF nColor = 255;
赋值使用RGB宏,例如:
COLORREF nColor = RGB(0,255,0);
获取颜色的RGB值
GetRValue(nColor)/GetGValue/GetBValue
例如: BYTE nRed = GetRValue(nColor);
3.3 点的使用
GetPixel - 获取指定点的颜色
COLORREF GetPixel(
HDC hdc,
//绘图设备句柄
int nXPos,
//x轴坐标
int nYPos
//x轴坐标
);
SetPixel - 设置指定点的颜色
COLORREF SetPixel(
HDC hdc,
//绘图设备句柄
int X,
//x轴坐标
int Y,
//x轴坐标
COLORREF crColor
//设置的颜色值
);
3.4 线的使用
MoveToEx - 移动当前点到指定点(指定线的初始点)
BOOL MoveToEx(
HDC hdc,
//DC句柄
int X,
//指定点的x坐标
int Y,
//指定点的y坐标
LPPOINT lpPoint
//当期点的BUFF
);
LineTo - 从当前点到终点绘制一条直线
BOOL LineTo(
HDC hdc,
//DC句柄
int nXEnd,
//指定终点的x坐标
int nYEnd
//指定终点的y坐标
);
默认情况下,当前为(0,0), 上一次绘图的终点是下一次绘图的当前点
3.5 封闭图形的使用( 能够用画刷填充的图形 ?)
Rectangle/RoundRect
BOOL Rectangle(
HDC hdc, //DC句柄
int nLeftRect, //左上角的x坐标
int nTopRect, //左上角的Y坐标
int nRightRect, //右下角的X坐标
int nBottomRect //右下角的Y坐标
);
BOOL RoundRect(
HDC hdc,
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect,
int nWidth,
//指定用来画圆角的椭圆的宽
int nHeight
//指定用来画圆角的椭圆的高
);
3.6 圆的使用
Ellipse - 绘制圆 或 椭圆
BOOL Ellipse(
HDC hdc,
//绘图设备句柄(BeginPaint函数的返回值)
int nLeftRect,
//左上角的X坐标
int nTopRect,
//左上角的Y坐标
int nRightRect,
//右下角的X坐标
int nBottomRect
//右下角的Y坐标
);
3.7 圆弧的使用
//设置圆弧的方向
int SetArcDirection(
HDC hdc,
//句柄
int ArcDirection
//圆弧的方向
//AD_CLOCKWISE 顺时针
//AD_COUNTERCLOCKWISE 逆时针
);
BOOL Arc(
HDC hdc,
int xLeft,
int yTop,
//外接矩形的左上角坐标
int xRight,
int yBottom,
//外接矩形的右下角坐标
int XStart,
int YStart,
//圆弧的起点坐标
int XEndA,
int YEnd
//圆弧的终点坐标
);
Lesson11
4 GDI绘图对象 - 画笔
4.1 画笔相关
作用:线的颜色,线型(实线,虚线),线粗
HPEN - 画笔的句柄
4.2 画笔的使用
4.2.1 创建画笔 - CreatePen
HPEN CreatePen(
int fnPenStyle,
//画笔的样式
int nWidth,
//画笔的宽度
COLORREF crColor
//画笔的颜色
);//创建成功,返回画笔句柄
PS_SOLID - 实心先,可以支持1个单位宽度
其它所有的风格,只能是1个单位的宽度 例如:PS_DASH
4.2.2 将画笔应用到DC中 - SelectObject
HGDIOBJ SelectObject(
HDC hdc, //DC句柄
HGDIOBJ hgdiobj
//绘图对象句柄
); //返回旧的绘图对象句柄
4.2.3 绘图
4.2.4 取出DC中的画笔 - SelectObject
将原来的画笔,使用SelectObject函数放入到DC中,
就会将我们创建的画笔取出
4.2.5 释放画笔 - DeleteObject
BOOL DeleteObject(
HGDIOBJ hObject
//绘图对象句柄
);
只能删除不被DC使用的画笔,所以在释放画笔之前,必须
将画笔从DC中取出
5 GDI 绘图对象 - 画刷
5.1 画刷
作用:给封闭图形填充颜色,图案
HBRSH - 画刷句柄
5.2 画刷的使用
5.2.1 创建画刷
CreateSolidBrush - 创建实心的画刷
HBRUSH CreateSolidBrush(
COLORREF crColor
//画刷的颜色
); //创建成功,返回画刷的句柄
CreateHatchBrush - 创建阴影画刷
HBRUSH CreateHatchBrush(
int fnStyle,
//阴影样式
COLORREF clrref
//画刷的颜色
);//创建成功,返回画刷的句柄
CreatePatternBrush - 创建位图画刷
HBRUSH CreatePatternBrush(
HBITMAP hbmp
//位图句柄
);//创建成功,返回画刷的句柄
//加载位图
HBITMAP LoadBitmap(
HINSTANCE hInstance,
//应用程序实例句柄
LPCTSTR lpBitmapName
//字符串资源ID
);//返回位图句柄
5.2.2 将画刷应用到DC中
- SelectObject
5.2.3 绘图
5.2.4 取出DC中的画刷
- SelectObject
5.2.5 释放画刷
- DeleteObject
5.3 其它
可以使用GetStockObject函数获取系统的画刷,画笔等
如果我们不使用画刷填充,需要使用NULL_BRUSH参数,获取不填充的画刷
GetStockObject返回的画刷不需要释放
6 GDI绘图对象 - 位图
6.1 位图相关
光栅图形 - 记录每个点的颜色等信息
矢量图形 - 记录图像的算法,绘图的指令
HBITMAP - 位图句柄
6.2 位图的使用
6.2.1 添加位图资源
6.2.2 加载位图资源
6.2.3 创建一个与当前DC相匹配的DC
HDC CreateCompatibleDC(
HDC hdc
//当前的绘图设备,可以为NULL (使用屏幕DC)
); //返回内存DC句柄
为什么要弄一个内存DC?
为了不让用户看见成像的过程,提升用户体验
6.2.4 将位图应用内存DC (内存DC得到位图的时候,就将这个位图绘制在虚拟区域中)
SelectObject
6.2.5 绘制位图(成像)
BOOL BitBlt( //1:1成像
HDC hdcDest, //目标DC
int nXDest, //目标的左上角的x坐标
int nYDest, //目标的左上角的y坐标
int nWidth, //目标的宽度 (成像多少图形)
int nHeight, //目标的高度
HDC hdcSrc, //源DC
int nXSrc, //源的左上角的x坐标
int nYSrc, //源的左上角的y坐标
DWORD dwRop //绘制方法
);
BOOL StretchBlt( //缩放成像
HDC hdcDest, //目标DC
int nXOriginDest, //目标的左上角的x坐标
int nYOriginDest, //目标的左上角的y坐标
int nWidthDest, //目标的宽度
int nHeightDest, //目标的高度
HDC hdcSrc, //源DC
int nXOriginSrc, //源的左上角的x坐标
int nYOriginSrc, //源的左上角的y坐标
int nWidthSrc, //源DC宽 (拷贝多少图形)
int nHeightSrc, //源DC高
DWORD dwRop //绘制方法
);
6.2.6 取出位图
SelectObject
6.2.7 释放位图
DeleteObject
6.2.8 释放内存DC
DeleteDC
6.3 其它
使用GetObject 获取位图信息
int GetObject(
HGDIOBJ hgdiobj,
//绘图对象的句柄
int cbBuffer,
//缓冲区的大小
LPVOID lpvObject
//缓冲区
);
BITMAP 位图信息的结构
/* Bitmap Header Definition */
typedef struct tagBITMAP
{
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP;
Lesson 12
十一、坐标系
1. 坐标系的分类
1.1 设备坐标系(以显示器为例)
以像素为单位,设备的左上角为原点,X轴右为正,Y轴下为正的坐标系
1.1.1 屏幕坐标系
- 以当前显示器的左上角为原点
1.1.2 窗口坐标系
- 以当前窗口的左上角为原点
1.1.3 窗口客户区坐标系
- 以当前窗口的客户区左上角为原点
1.2 逻辑坐标系(计算机的是逻辑坐标系)
在GDI绘图中,使用的是逻辑坐标绘图
2 坐标的映射关系
2.1 映射模式
逻辑坐标和设备坐标之间的映射关系 (1逻辑单位=几个设备单位)
设备坐标的单位由设备自己决定,大小固定(显示器的像素,打印机1/1440英寸)
逻辑坐标的单位可以通过程序设置
int SetMapMode(
HDC hdc, //DC句柄
int fnMapMode //新的映射模式
);//返回旧的映射模式
MM_TEXT - 1个逻辑单位 = 1个设备单位(默认默认)(x轴右为正,y轴下为正)
MM_HIENGLISH - 1个逻辑单位 = 0.001英寸(x轴右为正,y轴上为正)
MM_LOENGLISH - 1个逻辑单位 = 0.01英寸 (x轴右为正,y轴上为正)
MM_HIMETRIC - 1个逻辑单位 = 0.01毫米 (x轴右为正,y轴上为正)
MM_LOMETRIC - 1个逻辑单位 = 0.1毫米 (x轴右为正,y轴上为正)
MM_TWIPS - 1个逻辑单位 = 1/1440 (x轴右为正,y轴上为正) (可以用来打印预览)
逻辑单位转换成具有任意比例的任意单位
MM_ISOTROPIC - 1个逻辑单位 = 自定义
MM_ANISOTROPIC
X轴的1个逻辑单位 = 自定义1
Y轴的1个逻辑单位 = 自定义2
用SetWindowExtEx 和 SetViewportExtEx 函数可指定单位、方向和比例
BOOL SetWindowExtEx(
HDC hdc,
//DC句柄
int nXExtent, 1
//x轴的逻辑比例
int nYExtent, 1
//y轴的逻辑比例
LPSIZE lpSize
//原来的比例
);
BOOL SetViewportExtEx(
HDC hdc,
//DC句柄
int nXExtent,
//x轴的设备比例 2
int nYExtent,
//y轴的设备比例 3
LPSIZE lpSize
//原来的比例
);
十二、文字和字体
1 绘制文字
1.1 TextOut - 将文字绘制到指定的位置
1.2 DrawText - 将文字绘制到矩形区域内
int DrawText(
HDC hDC, //DC句柄
LPCTSTR lpchText, //字符串BUFF
int nCount, //字符串长度
LPRECT lpRect, //矩形区域BUFF
UINT uFormat //绘制方式
);
BOOL ExtTextOut(
HDC hdc, //DC句柄
int X, //字符串的x坐标
int Y, //字符串Y坐标
UINT fuOptions, //输出选项(基本不用,给0就可以)
const RECT* lprc, //输出矩形区域(基本不用,给NULL)
LPCTSTR lpString, //字符串的BUFF
UINT cbCount, //字符串的长度
const int* lpDx //字符的间距数组
);
注:字符间距是上一个字符的开始到下一个字符开始之间的距离
2 文字的颜色和背景
文字的颜色的设置
COLORREF SetTextColor(
HDC hdc,
COLORREF crColor //要设置的文字的颜色
);
文字的背景颜色的设置
COLORREF SetBkColor(
HDC hdc,
COLORREF crColor //要设置的文字的颜色
);//执行成功,返回旧文字的背景颜色
文字的背景模式
int SetBkMode(
HDC hdc,
int iBkMode //背景模式 OPAQUE/TRANSPARENT 不透明/透明
);
注:透明时SetBkColor失效
3 字体 ( GDI绘图对象 )
3.1 字体相关
字体名: 标识字体的类型
HFONT - 字体句柄
3.2 字体的使用
3.2.1 创建字体 CreateFont
HFONT CreateFont(
int nHeight, //字体的高度
int nWidth, //字体的宽度
int nEscapement, //字体的切斜度(以度为单位)
int nOrientation, //字体的旋转度(二维编程看不出效果)
int fnWeight, //字体的粗度
DWORD fdwItalic, //是否是斜体(TRUE 是,FALSE否)
DWORD fdwUnderline, //是否有下划线
DWORD fdwStrikeOut, //是否有删除
DWORD fdwCharSet, //字符集(汉语操作系统定死GB2312_CHARSET)
DWORD fdwOutputPrecision, //输出精度(基本无效 置0)
DWORD fdwClipPrecision, //裁剪精度(基本无效 置0)
DWORD fdwQuality, //输出质量(基本无效 置0)
DWORD fdwPitchAndFamily, //匹配字体(基本无效 置0)
LPCTSTR lpszFace //字体名称
);//返回创建的字体的句柄
3.2.2 将字符应用到DC中
SelectObject
3.2.3 绘制文字
3.2.4 从DC中取出字体
SelectObject
3.2.5 释放字体
DeleteObject
十三 对话框
1 对话框的分类
1.1 模式(态)对话框 - 当对话框显示时,会禁止本进程的其它窗口和用户
进行交互操作
1.2 非模式(态)对话框 - 当对话框显示时,其它窗口同样可以和用户进行交互操作
模式对话框和非模式对话框的创建和销毁完成不同
2 对话框的基本使用
2.1 对话框的窗口处理函数
2.2 创建对话框
2.3 对话框的关闭
3 模式对话框的使用
3.1 对话框的处理函数 DialogProc (并非真正的对话框处理函数)
BOOL CALLBACK DigLogProc(
HWND hwndDlg, //对话框的窗口句柄
UINT uMsg, //消息ID
WPARAM wParam, //消息附带参数1
LPARAM lParam //消息附带参数2
);
对话框的处理函数的调用和普通的窗口不同
普通的窗口处理函数是自定义调用缺省
WindowProc(.......)
{
return DefWindowProc(.....);
}
对话框处理函数是缺省调用自定义
SysDlgProc(...)
{
....; //区分是哪个对话框的消息,
//并调用相应的自定义的处理函数
if ( DialogProc )
return;
......; //缺省的各种消息的默认处理
}
返回FALSE - 表示DialogProc没有处理这个消息
交给缺省处理函数去处理
返回TRUE - 表示DialogProc处理了这个消息
直接返回,缺省的处理函数不处理
3.2 创建模式对话框
int DialogBox(
HINSTANCE hInstance, //应用程序实例句柄
LPCTSTR lpTemplate, //对话框字符串资源ID
HWND hWndParent, //父窗口句柄
DLGPROC lpDialogFunc //自定义的对话框处理函数
);
需要添加对话框资源;
DialogBox是一个阻塞函数,只有当对话框关闭后才会返回,
继续执行后续代码;
返回值由EndDialog指定
3.3 模式对话框的关闭
BOOL EndDialog( //销毁对话框同时让DialogBox返回
HWND hDlg, //对话框句柄
int nResult //指定DialogBox函数的返回值
);
注:关闭模式对话框只能用EndDialog函数,不能使用DestroyWindow函数。
DestroyWindow虽然能销毁窗口,但是不能让DialogBox返回
3.4 对话框的消息
对话框没有WM_CREATE消息,取而代之的是 WM_INITDIALOG
WM_INITDIALOG - 对话框创建成功,但还未显示之前产生,
可以完成自己的初始化工作
4. 非模式对话框
4.1 对话框的处理函数(与模式对话框处理函数一样)
4.2 创建非模式对话框
HWND CreateDialog(
HINSTANCE hInstance, //应程序实例句柄
LPCTSTR lpTemplate, //对话框字符串资源ID
HWND hWndParent, //父窗口
DLGPROC lpDialogFunc //窗口处理函数
);
非阻塞函数,创建成功返回对话框窗口句柄,
需要使用ShowWindow函数显示对话框
4.3 非模式对话框的关闭
关闭时使用DestroyWindow销毁窗口,不要使用EndDialog关闭非模式对话框
5 对话框VS普通窗口
5.1 创建
模式对话框 - DialogBox,阻塞函数
无模式对话框 - CreateDialog
普通窗口 - CreateWindow/CreateWindowEx
5.2 窗口处理函数
对话框 - DialogProc(并非真正的对话框处理函数)
普通窗口 - WindowProc,需要调用缺省窗口处理函数
5.3 窗口消息
普通窗口 - WM_CREATE
对话框 - WM_INITDIALOG
5.4 窗口关闭
模式对话框 - EndDialog
无模式对话框/普通窗口 -DestroyWindow
Lesson13
十四、子控件
1. 子控件相关
系统已经定义(注册)的窗口类型,相应的处理函数都已经有系统完成。
例如:按钮,文件编辑框等等
2. 子控件的创建
不需要注册,直接使用CreateWindow/CreateWindowEx函数创建该类的窗口。
子控件创建时,每个控件都有唯一的ID。
3. 控件的消息
程序和控件的交互,通过消息完成
3.1 控件的窗口消息
程序员可以使用SendMessage函数,向控件发送消息,
获取控件的信息和设置控件的状态
3.2 控件的通知消息
控件有相应的事件发生后,会向父窗口发送通知消息(WM_COMMAND),
父窗口根据控件ID,做相应的处理
十五、静态框
1. 静态框的分类
文字静态框 - 显示文字
图标静态框 - 显示图标 设置SS_ICON/SS_BITMAP
2.静态框作用
作用:显示文字或图像,类名"STATIC"
3.静态框的使用
3.1 创建
3.1.1 函数CreateWindow/CreateWindowEx函数
3.1.2 风格
图标静态框使用SS_ICON/SS_BITMAP风格
如果要创建图标静态框,那么窗口名称参数要设置为图标或者位图的ID
例如:
CreateWindowEx(0,"STATIC","#101",.....);
3.2 静态框控件的窗口消息
SendMessage函数发送到控件(处理函数)即可
例如:STM_SETICON
WPARAM - 图标句柄
LPARAM - 未使用,必须为0
3.3 控件的通知消息(通知码:STN_CLICKED)
需要在创建控件时增加SS_NOTIFY风格
通知消息通过 WM_COMMAND 消息发送到父窗口
附:
WM_COMMAND
WPARAM
LOWORD -菜单、快捷键、控件的ID
HIWORD -对于菜单 0
-对于快键键 1
-对于控件 通知码(Notificaton Code)
LPARAM -对于菜单、快捷键为NULL
-对于控件为控件句柄
Lesson14
十六、按钮(Button)
根据按钮的风格,将按钮分为4类
1. 按钮相关
1.1 下压式按钮
BS_PUSHBUTTON/BS_DEFPUSHBUTTON
1.2 分组框
BS_GROUPBOX
1.3 复选框
BS_CHECKBOX 勾选与非勾选状态需要程序员自己维护
BS_AUTOCHECKBOX 勾选与非勾选状态系统维护
BS_3STATE 三态(勾选,灰色勾选,非勾选)
BS_AUTO3STATE
1.4 单选框
BS_RADIOBUTTON
BS_AUTORADIOBUTTON
窗口类名:“BUTTON”
2. 下压式按钮
2.1 创建按钮
2.2 窗口消息
2.3 通知消息
BN_CLICKED
3. 分组框
常用于界面控件的分组显示,较高界面友好性
4. 复选框
4.1 风格和创建
BS_CHECKBOX -点击时,需要自己维护状态
BS_AUTOCHECKBOX -点击时,系统自动维护状态
BS_3STATE/BS_AUTO3STATE -多一个灰色的勾选状态
4.2 窗口消息
BM_SETCHECK - 设置按钮的状态
WPARAM - 设置的状态 BST_CHECKED/BST_UNCHECKED/BST_INDETERMINATE
LPARAM - 未使用,必须为0
BM_GETCHECK - 获取按钮的状态
WPARAM/LPARAM -均未使用,必须为0
4.3 通知消息
BN_CLICKED 按钮被点击
5.单选框
5.1 风格和创建
BS_RADIOBUTTON - 自己维护状态
BS_AUTORADIOBUTTON - 系统维护状态
5.2 窗口消息
BM_SETCHECK
BM_GETCHECK
每组单选框只能有一个被选中
5.3 通知消息
BN_CLICKED
5.4 单选框分组
可以使用WS_GROUP风格分组,从当前具有WS_GROUP风格的单选框开始,
到下一个具有WS_GROUP风格的单选框之前为一组单选框
例如: A、B为一组 C、D为一组
CreateWindowEx(."A"...|WS_GROUP,........);
CreateWindowEx(."B"...,........);
CreateWindowEx(."C"...|WS_GROUP,........);
CreateWindowEx(."D"...,........);
6 BS_PUSHLIKE
该风格可以将单选框或复选框做成下压式的外观
十七、编辑框
1. 编辑框相关
从风格可以将编辑框分为:
单行编辑框 - 只能处理一行文字(默认)
多行编辑框 - 可以多行文字(ES_MULTILINE)
密码编辑框 - (ES_PASSWORD)
窗口类名 :“EDIT”
2. 消息的使用
2.1 窗口消息
WM_GETTEXT - 获取文本编辑框中的文本内容
WPARAM -缓冲区的长度
LPARAM -缓冲区的地址
WM_GETTEXTLENGTH - 得到编辑框中字符串长度
WPARAM/LPARAM - 均未使用,必须为0
WM_SETTEXT -设置文字编辑框的文本内容
WPARAM -未使用,必须为0
LPARAM -字符串缓冲区
练习:两个菜单项(保存,打开)
点击"保存",将编辑框中的内容保存到c:/my.txt中
点击"打开",将c:/my.txt中的内容显示到编辑框中
#include "stdio.h"
fopen/fread/fwrite/fclose/fseek/ftell
Lesson15
十八、组合框
1.组合框的分类
1) 简单组合框 -CBS_SIMPLE
2) 下拉式组合框 -可以输入,也可以下拉选择CBS_DROPDOWN
3) 下拉式列表组合框 -只可以选择,不可以输入CBS_DROPDOWNLIST
窗口的类名"COMBOBOX"
2. 组合框的使用
2.1 创建组合框
2.2 选项的添加
CB_ADDSTRING
WPARAM -未使用,必须为0
LPARAM -字符串指针
CB_INSERTSTRING
2.3 选项的删除
CB_RESETCONTENT -清除所有选项
WPARAM/LPARAM 均未使用,必须为0
CB_DELETESTRING -删除指定项
WPARAM -选项的索引
LPARAM -未使用,必须为0
2.4 获取和设置选择项
CB_SETCURSEL - 设置当前被选择项
WPARAM - 选项的索引
LPARAM -未使用,必须为0
CB_GETCURSEL - 获取当前被选择项
WPARAM/LPARAM 均未使用,必须为0
SendMessage函数的返回,获取选择项的索引,如果当前没有选择项返回CB_ERR
2.5 查找选项
CB_FINDSTRING - 根据字符串,查找选项(非精确匹配)
WPARAM - 查找选项开始索引
LPARAM - 匹配字符串指针
CB_FINDSTRINGGEXACT - (精确匹配)
CB_SELECTSTRING - 查找并且将找到的选项设置为当前选项项(非精确匹配)
2.6 获取选项的字符
CB_GETLBTEXTLEN - 获取选项字符的长度
WPARAM - 选项的索引
LPARAM - 未使用,必须为0
字符长度通过SendMessage函数返回值得到
CB_GETLBTEXT - 获取选项字符的内容
WPARAM - 选项的索引
LPARAM - 获取字符内容的BUFF
2.7 选项可以有自己的自定义数据
CB_SETITEMDATA - 设置选项的自定义数据
WPARAM - 选项的索引
LPARAM - 选项的自定义数据BUFF
CB_GETTIMEDATA - 获取选项的自定义数据
WPARAM - 选项的索引
LPARAM - 未使用,必须为0
自定义数据通过SendMessage函数的返回值得到,
自定义数据的类型:DWORD
3. 通知消息
CBN_SELCHANGE - 当选择项发送变化后,产生的消息
CBN_EDITCHANGE - 当输入发送变化后,产生的消息
列表框
1. 列表框分类
单行列表框 - 默认风格
多行列表框 - LBS_MULTICOLUMN
创建"LISTBOX"
2. 窗口消息
2.1 添加选项
LB_ADDSTRING - 追加
LB_INSERTSTRING - 按指定位置添加
2.2 删除选项
LB_DELETESTRING -删除指定项
LB_RESETCONTENT -清空所有项
2.3 获取和设置选择项
LB_GETCURSEL
LB_SETCURSEL
2.4 匹配查找
LB_FINDSTRING
LB_FINGSTRINGGEXACT
LB_SELECTSTRING
2.5 获取选项字符
LB_GETTEXT - 获取选项文本内容
LB_GETTEXTLEN - 获取选项文本长度
2.6 附加数据
LB_SETTIMEDATA
LB_GETTIMEDATA
3. 通知消息
LB_SELCHANGE - 当前选择项发生变化时,依附于WM_COMMAND消息发送到父窗口
滚动条控件(一般不用了,用滚动条风格)
Lesson16
十九、Windows库程序
1.静态库程序
运行时不独立存在,会被嵌入(链接时)到可执行程序或其它动态库中,
目标程序的归档。
文件扩展名:LIB
2.动态库程序
运行时独立存在,不会被链接到可执行程序或者其它动态库中。
文件扩展名: DLL
3.静态库程序
3.1 特点
1)运行时不独立存在
2)可以链接到可执行程序或其它动态库中
3)目标程序的归档
3.2 C语言的静态库
3.2.1 静态库的创建
1)建立项目
2)添加库程序,源文件.c文件
3)生成.lib文件
3.2.2 静态库的使用
1)可以使用pragma关键字设置
#pragma comment(lib,"../lib/clib.lib")
建立一个.c文件,.c文件可以在文件中直接使用C库函数,不需要
头文件。
2)项目的"属性"->"配置属性"->"链接器"->"输入"->"附加依赖项"中设置(../lib/clib.lib)
3.3 c++语言的静态库
3.3.1 静态库的创建
1)建立项目
2)添加库程序,源文件.cpp文件
3)生成.lib文件
3.3.2 静态库的使用
1)可以使用pragma关键字
2)项目的"属性"->"配置属性"->"链接器"->"输入"->"附加依赖项"中设置
c++语言的静态库需要提供头文件,C语言最好也提供文件头
注意:CPP环境使用C静态库,库中函数的声明要增加extern "C"
例如:
extern "C"int Clib_add(int add1,int add2);
4 动态库程序
4.1 动态库程序的特点
1)运行时独立存在
2)不会链接到可执行程序或其它动态库中
3)使用时加载
与静态库的比较
1)由于静态库是将源码嵌入到使用程序中,多个程序使用时,会有
多份代码,所以程序的体积会增大。
动态库的代码只需要一份,其它程序通过函数的地址使用,所以体积小
2)静态库发生变化,使用(静态库的)程序需要重新编译链接嵌入到使用程序中。
动态库发生变化,如果库中函数原型未发生改变,其它使用动态库的程序不需要
重新编译链接
4.2 动态的创建
4.2.1 建立项目
4.2.2 添加库程序
4.2.3 库程序导出(depends工具可以查看导出函数)
(导出函数的偏移地址)提供给使用者库中函数的信息,只有被导出
的库函数才能被调用者使用
1)声明导出
函数声明时加上 __declspec(dllexport)
注意:编译链接之后,会有lib文件(导出函数的偏移地址),是作为
动态库函数映射使用,与静态库的lib文件不同。
2)模块文件.def导出
例如:
LIBRARY CPPDLL(库名称)
EXPORTS
CPPdll_add @1
CPPdll_sub @2
CPPdll_mul @3
项目"属性"->"配置属性"->"链接器"->"输入"->"模块定义文件"
4.4 动态库的使用
4.4.1 隐式连接 (动态库加载过程不明显,需要用到动态库配套生产的.lib文件)
(1)头文件和函数原型
在函数原型的声明前增加__declspec(dllimport)
(2)导入动态库的lib文件
方式1:使用pragma关键字导入
方式2:配置属性
隐式链接情况,Dll可以存在的路径:
1. 与可执行程序文件在同一目录下
2. 当前工作目录
3. WINDOWS目录
4. WINDOWS/SYSTEM32目录
5. WINDOWS/SYSTEM目录
6. 环境变量PATH指定目录
4.4.2 显示连接(动态库的加载过程明显)
1)定义函数指针类型
2)加载动态库
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName
//动态库的名称或全路径
); //返回动态库的实例句柄
3)获取函数地址
FARPROC WINAPI GetProcAddress(
_In_ HMODULE hModule, //动态库实例句柄
_In_ LPCSTR lpProcName //函数名称
);//执行成功,返回函数地址
4)使用函数
5)卸载动态库
BOOL FreeLibrary(
HMODULE hLibModule
//动态库实例句柄
);
注意:显示连接,获取函数地址时,如果动态库的导出方式为
__declspec(dllexport),那么获取函数地址失败。显示
连接方式只适用于模块文件导出函数的动态库
4.4.3 两种连接方式对比
1) 在库函数定义不变的情况下
隐式连接: 由于库函数地址是在程序编译链接时拷贝的,所以
当动态库发生变化后,使用程序需要重新编译链接
显示连接: 由于库函数地址是在程序运行时动态从库中查询到的,所以
库发生变化后不需要重新编译链接
2)动态库的加载时机不同
隐式连接: 动态库是再使用程序启动时就被加载,当动态库不存在
使用程序无法启动。
显示连接: 动态库是在调用LoadLibrary函数时才被加载,
因此使用程序可以启动,只有使用程序执行到
LoadLibrary函数时,才会出错
5 DLL中类的使用
5.1 DLL中定义并且实现一个类
5.2 DLL中类要导出
在类名前增加__declspec(dllexport)
例如:
class __declspec(dllexport) CMath
{
public:
......
};
通常使用预编译宏开关切换类的导入或导出
例如:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS __declspec(dllexport)
#else
#define EXT_CLASS __declspec(dllimport)
#endif
5.3 使用DLL中的类
5.3.1 带入dll的lib文件
5.3.2 类的定义 CMath math;
5.3.3 使用类中的成员
6 动态库的程序入口
入口函数不是DLL必须得,用于DLL内部初始化或者善后处理
动态库的加载和卸载时被调用
例如:调用LoadLibrary或 FreeLibrary 函数时,会被调用
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
//动态库程序实例句柄
DWORD fdwReason,
//调用原因
LPVOID lpvReserved
//保留
);
Lesson 17
二十、Windows的文件
1.创建并打开文件
HANDLE CreateFile(
LPCTSTR lpFileName,
//文件名称(全路径)
DWORD dwDesiredAccess,
//访问方式 读/写
DWORD dwShareMode,
//共享方式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
//安全属性,默认为NULL
DWORD dwCreationDisposition,
//创建/打开方式
DWORD dwFlagsAndAttributes,
//文件属性
HANDLE hTemplateFile
//文件模板句柄,默认为NULL
);//成功,返回文件句柄
2.写数据
BOOL WINAPI WriteFile(
HANDLE hFile,
//文件句柄
LPCVOID lpBuffer,
//数据BUFF
DWORD nNumberOfBytesToWrite,
//准备写入的数据的长度
LPDWORD lpNumberOfBytesWritten,
//实际写入的数据的长度
LPOVERLAPPED lpOverlapped
//置NULL,同步
);
3. 读数据
BOOL WINAPI ReadFile(
HANDLE hFile,
//文件句柄
LPVOID lpBuffer,
//数据BUFF
DWORD nNumberOfBytesToRead,
//准备读取的长度
LPDWORD lpNumberOfBytesRead,
//实际读取的长度
LPOVERLAPPED lpOverlapped
//NULL(同步传输)
);
4. 文件的长度
DWORD GetFileSize(
HANDLE hFile,
//文件句柄
LPDWORD lpFileSizeHigh
//文件长度的高32位
); //返回值,返回文件长度的的低32位
5. 文件指针
DWORD SetFilePointer(
HANDLE hFile,
//文件句柄
LONG lDistanceToMove,
//偏移量的低32位
PLONG lpDistanceToMoveHigh,
//偏移量的高32位
DWORD dwMoveMethod
//移动的相对参照位置
);
6. CopyFile - 拷贝文件
BOOL WINAPI CopyFile(
LPCTSTR lpExistingFileName,
//源文件名称
LPCTSTR lpNewFileName,
//目标文件名称
BOOL bFailIfExists
//TURE 不覆盖 FALSE 覆盖
);
7. DeleteFile - 删除文件
BOOL DeleteFile(
LPCTSTR lpFileName
);
8. MoveFile - 移动文件
BOOL WINAPI MoveFile(
LPCTSTR lpExistingFileName,//源文件
LPCTSTR lpNewFileName //目标文件
);
9. CloseHandle - 关闭文件
Lesson18
二十一、文件遍历
1.查找文件
HANDLE WINAPI FindFirstFile(
LPCTSTR lpFileName, //查找路径
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); //返回查找句柄
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; //文件的创建时间
FILETIME ftLastAccessTime; //文件最后一次访问时间
FILETIME ftLastWriteTime; //文件最后一次修改时间
DWORD nFileSizeHigh; //文件大小高32位
DWORD nFileSizeLow; //文件大小低32位
DWORD dwReserved0; //目前没用
DWORD dwReserved1; //目前没用
TCHAR cFileName[MAX_PATH]; //文件名称
TCHAR cAlternateFileName[14]; //文件内部名称
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
文件属性:
FILE_ATTRIBUTE_ARCHIVE 普通文件
FILE_ATTRIBUTE_DIRECTORY 目录文件
2. 获取下一下文件
BOOL FindNextFile(
HANDLE hFindFile, //查找句柄
LPWIN32_FIND_DATA lpFindFileData //查找的数据
); //找到返回TRUE
3. 关闭查找
BOOL FindClose(
HANDLE hFindFile //查找句柄
);
二十二、Windows的内存管理
1. Windows地址空间
程序中可以寻址的最大范围,对32位程序,地址空间范围
0-2^32(4G),地址空间越大,相对程序编写越简单
2. 地址空间的划分
2.1 用户地址空间(0-2G) 0x7FFFFFFF
存放用户态程序和数据,用户空间的代码是不能访问内核空间的
数据和代码的
2.1.1 空指针区(NULL区,0-64K) 0x00010000
系统将0-64k的指针,都认为是空指针
2.1.2 用户区 (0x00010000 - 0x7FFFFFFF)
2.1.3 64K禁入区(0x7FFFFFFF - 0x7FFEFFFF)
防止用户访问内核地址空间
2.2 内核地址空间(2G-4G)
存放内核的代码和数据,例如:系统驱动。
内核空间的代码可以访问用户空间
3. Windows内存
3.1 区域
区域是有连续的一块内存构成,区域的大小一般为64k
或者64K的倍数。每个区域都有自己的状态。
1)空闲:没有被使用的区域
2)私有:被预定的区域
3)映像:存放代码的区域
4)映射:存放数据的区域
3.2 物理内存
系统可以使用的实际内存,CPU可以直接访问的内存
3.3 虚拟内存(硬盘交换文件)
将硬盘文件虚拟成内存使用。(pagefile.sys文件)
CPU如果要访问虚拟内存数据,必须将虚拟内存数据,放到物理内存中
3.4 内存页
系统管理内存的最小单位,内存页的大小为4K,每个内存页都有自己的
权限
3.5 页目表
31 22 21 12 11 0
|---------|---------|---------------|
10位 10位 12位
页目录1k 页表1k 4k
楼层 房间 座位
1024 1024 4*1024 = 4*1024*1024*1024 = 4G
3.6 从内存中获取数据
3.6.1 根据地址在物理内存中查找相应的位置,如果找到物理内存,
取回数据,如果没有找到执行3.6.2
3.6.2 根据地址在虚拟内存中查找相应的位置,如果也没有找到,那么
该地址没有内存空间,返回错误。如果找到执行3.6.3
3.6.3 将改地址所在的内存页,置换到物理内存中,同时将原来物理内存的
数据,存放到虚拟内存中。
3.6.4 将物理内存中的数据返回给使用者
3.7 内存分配
3.7.1 虚拟内存分配
适合大内存分配,一般1M以上的内存。
3.7.2 堆内存分配
适合小内存分配,一般1M以下的内存。
3.7.3 栈内存分配
适用小内存分配,一般1M以下内存
4. 虚拟内存分配
4.1 虚拟分配的特点
速度快,大内存效率高,将地址和内存分配分别执行,
可以在需要的时候,在提交内存,常用于大型电子表
格的处理。
4.2 虚拟内存的使用
4.2.1 内存的分配
LPVOID WINAPI VirtualAlloc(
LPVOID lpAddress,
//NULL或提交地址
SIZE_T dwSize,
//分配的地址大小,内存页4k的整数倍
DWORD flAllocationType,
//分配方式
DWORD flProtect
//内存访问方式
);
MEM_COMMIT - 提交内存,分配之后返回内存地址空间
MEM_RESERVE - 只申请地址,没有提交内存,内存空间不生成,
要使用内存必须再次提交
//获取内存使用状态
BOOL WINAPI GlobalMemoryStatusEx(
_Inout_ LPMEMORYSTATUSEX lpBuffer
);
typedef struct _MEMORYSTATUSEX {
DWORD dwLength; //结构体大小(sizeof(MEMORYSTATUSEX))
DWORD dwMemoryLoad; //物理内存使用率(%)
DWORDLONG ullTotalPhys; //物理内存总大小
DWORDLONG ullAvailPhys; //可用的物理内存
DWORDLONG ullTotalPageFile; //pagefile文件最多可写入内存大小
DWORDLONG ullAvailPageFile; //pagefile文件空闲部分大小
DWORDLONG ullTotalVirtual; //用户可用的地址空间
DWORDLONG ullAvailVirtual; //当前空闲的地址空间
DWORDLONG ullAvailExtendedVirtual; //保留参数,必须为0
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;
4.3 使用内存
4.4 释放
BOOL VirtualFree(
LPVOID lpAddress,
//释放地址
DWORD dwSize,
//释放大小,写0全部释放
DWORD dwFreeType
//释放的方式
);
MEM_DECOMMIT - 只释放内存,地址不释放
MEM_RELEASE - 地址和内存都释放
Lesson19
5. 堆内存
5.1 堆内存的分配
适合小内存分配,一般小于1M的内存。每个进程都有自己的堆,
默认大小就是1M,根据情况可以调整
5.2 堆的使用
5.2.1 堆的信息
GetProcessHeap - 获取程序的堆
HANDLE GetProcessHeap(VOID) - 返回程序中第一个堆的句柄
DWORD WINAPI GetProcessHeaps( - 获取程序中所有堆
DWORD NumberOfHeaps,
//最多可以接受堆句柄的个数
PHANDLE ProcessHeaps
//接收堆句柄的BUFF(数组)
);//返回堆的个数
5.2.2 创建堆
HANDLE WINAPI HeapCreate(
DWORD flOptions,
//创建选项(方式)
SIZE_T dwInitialSize,
//初始大小
SIZE_T dwMaximumSize
//最大值 0表示没有上限
);//创建成功返回堆句柄
创建方式:
HEAP_NO_SERIALIZE - 堆内存支持不连续存取
HEAP_GENERATE_EXCEPTIONS - 当堆分配内存失败时,会抛出异常。
如果不设置,则返回NULL
HEAD_CTEATE_ENABLE_EXCUTE - 堆中存放的内存是可以执行的
代码。如果不设置,意味着
堆中存放的是不可以执行的
数据
5.2.3 从堆中分配内存
LPVOID HeapAlloc(
HANDLE hHeap,
//堆句柄
DWORD dwFlags,
//分配方式
DWORD dwBytes
//分配大小
); //分配成功,返回地址
分配方式:
HEAP_NO_SERIALIZE - 堆内存支持不连续存取
HEAP_GENERATE_EXCEPTIONS - 当堆分配内存失败时,会抛出异常。
如果不设置,则返回NULL
HEAP_ZERO_MEMORY - 堆内存初始化为0
5.2.4 使用内存
5.2.5 释放
BOOL WINAPI HeapFree(
HANDLE hHeap,
//堆句柄
DWORD dwFlags,
//释放方式,已经废弃
LPVOID lpMem
//释放地址
);
5.2.6 销毁堆
BOOL WINAPI HeapDestroy(
_In_ HANDLE hHeap
//堆句柄
);
注:当堆被销毁后,使该堆中分配的内存全部都被释放
6. 栈内存
每个线程都有自己的栈内存,默认大小1M,一般有系统维护
7. 内存映射文件
7.1 作用
将文件映射成内存使用,当使用内存时,就是使用文件
7.2 内存映射文件的创建并使用
7.2.1 创建并打开文件
CreateFile (文件必须有可读可写的属性)
7.2.2 创建内存映射文件
HANDLE CreateFileMapping(
HANDLE hFile,
//文件句柄
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
//安全属性,默认为NULL
DWORD flProtect,
//访问方式
DWORD dwMaximumSizeHigh,
//内存映射文件大小的高32位
DWORD dwMaximumSizeLow,
//内存映射文件大小的低32位
LPCTSTR lpName
//内存映射文件的名称(可以为NULL)
);//创建成功,返回内存映射文件句柄
7.2.3 获取内存映射文件某个地址(得到地址)
LPVOID WINAPI MapViewOfFile(
HANDLE hFileMappingObject,
//内存映射文件的句柄
DWORD dwDesiredAccess,
//访问模式
DWORD dwFileOffsetHigh,
//偏移量的高32位
DWORD dwFileOffsetLow,
//偏移量的低32位
SIZE_T dwNumberOfBytesToMap
//映射的字节数量(赋值0)
);
PS: dwFileOffsetHigh和dwFileOffsetLow合成的偏移量
必须是区域粒度(64K)的倍数。
(合成的偏移量必须是64k的整数倍)
7.2.4 使用内存
7.2.5 将地址和内存文件分开
BOOL UnmapViewOfFile(
LPCVOID lpBaseAddress
//卸载地址
);
7.2.6 关闭内存映射文件
CloseHandle - 内存映射文件一旦关闭就没有了
7.2.6 关闭文件
CloseHandle
7.3 内存映射文件的打开和使用
7.3.1 打开内存映射文件
通过内存映射文件名称,打开内存映射文件,并获取内存
映射文件句柄
HANDLE WINAPI OpenFileMapping(
DWORD dwDesiredAccess,
//访问方式(FILE_MAP_ALL_ACCESS)
BOOL bInheritHandle,
//继承标识(返回的句柄是否可被子进程继承)
LPCTSTR lpName
//内存映射文件的名称
);//返回内存映射文件的句柄
7.3.2 获取内存映射文件某个地址
MapViewOfFile
7.2.4 使用内存
7.2.5 将地址和内存文件分开
UnmapViewOfFile
7.2.6 关闭内存映射文件
CloseHandle
Lesson 20
二十三、Windows的进程
1. 进程
进程是一个容器,包含程序执行需要的代码、数据还有资源
等信息。Windows是一个多任务的操作系统,可以同时执行多个
进程。
进程是系统进行资源分配和调度的一个独立单位;
线程CPU调度和分派的基本单元(执行单元)
2. 进程特点
1)每个进程都有自己的ID
2) 每个进程都有自己的地址空间,本进程无法访问
别的进程的地址空间
3) 每个进程都有安全属性
4) 每个进程至少包含一个线程(主线程)
3. 进程的信息
3.1 进程ID
DWORD GetCurrentProcessID()
3.2 进程句柄
HANDLE GetCurrentProcess()
- 返回进程虚拟句柄(-1 0xffffffff),可以使用该句柄访问进程的
所有操作
所谓虚拟句柄,就是该句柄只在调用进程的进程中有效,也不能被继承
4.进程的使用
4.1 创建进程
WinExec - 早起16位
ShellExecute - Shell操作
CreateProcess - 目前最多使用
BOOL WINAPI CreateProcess(
LPCTSTR lpApplicationName,
//应用程序名称
LPTSTR lpCommandLine,
//命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes,
//进程安全属性 SD 一般NULL
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//线程安全属性 SD 一般NULL
BOOL bInheritHandles,
//进程的句柄可否被继承
DWORD dwCreationFlags,
//创建方式
LPVOID lpEnvironment,
//环境信息
LPCTSTR lpCurrentDirectory,
//当前目录
LPSTARTUPINFO lpStartupInfo,
//起始信息(新进程的主窗口如何显示)
LPPROCESS_INFORMATION lpProcessInformation
//返回新进程信息(进程和线程的句柄ID)
);
4.2 结束进程
//进程结束自己
void ExitProcess(
UINT uExitCode //退出通知码,可以随意指定
);
//进程结束别的进程
BOOL TerminateProcess(
HANDLE hProcess,
//进程句柄
UINT uExitCode
//进程终止码
);
主要函数main/winmain返回,进程结束
4.3 打开进程
通过进程ID获取进程句柄
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess,
//访问权限 (PROCESS_ALL_ACCESS)
BOOL bInheritHandle,
//继承标示
DWORD dwProcessId
//进程ID
); //返回进程句柄
4.4 关闭进程句柄
CloseHandle - 不会结束线程
4.5 进程的等候
等候可等候的句柄有信号(有信号产生的句柄)
DWORD WINAPI WaitForSingleObject(
HANDLE hHandle,
//句柄
DWORD dwMilliseconds
//等候时间(毫秒)INFINITE 表示无限等待
);
阻塞函数,等候句柄的信号,只在句柄有信号或超出等候时间,
才会结束等候(返回)
返回值:
WAIT_TIMEOUT 等待超时
WAIT_OBJECT_0 指定的对象有信号产生
Lesson21
二十四、Windows线程
1. Windows线程
线程是进程中可执行代码的一个实体,是被系统独立调度和分派的基本单位。
线程是在某个进程的上下文中创建的,而且会在这个进程内部“终其一生”。这
就意味着线程是要在其所属进程的地址空间内执行代码和处理数据。
一个进程当中可以有多个线程,同属一个进程的所有线程共享进程所拥有的全部
资源。(它们执行的代码可以相同也可以不同)
Windows线程的特点:
1)线程都具有1个ID
2)线程具有自己的安全属性
3)每个线程都具有自己的内存栈
4)每个线程都具有自己的寄存器信息
进程多任务和线程多任务:
进程多任务是每个进程都使用私有地址空间
线程多任务是进程内的多个线程使用同一个地址空间
相比于线程,进程所使用的系统资源更多,其原因在于地址空间,
为一个进程创建一个虚拟地址空间需要大量的系统资源。系统中会
发生大量的记录活动,而这需要用到大量的内存。线程只有一个内
存对象和一个内存栈,几乎不涉及记录活动,所以不需要占用多少
内存。建议使用多线程来解决多任务编程的问题。
线程描述了进程内部的一条执行路线,每次初始化进程时,系统都会
创建一个主线程。
对于Windows下的C/C++程序:
1.系统为进程分配资源并创建主线程
2.主线程首先执行C/C++运行库启动代码
3.C/C++运行库中调用入口函数(main/winmain)
4.入口函数中开始执行我们自己的代码
5.执行完我们自己的代码后,入口函数返回
6.继续回到C/C++运行库代码,执行ExitProcess
7.系统回收进程的所有资源
主线程结束意味着进程结束
入口函数返回意味着进程结束
线程的调度:
将CPU的执行时间划分为时间片,依次根据时间片执行不同的线程
线程轮询:
线程A -> 线程B -> 线程A ......
2.线程的使用
2.1 定义线程处理函数
DWORD WINAPI ThreadProc(
LPVOID lpParam
//创建线程时,传递给线程的参数
);
2.2 创建线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//安全属性(NULL 采用默认属性)
SIZE_T dwStackSize,
//线程栈的大小(0 采用默认值1M)
LPTHREAD_START_ROUTINE lpStartAddress,
//线程处理函数的函数地址
LPVOID lpParameter,
//传递给线程处理函数的参数
DWORD dwCreationFlags,
//线程的创建方式
LPDWORD lpThreadId//threadidentifier
//创建成功,返回线程ID
);//创建成功,返回线程句柄(可等待句柄)
创建方式:
0 - 创建之后线程立马执行
CREATE_SUSPENDED - 创建之后线程处于挂起(休眠)状态
注:
1.主线程结束了进程也就结束了,子线程根本就没有运行的机会
2.最好不要将局部变量(的指针)作为参数传递给线程函数
2.3 结束线程
结束函数所在的线程
void ExitThread(
DWORD dwExitCode
//退出通知码
);
结束执行线程
BOOL TerminateThread(
HANDLE hThread,
//线程句柄
DWORD dwExitCode
//退出通知码
);
建议:不管是线程还是进程都不要用Terminate或者Exit强制去结束它,
一般让他们优雅的结束,即线程函数或者入口函数返回。
2.4 关闭线程句柄
CloseHandle
2.5 线程的信息
GetCurrentThreadId - 获取当前线程的ID
GetCurrentThread - 获取当前线程的句柄(虚拟句柄-2)
打开指定ID的线程,获取其句柄
HANDLE WINAPI OpenThread(
DWORD dwDesiredAccess,
//访问方式
BOOL bInheritHandle,
//是否可被继承
DWORD dwThreadId
//线程ID
);
2.6 线程的挂起和执行
挂起
DWORD SuspendThread(
HANDLE hThread
//线程句柄
);
执行
DWORD WINAPI ResumeThread(
HANDLE hThread
//线程句柄
);
Lesson22
二十五、线程的同步
1.多线的问题
线程A -> 线程B -> 线程C….->线程A
临界区资源:可能被多个线程使用的任何内存资源都称为临界区资源
2.线程同步技术
原子锁
临界区
互斥
事件
信号量
3.等候函数
WaitForSingleObject - 等候单个
WaitForMultipleObjects - 等候多个
DWORD WINAPI WaitForMultipleObjects(
DWORD nCount,
//句柄数量
const HANDLE *lpHandles,
//句柄BUFF的地址
BOOL bWaitAll,
//等候方式
DWORD dwMilliseconds
//等候时间(INFINITE)
);
bWaitAll - 等候方式
TRUE - 表示所有句柄都有信号,才结束等候
FALSE - 表示句柄中只要有一个有信号,就结束等候
返回值:
bWaitAll = TRUE 返回值表示所有句柄都有信号
bWaitAll = FALSE 返回值减去 WAIT_OBJECT_0 为有信号的句柄在数组中的索引
二十六、原子锁
1.相关问题
多个线程对同一个数据进程原子操作(如:++、–),会产生结果丢失的现象
g_nValue++;
---------------------------
mov eax,dword ptr [g_nValue (0D77138h)]
add eax,1
mov dword ptr [g_nValue (0D77138h)],eax
伪代码:
eax = g_nValue;
eax = eax+1;
g_nValue = eax;
当线程A执行g_nValue++时,如果线程切换时间正好在线程A
将值保存到g_nValue之前,线程B继续执行g_nValue++,那么当
线程A再次被切换回来之后,会将原来线程A保存的值保存到
g_nValue上,线程B进行的加法操作被覆盖。
2.原子锁的使用(针对操作符运算)
2.1 原子锁
对单条指令的操作
2.2 API(所有运算符基本都有对应的原子锁)
InterlockedIncrement 自加原子锁
InterlockedDecrement 自减原子锁
InterlockedExchange 第二个参数的值替换第一个参数的值 赋值运算符原子锁
...
原子锁的实现:
直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问
二十七、临界区(针对一段代码)
1.问题
多线程情况下同时使用一段代码
2.临界区
锁定一段代码,防止多个线程同时使用该段代码
3.使用
3.1 初始化一个临界区
void WINAPI InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
//临界区变量(CRITICAL_SECTION结构体成员不用关心)
);
3.2 进入临界区
添加到被锁定的代码之前
void WINAPI EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
3.3 离开临界区
添加到被锁定的代码之后
void WINAPI LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
3.4 删除临界区
void WINAPI DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
4.原子锁和临界区
原子锁 - 单条指令
临界区 -单条或多行代码
所有加锁机制都会降低执行效率
5.临界区的死锁
InitializeCriticalSection/DeleteCriticalSection
成对出现,创建和销毁(C++构造和析构)
EnterCriticalSection/LeaveCriticalSection
对于同一临界区要成对出现,不然会死锁
死锁:
fun()
{
EnterCriticalSection(&cs);
if(NULL == lpBuf)
go end;
if( 3 != i)
go end;
end:
LeaveCriticalSection (&cs);
return;
}
Lesson 23
二十七、互斥 Mutex( 互斥体 )
1.相关问题
多任务下编程代码和资源共享使用
2.互斥的使用
2.1 创建互斥
HANDLE WINAPI CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
//安全属性(NULL)
BOOL bInitialOwner,
//初始的拥有者
LPCTSTR lpName
//命名(可以为NULL)
);创建成功返回互斥句柄
(可等候句柄:所有线程都不拥有互斥时有信号,任何一个线程拥有互斥无信号)
bInitialOwner - 初始的拥有者
TRUE - 调用CreateMutex的线程拥有互斥
FALSE - 创建的时没有线程拥有互斥
2.2 等候和获取互斥
WaitForSingleObject\WaitForMultipleObjects
互斥的等候遵循谁先等候谁先获取
2.3 释放互斥
BOOL ReleaseMutex(
HANDLE hMutex
//互斥体句柄
);
拥有互斥体的线程调用可以释放互斥体
2.4 关闭互斥句柄
CloseHandle
2.5 互斥和临界区的区别
临界区 - 用户态加锁,执行效率高,只能在同一个进程中使用
互斥体 - 内核态加锁,执行效率低,可以通过命令的方式跨进程使用
(互斥命名方式跨进程使用,单例进程,底层驱动中使用广泛)
二十八、事件
1.相关问题
多任务(进程或者线程)之间的通知的问题,即协调工作问题
2.事件的使用
2.1 创建事件
HANDLE WINAPI CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
//安全属性(NULL)
BOOL bManualReset,
//事件重置方式,即事件从有信号状态变成无信号状态
BOOL bInitialState,
//事件初始状态
LPCTSTR lpName
//事件命名,可以为NULL
);//创建成功返回事件句柄(可等候句柄)
事件的重置方式:
TRUE 手动
FALSE 自动
事件的初始状态:
TRUE 有信号
FALSE 无信号
2.2 等候事件
WaitForSingleObject\WaitForMultipleObjects
2.3 触发事件
将事件设置为有信号状态(设置事件)
BOOL WINAPI SetEvent(
HANDLE hEvent
//事件句柄
);
将事件设置为无信号状态(重置事件)
BOOL WINAPI ResetEvent(
HANDLE hEvent
//事件句柄
);
2.4 关闭事件句柄
CloseHandle
2.5 事件的死锁
线程之间相互等到事件
二十九、信号量(功能类似事件)
1. 相关的问题
类似于事件,解决多任务之间通知的相关问题。但是可以提供
一个计数器,可以设置次数
2. 信号量的使用
2.1 创建信号量
HANDLE WINAPI CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
//安全属性(NULL)
LONG lInitialCount,
//初始信号量计数值 3
LONG lMaximumCount,
//信号量的最大值 10
LPCTSTR lpName
//命令,可以为NULL
);创建成功返回信号量句柄(可等候句柄)
信号量计数值大于0时有信号,等于0是无信号
2.2 等候信号量
WaitForSingleObject\WaitForMultipleObjects
每等候通过一次,信号量的计数值键1,直到为0阻塞
2.3 重新设置信号量计数值(释放信号量)
BOOL WINAPI ReleaseSemaphore(
HANDLE hSemaphore,
//信号量句柄
LONG lReleaseCount,
//要重新设置的信号量计数值(不能超过创建时指定的最大值)
LPLONG lpPreviousCount
//接收原来信号量的数量,可以NULL
);
2.4 关闭句柄
CloseHandle
C++语法复习:
优先复习几点:
1、this
2、继承和多态
3、->* .*
4、union
标签:time button .cpp ane rlock 语法 逆时针 优雅 像素
原文地址:https://www.cnblogs.com/ostin/p/9198554.html