一 窗口和消息程序员
1. 前缀:web
2 WPARAM和LPARAM的意义
在Windows是一种16位系统时,WndProc的第三个参数被定义为WORD,是一个16位的无符号整数,而第四个参数被定义为一个LONG,是一个32位有符号整数,因此致使对单词PARAM(参数)加前缀W和L。
但在32位Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG,所以窗口过程的这两个参数都是32位的值。算法
3 新的函数类型
WndProc函数返回一个类型为LRESULT的值,该值是一个LONG型,32位有符号。
WndProc函数被指定为CALLBACK类型(供系统调用的函数),WinMain函数被指定为WINAPI类型。这些类型指在Windows自己和用户的应用程序之间发生的函数调用的特殊调用序列。数组
4 窗口类结构WNDCLASS
有10个字段,分别是:
① style:类风格,用于在何时发出窗口变化消息
② cbClsExtra:在类结构保存的窗口结构中预留一些额外空间
③ cbWndExtra:在Windows内部保存的窗口结构中预留一些额外空间
④ hbrBackground:指定基于这个类建立的窗口背景颜色
⑤ hCursor:读取光标
⑥ hIcon:读取图标
⑦ hInstance:程序的实例句柄
⑧ lpfnWndProc:指定处理基于这个窗口类建立的全部窗口的窗口过程
⑨ lpszClassName:指定类名
⑩ lpszMenuName:指定窗口类菜单数据结构
5 注册窗口类RegisterClass
通常在Windows XP及之后均可以很顺利的注册成功。因此能够只写RegisterClass(&wndclass);框架
6 建立窗口CreateWindow
窗口类定义了窗口的通常特征,调用CreateWindow能够指定有关窗口的更详细的信息。异步
hwnd = CreateWindow (szAppName, // 指定一个窗口类,基于该窗口类建立窗口 TEXT ("Hello Win"), // 这个字符串会出如今标题栏中 WS_OVERLAPPEDWINDOW, // 本窗口风格 CW_USEDEFAULT, //窗口左上角的X坐标 CW_USEDEFAULT, //窗口左上角的Y坐标 CW_USEDEFAULT, //窗口的宽度 CW_USEDEFAULT, //窗口的高度 NULL, //窗口对象的父窗口句柄 NULL, //窗口对象的菜单句柄或者子窗口编号 hInstance, //当前进程的实例句柄 NULL) ; //窗口对象的参数指针句柄
建立窗口返回的是窗口句柄。ide
7 显示窗口
窗口建立成功后,系统将在内存中为其分配一块内存,可是此时窗口并未显示在显示器上,因此须要使用两个调用。
① ShowWindow(窗口句柄,iCmdShow);
其中的第二个参数用于肯定如何儿子屏幕上显示窗口,是最小化仍是常规仍是最大化。
② UpdateWindow(窗口句柄)
调用上句将致使客户区被绘制。它经过给窗口过程发送一个WM_PAINT消息来作到这一点。函数
8 消息循环
调用UpdateWindow以后,窗口就出如今显示器上。
Windows为当前运行的每一个Windows程序维护一个“消息队列”。在发生事件的时候,Windows将事件转换为一个“消息”,并将消息放入程序的消息队列中。
程序经过执行一个叫作“消息循环”的代码从消息队列中取出消息。(注意GetMessage(阻塞,若没有消息则不返还控制权)和PeekMessage(非阻塞,若没有消息也会返回)的区别)工具
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } //其中msg是一个类型为MSG的结构,MSG结构在WINUSER.H中定义以下: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG /* 其中: hwnd 是消息发向的窗口句柄。 message 是消息标识符,以WM_开头。 wParam 一个32位的消息参数。 lParam 一个32位的消息参数。 time 消息放入消息队列的时间。 pt 消息放入消息队列时鼠标的坐标。*/
消息循环以GetMessage调用开始,它从消息队列中取出一个消息,GetMessage(&msg, NULL, 0, 0)这一调用传给Windows一个指向名为msg的MSG结构的指针。其他三个参数设置为NULL或0,表示程序接收本身建立的全部窗口的全部消息。 只要从消息队列中取出的消息的message域不为WM_QUIT,GetMessage就返回一个非0值,while循环就能够继续。
TranslateMessage(&msg); 将msg结构传给Windows,进行一些键盘转换。 DispatchMessage(&msg); 将msg结构传给Windows,而后Windows将里面的消息发给相应的窗口过程进行处理。处理后,WndProc返回到Windows,Windows返回到程序,程序继续下一个while循环。
9 窗口过程WndProc
实际的动做发生在窗口过程当中。窗口过程肯定了在窗口的客户区显示什么,以及怎么处理用户输入。
(1)窗口过程是命名为WndProc的函数。(也能够其余不冲突的名字)
(2)一个Windows程序能够包含多个窗口过程。
(3)一个窗口过程老是与调用RegisterClass注册的特定窗口类相关联。
(4)CreateWindow函数根据特定的窗口类建立一个窗口,返回该窗口的句柄。
(5)可是基于一个窗口类能够建立多个窗口。
1程序 —— 包括 ——n个窗口过程
1窗口过程 —— 关联 —— 1窗口类
1窗口类 —— 建立 —— n个窗口
CreateWindows根据窗口类建立一个窗口。
窗口过程老是声明成以下形式:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /*这4个参数跟MSG结构的前4个域是相同的。 第一个参数:接收消息的窗口句柄。 第二个参数:消息类型,标识消息的数字 最后两个参数:32位的消息参数。 */
程序一般不直接调用窗口过程,而是由Windows调用窗口过程。
10 窗口过程处理消息
窗口过程接收的每一个消息均是用一个数值来标识的,也就是传给窗口过程的message参数。
窗口过程处理消息时,必须返回0.窗口过程不予处理的全部消息应该被传给名为DefWindowsProc的Windows函数。
用switch语句来处理不一样消息,消息以WM_开头。
11 WM_CREATE消息
当Windows在处理CreateWindow函数时,窗口过程就会接收到WM_CREATE消息。
一般,窗口过程在WM_CREATE处理期间进行一次窗口初始化。
12 WM_PAINT消息
当窗口客户区域的一部分或所有变成“无效”时,必须进行刷新,WM_PAINT将通知程序。
在最初建立窗口的时候,整个客户区都是无效的,由于程序尚未在窗口上画任何东西。在调用UpdateWindow时,一般会触发第一个WM_PAINT消息,指示窗口过程在客户区域上画一些东西。
在改变程序窗口大小后,客户区也会变得无效,至于怎么变得无效由CS_引导的类风格选项肯定。
对WM_PAINT的处理几乎老是从一个BeginPaint调用开始,以一个EndPaint结束。
hdc = BeginPaint(hwnd, &pt); //do something;(如GetClientRect(hwnd, &rect);) EndPaint(hwnd, &pt);
hwnd是要刷新的窗口的窗口句柄。
pt是指向类型为PAINTSTRUCT的结构指针。
在BeginPaint调用中,若是客户区域的背景还未被擦除,就由Windows来擦除。而后使用注册窗口类的WNDCLASS结构中的hbrBackground域中第一的刷子来删除背景。
BeginPaint调用使整个客户区有效,并返回一个“设备环境句柄”。设备环境是指物理输出设备及其驱动程序。能够利用该“设备环境句柄”在客户区域显示文本和图形。
EndPaint调用释放设备环境句柄。
GetClientRect(hwnd, &rect);
第一个参数:程序的窗口句柄;
第二个参数:指向RECT类型的rectangle结构。该结构有4个LONG域,标识客户区域的尺寸。
当改变窗口大小时,WndProc经过调用GetClientRect来获取变化后的窗口大小,从新绘制客户区。
13 WM_DESTROY消息
当用户点击关闭按钮时发生。程序能够经过调用PostQuitMessage以标准方式响应WM_DESTROY消息;
PostQuitMessage(0); // 该函数在程序的消息队列插入一个WM_QUIT消息。
GetMessage对于除了WM_QUIT消息以外的从消息队列中取出的全部消息都返回非0值。而当GetMessage取到一个WM_QUIT消息时,返回0.
14 关闭程序时的消息传递
① 用户点击关闭按钮
② 产生WM_SYSCOMMAND消息;
③ 产生WM_CLOSE消息响应WM_SYSCOMMAND;
④ 产生WM_DESTROY消息响应WM_CLOSE;
⑤ 产生WM_QUIT消息响应WM_DESTROY。
15 进队消息和不进队消息
消息可以分为:进队消息和不进队消息。
进队消息是由Windows放入程序消息队列中的。在程序的消息循环中,从新返回并分配给窗口过程。
不进队消息在Windows调用窗口时直接发送给窗口过程。
也就是说,进队消息发送给消息队列,不进队消息发送给窗口过程。
在任何状况下,窗口过程都将得到窗口全部的消息。窗口过程是窗口的消息中心。
进队消息基本上是用户输入的结果,还包括时钟消息、刷新消息、退出消息。
不进队消息基本上是来自调用特定的Windows函数。
--------------------------------------------------------------------------------------------------
二 输出文本
16 有效矩形和无效矩形
窗口过程一旦接受到WM_PAINT消息以后,就准备更新整个客户区,但每每只需更新一个较小的区域。这个区域就称为“无效区域”。正是客户区内存在无效区域,才提示Windows将一个WM_PAINT消息放入消息队列。
Windows内部为每一个窗口保存一个“绘图信息结构”,这个结构包含了包围无效区域的最小矩形的坐标以及其余信息,这个矩形就叫作“无效矩形”。
若是在窗口过程处理WM_PAINT消息以前,客户区又有一个区域变为无效,那么Windows计算出一个包围两个无效区域的新的无效矩形,并将这个变化后的信息放在绘制信息结构中。
一个消息队列在一个时刻只能有一个WM_PAINT消息在队列中。
窗口过程能够调用InvalidateRect使客户区变为无效。若是消息队列包含一个WM_PAINT消息,那么Windows将计算出新的无效矩形;不然,就在消息队列中添加一个WM_PAINT消息。
在处理WM_PAINT消息期间,窗口过程在调用了BeginPaint以后,整个客户区就会变得有效。
程序也能够显式调用ValidateRect函数使客户区内的任意矩形区域变得有效。若是这条调用使整个客户区都有效,那么将在当前消息队列中删除WM_PAINT消息。
17 设备环境
要在窗口的客户区绘图,可使用Windows的图形设备接口GDI函数。
设备环境DC是GDI内部保存的数据结构。
设备环境与特定的显示设备有关。
设备环境中的有些值是图形化的“属性”,如指出颜色、背景色、坐标映射方式等。
当程序要绘图时,必须先获取设备环境句柄。在获取了该句柄以后,Windows用默认的属性值填充设备环境结构的内部各域。
当程序在客户区绘图完毕后,必须释放设备环境句柄。句柄被释放后再也不有效,也再也不使用。程序必须在处理单个消息期间获取和释放句柄。
18 获取设备环境句柄的方法之一
在使用WM_PAINT消息时,使用这种方法。它涉及到BeginPaint和EndPaint两个函数。
在处理WM_PAINT消息时,窗口过程首先调用BeginPaint。BeginPaint函数通常在准备绘制时致使无效区域的背景被擦除。BeginPaint返回的值是设备环境句柄,这一返回值一般被保持在叫作hdc的变量中。
HDC hdc;
HDC数据类型定义为32位的无符号数。
而后,程序就可使用须要设备环境句柄的GDI函数了。
调用EndPaint便可释放设备环境句柄。
通常地,处理WM_PAINT消息的形式以下:
case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //GDI函数 EndPaint(hwnd, &ps); return 0;
处理WM_PAINT消息时,必须成对地调用BeginPaint和EndPaint。
19 绘图信息结构
Windows为每个窗口保存一个绘图信息结构。这就是PAINTSTRUCT,定义以下:
typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT
在用BeginPaint时,Windows填充该结构的各个字段。用户程序只须要使用前三个字段。
hdc是设备环境句柄。
fErase一般被标识为FLASE,这意味着Windows已经擦除了无效矩形的背景。
若是程序经过调用Windows函数InvalidateRect使客户区中的矩形失效,那么该函数的最后一个参数会指定fErase的值。若是指定0,那么在稍后的PAINTSTRUCT里面的fErase会被设置为TRUE。
rcPaint是RECT结构,定义了无效矩形的边界。RECT结构中的left、top、right、bottom以像素点为单位。此时,Windows将绘图操做限制在此RECT结构定义的矩形范围内,若是要在无效矩形外绘图,应该在调用BeginPaint以前,使用以下调用:
InvalidateRect(hwnd, NULL, TRUE);
它将使整个客户区无效,并擦除背景。
20 取设备环境句柄的方法之二
要获得窗口客户区的设备环境句柄,能够调用GetDC来获取句柄。在使用完后调用ReleaseDC;
hdc = GetDC(hwnd); //使用GDI函数 ReleaseDC(hwnd, hdc);
GetDC和ReleaseDC函数必须成对地使用。
GetDC返回的设备环境句柄具备一个剪取矩形,等于整个客户区。
GetDC不会使任何无效区域变为有效,要是整个客户区有效,须要调用ValidateRect(hwnd, NULL);
通常能够调用GetDC和ReleaseDC来对键盘消息、鼠标消息做出反应。
21 TextOut细节
TextOut是用于显示文本的最经常使用的GDI函数。语法是:
TextOut(hdc, x, y, psText, iLength);
第一个参数:设备环境句柄,既能够是GetDC的返回值,也能够是BeginPaint的返回值。
第二个参数:定义客户区内字符串的开始位置的水平坐标。
第三个参数:定义客户区内字符串的开始位置的垂直坐标。
第四个参数:指向要输出的字符串的指针。
第五个参数:字符串中字符的个数。若是psText中的字符是Unicode的,那么串中的字节数就是iLength值的两倍。
设备环境还定义了一个剪取区域。
对于从GetDC获取的设备环境句柄,默认的剪取区是整个客户区。
对于从BeginPaint获取的设备环境句柄,默认的剪取区是无效区域。
Windows不会在剪取区域以外的任何位置显示字符串。
22 字符大小
要用TextOut显示多行文本,就必须肯定字体的字符大小,能够根据字符的高度来定位字符的后续行,以及根据字符的宽度来定位字符的后续列。
系统字体的字符高度和平均宽度取决于视频显示器的像素大小。
程序能够调用GetSystemMetrics函数来肯定关于用户界面构件大小的信息。
程序能够调用GetTextMetrics函数来肯定字体大小。
metric是度量的意思。
TEXTMETRIC的结构:
typedef struct tagTEXTMETRIC { LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; 其余字段 } TEXTMETRIC, *PTEXTMETRIC;
要使用GetTextMetrics函数,须要先定义一个一般被称为tm的结构变量:
TEXTMETRIC tm;
在须要肯定文本尺寸时,先要获取设备环境句柄,再调用GetTextMetrics:
hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); // 操做; ReleaseDC(hwnd,hdc);
23 文本尺寸
字体的纵向大小由5个值肯定:
① tmHeight,等于tmAscent加上tmDescent。这两个值表示了基线上下字符的最大纵向高度。
② tmAscent,基线以上的高度
③ tmDescent,基线如下的高度
④ tmInternalLeading,重音号和字符之间的距离,如ü中的u和两点的距离。
⑤ tmExternalLeading,通常用于多行文本间行距的调整。
字符的横向大小由2个值肯定:
① tmAveCharWidth,小写字母加权平均宽度。
② tmMaxCharWidth,字体中最宽字符的宽度。
对于等宽字体,tmAveCharWidth和tmMaxCharWidth这两个值相等。
大写字母的平均宽度比较复杂,若是:
① 字体是等宽字体,那么大写字母的平均宽度等于tmAveCharWidth。
② 字体是变宽字体,那么大写字母的平均宽度等于tmAveCharWidth*1.5。
判断字体是不是变宽字体,能够经过TEXTMETRIC结构中的tmPitchAndFamily域的低位判断,若是低位是1,那么是变宽字体,若是是0,那么是等宽字体。
大写字母宽度 = (tm.tmPitchAndFamily & 1 ? 3 : 2) / 2 * 小写字母宽度
24 格式化文本
在一次Windows对话期间,系统字体的大小不会改变,所以在程序运行过程当中,只须要调用一次GetTextMetric。最好是在窗口过程当中处理WM_CREATE消息时进行此调用。
假设要编写一个Windows程序,在客户区显示多行文本,这须要先获取字符宽度和高度。能够在窗口过程内定义两个变量来保存字符宽度和总的字符高度。
case WM_CREATE: hdc = BeginPaint(hwnd, &pt); GetTextMetric(hdc, &tm); cxChar = tm.tmAveCharWidth; 小写字母宽度 cyChar = tm.Height + tm.tmExternalLeading; 字母高度 cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) / 2 * tm.tmAveCharWidth; 大写字母宽度 EndPaint(hwnd, &pt); return 0;
25 客户区的大小
窗口最大化以后的客户区大小,能够经过以SM_CXFULLSCREEN和SM_CYFULLSCREEN为参数调用GetSystemMetric来得到。
要肯定客户区的大小,最好的方法是在窗口过程处理WM_SIZE消息。在窗口大小改变时,就会产生WM_SIZE消息。传给窗口过程的lParam参数的低位字中包含客户区的宽度x,高位字中包含客户区的高度y。要保存这些尺寸,能够定义两个int型变量来保存。
static int cxClient,cyClient;
而后在WM_SIZE消息处理中:
case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0;
用cyClient/cyChar能够获得客户区能够显示的文本总行数。
26 滚动条的范围和位置
每一个滚动条都有一个相关的范围和位置。这是一对整数。当滚动框在滚动条的顶部(左部)时,滚动框的位置是范围的最小值;在滚动条的底部(右部)时,滚动框的位置是范围的最大值。
在默认状况下,滚动条的范围是0~100,但将范围改变为更方便于程序的数值也是很容易的:
SetScrollRange(hwnd, iBar, iMin, iMax, bRedraw); /* hwnd为该窗口的句柄。 iBar为SB_VERT或SB_HORZ。 iMin和iMax为范围。 bRedraw,若是要Windows根据新范围重绘滚动条,则设置为TRUE。 */
滚动框的位置不是连续的,而是离散的整数值。
可使用SetScrollPos在滚动条范围内设置新的滚动框位置:
SetScrollPos(hwnd, iBar, iPos, bRedraw);
参数iPos是新位置,必须在iMin至iMax的范围内。
Windows提供了相似的函数GetScrollRange和GetScrollPos来获取滚动条的当前范围和位置。
27 滚动条消息
在用鼠标单击滚动条或者拖动滚动框时,Windows都给窗口过程发生WM_VSCROLL或WM_HSCROLL消息。在滚动条上的每一个鼠标动做都至少产生两个消息,一个在按下鼠标键时产生,一个在释放鼠标键时产生。
WM_VSCROLL和WM_HSCROLL也带有wParam和lParam消息参数。
lParam只用于做为子窗口而建立的滚动条(一般在对话框内)。
wParam消息参数被分为一个低位字和一个高位字。
低位字是一个数值,指出了鼠标对滚动条进行的操做。这个数值被看做一个“通知码”。通知码以SB开头。
#define SB_LINEUP 0 #define SB_LINELEFT 0 #define SB_LINEDOWN 1 #define SB_LINERIGHT 1 #define SB_PAGEUP 2 #define SB_PAGELEFT 2 #define SB_PAGEDOWN 3 #define SB_PAGERIGHT 3 #define SB_THUMBPOSITION 4 #define SB_THUMBTRACK 5 #define SB_TOP 6 #define SB_LEFT 6 #define SB_BOTTOM 7 #define SB_RIGHT 7 #define SB_ENDSCROLL 8
当把鼠标的光标放在滚动框上并按住鼠标键时,就产生SB_THUMBPOSITION和SB_THUMBTRACK消息。
当wParam的低位字是SB_THUMBTRACK时,wParam的高位字是用户在拖动滚动框时的当前位置。
当wParam的低位字是SB_THUMBPOSITION时,wParam的高位字是用户释放鼠标后滚动框的最终位置。
28 滚动条信息函数
滚动条文档指出SetScrollPos、SetScrollRange、GetScrollPos、GetScrollRange函数是过期的。
在Win32 API中,升级了2个滚动条函数,称做SetScrollInfo和GetScrollInfo。这些函数完成上述4个函数的所有功能,并增长了2个新特性。
第一个功能设计滚动框的大小。滚动框的大小称做页面大小。算法是:
滚动框大小 / 滚动长度 ≈ 页面大小 / 范围 ≈ 显示的文档数量 / 文档的总大小
可使用SetScrollInfo来设置页面大小。
第二个功能是GetScrollInfo函数,它能够获取32位的范围值。
SetScrollInfo和GetScrollInfo函数的语法是:
SetScrollInfo(hwnd, iBar, &si, bRedraw); GetScrollInfo(hwnd, iBar, &si); //ar参数是SB_VERT或SB_HORZ。 //Redraw能够是TRUE或FALSE,指出了是否要Windows从新绘制计算了新信息后的滚动条。 //函数的第三个参数是SCROLLINFO结构,定义为: typedef struct tagSCROLLINFO { UINT cbSize; UINT fMask; int nMin; int nMax; UINT nPage; int nPos; int nTrackPos; } SCROLLINFO
在程序中,能够定义以下的SCROLLINFO结构类型:
SCROLLINFO si;
在调用SetScrollInfo或GetScrollInfo函数以前,必须将cbSize自动设置为结构的大小:
si.cbSize = sizeof(si);或si.cbSize = sizeof(SCROLLINFO);
fMask:把fMask字段设置为以SIF前缀开头的一个或多个标识,来获取或设置里面的结构中域的值。
① SIF_RANGE:用于获取或设置滚动条的范围
② SIF_POS:用于获取或设置滚动框的位置
③ SIF_PAGE:用于获取或设置滚动条的页面大小
④ SIF_TRACKPOS:用于获取或设置滚动框移动时的位置
-------------------------------------------------------------------------------------------------------------------
三 图形基础
29 GDI基础(微软GDI文档GDI Reference)
图形设备接口GDI是Windows的子系统,它负责在视频显示器和打印机上显示图形。
Windows NT中的图形主要由GDI32.DLL动态连接库输出的函数来处理。
GDI的主要目的之一是支持与设备无关的图形。
图形输出设备分为两大类:光栅设备和矢量设备。
大多数PC机显示器、打印机都是光栅设备。
绘图仪是矢量设备。
组成GDI的函数能够分为这样几类:
① 获取(或重建)和释放(或清除)设备环境的函数;
② 获取有关设备环境信息的函数;
③ 绘图函数;
④ 设置和获取设备环境参数的函数;
⑤ 使用GDI对象的函数。
30 GDI图元
在屏幕或打印机上显示的图形类型自己能够被分为几类,一般被称为“图元”。
① 直线和曲线
② 填充区域
③ 位图:位图是位的矩形数组,位对应于显示设备上的像素,它们是光栅图形的基本工具。GDI支持两种类型的位图——老的“设备有关”位图,新的“设备无关”位图。
④ 文本
31 GDI其余方面
① 映射模式和变化;
② 元文件:元文件是以二进制形式存储的GDI命令的集合。元文件主要用于经过剪贴板传输矢量图形表示。
③ 区域:区域是形状任意的复杂区;
④ 路径:路径是GDI内部存储的直线和曲线的集合;
⑤ 剪裁:绘图能够限制在客户区的某一部分中,这就是剪裁。剪裁一般是经过区域或者路径定义的。
⑥ 调色板:定制调色板一般限于显示256色的显示器。Windows仅保留这些色彩之中的20种供系统使用,但能够改变其余236种色彩。
⑦ 打印
32 进一步探讨设备环境
想在一个图形输出设备上绘图时,首先必须得到一个设备环境的句柄。将句柄返回给程序时,Windows就给了用户使用设备的权限。而后在GDI函数中将该句柄做为一个参数,向Windows标识想在其上进行绘图的设备。
(1)获取设备环境句柄
若是在处理一条消息时获取了设备环境句柄,应该在退出窗口函数以前释放它。
获取设备环境句柄的几种方法:
① 在处理WM_PAINT消息时,使用BeginPaint和EndPaint调用
hdc = BeginPaint(hwnd, &ps); //GDI操做 EndPaint(hwnd, &ps);
注:变量ps是类型为PAINTSTRUCT的结构,该结构的hdc字段是BeginPaint返回的设备环境句柄。PAINTSTRUCT结构包含了一个名为rcRect的RECT结构,该结构定义了包围窗口无效范围的矩形。使用从BeginPaint得到的设备环境句柄,只能在这个区域内绘图。BeginPaint调用使这个区域有效。
② 能够在处理非WM_PAINT消息时获取设备环境句柄
hdc = GetDC(hwnd); //GDI操做 ReleaseDC(hwnd, hdc);
注:这个设备环境适用于窗口句柄为hwnd的客户区。这个调用能够在整个客户区上绘图。
③ Windows程序还能够获取适用于整个窗口的设备环境句柄
hdc = GeWindowstDC(hwnd); //GDI操做 ReleaseDC(hwnd, hdc);
注:这个设备环境除了客户区以外,还包括窗口的标题栏、菜单、滚动条和框架。若是要使用该函数,必须捕获WM_NCPAINT消息。
④ 获取整个屏幕的设备环境句柄
hdc = CreateDC(TEXT(“DISPLAY”), NULL, NULL, NULL);
原型是:
hdc = CreateDC(pszDriver, pszDevice, pszOutput, pData); //GDI操做 DeleteDC(hdc);
⑤ 若是只须要获取关于某设备环境的一些信息,而并不进行任何绘画,在这种状况下,可使用CreateIC来获取一个“信息描述表”的句柄,其参数和CreateDC同样。
⑥ 一个设备环境一般是指一个物理显示设备。一般,须要获取有关该设备的信息,其中包括显示器的显示尺寸和色彩范围。能够经过GetDeviceCaps函数来获取这些信息。
iValue = GetDeviceCaps(hdc, iIndex);
参数iIndex的取值为WINGDI.H头文件中定义的29个标识符之一。
33 用TextOut输出整型的方法
设一开始有整型:int i = 100;
要用TextOut函数将i输出,须要用到三个函数:
① wsprintf
② TEXT宏
③ TextOut
首先得先说明下wsprintf的原型:
int wsprintf(LPTSTR lpOut, LPCTSTR lpFmt,...); /* 一个参数:缓冲区,是一个字符数组,通常定义为TCHAR型。 第二个参数:格式字符串,由于第一个参数是TCHAR类型,必定要和TEXT宏联合使用,这样才能在不一样的编译环境下均可以顺利编译。 后续参数:要输出的的整型变量。 * //第一个参数:要定义一个TCHAR的字符数组做为缓冲区。 TCHAR szBuffer[10]; //足够大就好了 //第二个参数,须要使用到TEXT宏。 //XT宏的原型: TEXT(LPTSTR string //ANSI or Unicode string); //处理要转换的整型,具体用法是: TEXT(“%d”); //上述的两个调用应该写成: int iLength = 0; //用来保存字符串中的字符个数; iLength = wsprintf(szBuffer, TEXT(“%d”), i); //语句的做用是:将i存进szBuffer中,返回szBuffer存有的字符个数到iLength中。 TextOut的原型是: TextOut(hdc, x, y, psText, iLength); /* 数是设备环境句柄; 第二个参数是输出的文本的x坐标; 第三个参数是输出的文本的y坐标; 第四个参数是是指向要输出的字符串的指针; 第五个参数是字符串中的字符个数; */ //那么TextOut函数应该写成: TextOut(hdc, x, y, szBuffer, iLength);
34 设备的大小
使用GetDeviceCaps函数能获取有关输出设备物理大小的信息。
对于打印机,用“每英寸的点数dpi”表示分辨率。
对于显示器,用水平和垂直的总的像素数来表示分辨率。
用“像素大小”或“像素尺寸”表示设备水平或垂直显示的总像素数。
用“度量大小”或“度量尺寸”表示以每英寸或毫米为单位的显示区域的大小。
像素大小 / 度量大小 = 分辨率
使用SM_CXSCREEN和SM_CYSCREEN参数从GetDeviceCaps获得像素大小;
使用HORZSIZE和VERTSIZE参数从GetDeviceCaps获得度量大小;
二者相除就能够获得水平分辨率和垂直分辨率。
若是设备的水平分辨率和垂直分辨率相等,就称该设备具备“正方形像素”。
由于整个屏幕的度量大小是固定的,因此能够根据分辨率调整水平或垂直显示的像素数。若是分辨率小,那么“像素大小”也就小,也就是说,总像素数少了,那么每一个像素的尺寸也就变得大些。
35 字体的大小
如今讨论字体的大小问题,这里不是说字号,而是说字体显示的dpi值。Windows系统默认是每英寸96点,所另一种选择,就是每英寸120点。
咱们在调整分辨率的时候,从小分辨率变化到大分辨率时,会以为图标的文字变小,那是由于在大分辨率下,每一个像素的面积变小,假设一个字须要100个像素来显示,那么从小分辨率变化到大分辨率时,字的总面积就变小了,因此字的大小也就发生变化,而这一变化是字体的大小变化,而不是该字的字号发生变化。
在传统的排版中,字体的字母大小由“磅”表示。1磅≈1/72英寸,在计算机排版中1磅正好为1/72英寸。
理论上,字体的磅值是从字体中最高的字符顶部到字符下部的字符底部的距离,其中不包括重音号。根据TEXTMETRIC结构,字体的磅值等于tmHeight – tmInternalLeading。
36 关于色彩 “全色”视频显示器的分辨率是每一个像素24位:8位红色、8位绿色、8位蓝色。 “高彩色”显示分辨率是每一个像素16为:5位红色、6位绿色、5位蓝色。 显示256种颜色的视频适配器每一个像素须要8位。然而这些8位的值通常由定义实际颜色的调色板表组织。 使用GetDeviceCaps可使程序员肯定视频适配器的存储组织,以及可以表示的色彩数目。 这个调用返回色彩平面的数目:iPlanes = GetDeviceCaps(hdc, PLANES); 这个调用返回每一个像素的色彩位数:iBitsPixel = GetDeviceCaps(hdc, BITSPIXEL); 大多数彩色图形显示设备要么使用多个色彩平面,要么每像素有多个色彩位,可是不能同时两者兼用;即这两个调用必须有一个返回1.(通常都是第一个返回1)。 在大多数GDI函数调用中,使用COLORREF值(32位)来表示一种色彩。
理论上,COLORREF能够指定2的24次方或1600万种色彩。 这个无符号长整数经常称为一个“RGB色彩”。在使用RGB(r, g, b);宏时注意参数的顺序是红、绿、蓝。而在无符号长整数中,由高位到低位是0、蓝、绿、红。 当三个参数都是0时,表示黑色,当三个参数都是255时,表示白色。 黑色 = RGB(0,0,0) = 0x00000000 白色 = RGB(255, 255, 255) = 0x00FFFFFF
37 保存设备环境
一般,在调用GetDC或BeginPaint时,Windows会用默认值建立一个新的设备环境,对设备环境其属性所作的一切修改在调用ReleaseDC或EndPaint被释放掉。
若是须要使用非默认的设备环境属性,则必须在每次获取设备环境句柄时初始化设备环境。
若是须要在释放设备环境以后,仍然保存程序中对设备环境所作的改变,以便在下一次调用GetDC和BeginPaint时它们仍起做用。则应该在窗口类那将CS_OWNDC标志包含进窗口类风格中。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
如今,基于这个窗口类所建立的每一个窗口都将拥有本身的设备环境,它一直存在,直到窗口被删除。
若是使用了CS_OWNDC风格,就只需初始化设备环境一次,能够在处理WM_CREATE消息期间完成这一操做。
CS_OWNDC风格只影响GetDC和BeginPaint得到的设备环境,不影响其余函数得到的设备环境,如GetWindowDC得到的设备环境。
在某些状况下,能够须要改变某些设备环境,用改变后的属性进行绘图,而后又要恢复回改变前的属性。这时,能够经过以下调用来保存设备环境的状态。
//保存: int idSaved = 0; idSaved = SaveDC(hdc); //恢复: RestoreDC(hdc, idSaved);
也能够不保存SaveDC的返回值,这时候若是要恢复,就只能恢复到最近保存的状态,RestoreDC(hdc, -1);
38 写像素
写像素SetPixel(hdc, x, y, crColor);其中:
hdc是设备环境句柄;
x, y是像素点的坐标;
crColor是要设置的颜色,通常能够用RGB(r, g, b)设置。
39 线条
几种画线函数:
① LineTo:画直线
② Polyline和PolylineTo:画一系列相连的直线
③ PolyPolyline:画多组相连的线
④ Arc和ArcTo和AngleArc:画椭圆线
⑤ PolyBezier和PolyBezierTo:画贝塞尔线条
⑥ PolyDraw:画一系列相连的线以及贝塞尔线条
几种填充函数:
① Rectangle:画矩形
② Ellipse:画椭圆
③ RoundRect:画带圆角的矩形
④ Pie:画椭圆的一部分,使其看起来像一个扇形
⑤ Chord:画椭圆的一部分,使其看起来像弓形
⑥ Polygon:画多边形
⑦ PolyPolygon:画多个多边形
设备环境的5个属性影响着用这些函数所画线条的外观:
① 当前画笔的位置;
② 画笔;
③ 背景方式;
④ 背景色;
⑤ 绘图模式。
画一条直线,必须调用2个函数,第一个函数指定了线的开始点坐标,第二个函数指出了线的终点坐标:
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
MoveToEx不会画线,只是设置了设备环境的“当前位置”属性。而后LineTo函数从当前的位置到它所指定的点画一直线。在默认的设备环境中,当前位置最初是在点(0,0)。
MoveToEx最后一个参数是指向POINT结构的指针。从该函数返回后,POINT结构的x和y字段指出了以前的“当前位置”,若是不须要这个信息,直接填NULL。
若是须要获取当前位置,先定义一个POINT的结构变量pt,而后经过下面的调用:
GetCurrentPositionEx(hdc, &pt);
几个函数的原型:
Rectangle(hdc, xLeft, yTop, xRight, yBottom);
Ellipse(hdc, xLeft, yTop, xRight, yBottom);
RoundRect(hdc, xLeft, yTop, xRight, yBottom, xCorner, yCorner);
Chord(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Pie(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Arc(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
一个二维的贝塞尔线条由4个点定义——两个端点和两个控制点。曲线的控制点固定,将曲线从两个端点间的直线处拉伸构造曲线。
40 使用画笔
调用任何画笔函数时,Windows使用设备环境中当前选中的“画笔”来画线。画笔决定线的色彩、宽度、线型。线型能够是实线、点划线、虚线,默认设备环境中画笔是BLACK_PEN,一个像素宽,实线。
Windows提供三种现有画笔,分别是:BLACK_PEN, WHITE_PEN和NULL_PEN。
Windows使用句柄来引用画笔。用HPEN的类型定义,即画笔的句柄。
HPEN hPen;
调用GetStockObject,能够得到现有画笔的句柄。
hPen = GetStockObject(WHITE_PEN);
调用SelectObject将画笔选进设备环境。
SelectObject(hdc,hPen);
SelectObject的返回值是选进前设备环境的画笔句柄。
使用CreatePen或CreatePenIndirect建立一个“逻辑画笔”,这仅仅是对画笔的描述。这些函数返回逻辑画笔的句柄,而后调用SelectObject将画笔选进设备环境,以后才可使用新的画笔来画线。
在任什么时候候,只能有一种画笔选进设备环境。
在释放设备环境或在选择了另外一种画笔到设备环境中以后,就能够调用DeleteObject来删除所建立的逻辑画笔。
逻辑画笔是一种“GDI对象”,GDI对象有六种:画笔、刷子、位图、区域、字体、调色板。
CreatePen的原型是:
HPEN CreatePen(iPenStyle, iWidth, crColor);
iPenStyle参数肯定画笔是实线、虚线仍是点线。
iWidth参数肯定线宽,若是iPenStyle不是实线,且iWith大于1,那么画笔将变成实线。
crColor是RGB颜色。
获取当前画笔句柄:
hPen = GetCurrentObject(hdc, OBJ_PEN);
还能够创建一个逻辑画笔LOGPEN结构,调用CreatePenIndirect来建立画笔。
LOGPEN logpen;
此结构有三个成员:UINT lopnStyle 是画笔线型;POINT lopnWidth是按逻辑单位度量的画笔宽度,只用其中的x值;COLORREF lopnColor是画笔颜色
41 填充空隙
点式画笔和虚线画笔的空隙的着色取决于设备环境的两个属性——背景模式和背景颜色。默认的背景模式是OPAQUE,在这种方式下,Windows使用背景色填充空隙,默认的背景色为白色。
下述调用用来改变和获取Windows用来填充空隙的背景色:
改变:SetBkColor(hdc, crColor);
获取:GetBkColor(hdc);
下述调用用来改变和获取背景模式:
改变:SetBkMode(hdc, 模式);
模式:TRANSPARENT,忽略背景色,而且不填充空隙。
OPAQUE默认。
获取:GetBkMode(hdc);
42 绘图方式
设备环境中定义的绘图方式也影响显示器上所画线的外观。
当Windows使用画笔来画线时,实际上执行画笔像素与目标位置处原来像素之间的某种按位布尔运算。像素间的按位布尔运算叫作“光栅运算”,简称为“ROP”。因为画一条直线只涉及两种像素(画笔和目标),所以这种布尔运算又称为“二元光栅运算”,简称为“ROP2”。
在默认设备环境中,绘图方式定义为R2_COPYPEN,这意味着Windows只是将画笔像素复制到目标像素代替之。
Windows定义了16种不一样的ROP2码,用来设置不一样的绘图方式。
设置绘图方式:SetROP2(hdc, iDrawMode);
获取绘图方式:iDrawMode = GetROP2(hdc);
43 绘制填充区域
Windows中有7个用来画带边缘的填充图形的函数:
① Rectangle:画矩形
② Ellipse:画椭圆
③ RoundRect:画带圆角的矩形
④ Pie:画椭圆的一部分,使其看起来像一个扇形
⑤ Chord:画椭圆的一部分,使其看起来像弓形
⑥ Polygon:画多边形
⑦ PolyPolygon:画多个多边形
Windows用设备环境中选择的当前画笔来画图形的边界框,边界框还使用当前背景方式、背景色彩和绘图方式,跟画线时同样。
图形以当前设备环境中选择的刷子来填充。默认状况下,使用现有对象,这意味着图形内部将画成白色。
Windows定义6种现有刷子:WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH。
也能够本身定义刷子 HBRUSH hBrush;
经过GetStockObject来获取现有刷子:
hBrush = GetStockObject(WHITE_BRUSH);
经过SeletctObject将刷子选进设备环境:
SelectObject(hdc, bBrush);
若是要画一个没有边界框的图形,能够将NULL_PEN选进设备环境。
SelectObject(hdc, GetStockObject(NULL_PEN));
若是要画一个没有填充内部的图像,能够将NULL_BRUSH选进设备环境。
SelectObject(hdc, GetStockObject(NULL_BRUSH));
画多边形函数的原型:
Polygon(hdc, apt, iCount);
apt参数是POINT结构的一个数组,iCount是点的数目。若是该数组中的最后一个点和第一个点不一样,则Windows将会再加一条线,将最后一个点与第一个点连起来。
画多个多边形函数的原型:
PolyPolygon(hdc, apt, aiCounts, iPolyCount);
apt数组具备所有多边形的全部点。
aiCounts数组给出了多边形的端点数。
iPolyCount给出了所画的多边形的个数。
44 用画刷填充内部
Rectangle、RoundRect、Ellipse、Chord、Pie、Polygon和PolyPolygon图形的内部是用选进设备环境的当前画刷来填充的。画刷是一个8×8的位图,它水平和垂直地重复使用来填充内部区域。
Windows有5个函数,能够本身建立逻辑画刷,而后用SelectObject将画刷选进设备环境。
① hBrush = CreateSolidBrush(crColor); 纯颜色刷子
② hBrush = CreateHatchBrush(iHatchStyle, crColor); 带影射线的刷子
crColor是影线的颜色,影线的间隙用设备环境定义的背景方式和背景色来着色。
③ CreatePatternBrush()
④ CreateDIBPatternBrushPt() 基于位图的刷子
⑤ hBrush = CreateBrushIndirect(&logbrush);
该函数包含其余4个函数。
变量logbrush是一个类型为LOGBRUSH的结构,该结构有三个字段
UINT lbStyle;
COLORREF lbColor;
LONG lbHatch;
45 矩形函数
Windows包含了几种使用RECT结构和“区域”的绘图函数。区域就是屏幕上的一块地方,是矩形,多边形和椭圆的组合。
FillRect(hdc, &rect, hBrush); //用指定画刷来填充矩形。该函数不须要事先将画刷选进设备环境。 FrameRect(hdc, &rect, hBrush); //使用画刷画矩形框,但不填充矩形。 InvertRect(hdc, &rect); //将矩形中全部像素反转。
经常使用矩形函数:
① SetRect(&rect, xLeft, yTop, xRight, yBottom); 设置矩形的4个字段值。
② OffsetRect(&rect, x, y); 将矩形沿x轴和y轴移动几个单元。
③ InflateRect(&rect, x, y); 增减矩形尺寸
④ SetRectEmpty(&rect); 将矩形各字段设为0
⑤ CopyRect(&DestRect, &SrcRect); 将矩形复制给另外一个矩形。
⑥ IntersectRect(&DestRect, &SrcRect1,&ScrRect2);获取两个矩形的交集
⑦ UnionRect(&DestRect, &SrcRect1,&ScrRect2); 获取两个矩形的并集
⑧ bEmpty = IsRectEmpty(&rect); 肯定矩形是否为空
⑨ binRect = PtinRect(&rect, point);肯定点是否在矩形内
46 建立和绘制区域
区域是对显示器上一个范围的描述,这个范围是矩形、多边形和椭圆的组合。
区域能够用于绘制和剪裁,经过将区域选进设备环境,就能够用区域来进行剪裁。
当建立一个区域时,Windows返回一个该区域的句柄,类型为HRGN。
HRGN hRgn;
① 建立矩形区域:
hRgn = CreateRectRgn(xLeft, yTop, xRight, yBottom);
或
hRgn = CreateRectRgnIndirect(&rect);
② 建立椭圆区域:
hRgn = CreateEllipticRgn(xLeft, yTop, xRight, yBottom);
或
hRgn = CreateEllipticRgnIndirect(&rect);
③ 建立多边形区域:
hRgn = CreatePolygonRgn(&point, iCount, iPolyFillMode);
point参数是个POINT类型的结构数组;
iCount是点的数目;
iPolyFillMode是ALTERNATE或者WINDING
④ 区域的融合
iRgnType = CombineRgn(hDestRgn, hSrcRgn1, hSrcRgn2, iCombine);
这一函数将两个源区域组合起来并用句柄hDestRgn指向组合成的目标区域。
iCombine参数说明了hSrcRgn1和hSrcRgn2是怎么组合的。 RGN_AND 公共部分 RGN_OR 所有 RGN_XOR 所有除去公共部分 RGN_DIFF hSrcRgn1不在hSrcRgn2的部分 RGN_COPY hSrcRgn1的所有,忽略hSrcRgn2 //区域的句柄能够用到4个绘图函数: FillRgn(hdc, hRgn, hBrush); FrameRgn(hdc, hRgn, xFrame, yFrame); //xFrame, yFrame是画在区域周围边框的宽度和高度。 InvertRgn(hdc, hRgn); PaintRgn(hdc, hRgn);
47 矩形与区域的剪裁
区域也在剪裁中扮演了一个角色。
InvalidateRect函数使显示的一个矩形区域失效,并产生一个WM_PAINT消息。
InvalidateRect(hwnd, NULL, TRUE); 清除客户区;
能够经过调用GetUpdateRect来获取失效矩形的坐标。
使用ValidateRect函数使客户区的矩形有效。
当接收到一个WM_PAINT消息时,无效矩形的坐标能够从PAINTSTRUCT结构中获得,该结构是用BeginPaint函数填充的。
Windows中有两个做用于区域而不是矩形的函数:
InvalidateRgn(hwnd, hRgn, bErase);
和
ValidateRgn(hwnd, hRgn);
因此当接收到一个WM_PAINT消息时,可能由无效区域引发的。剪裁区域不必定是矩形。
SelectObject(hdc, hRgn);
或
SelectClipObject(hdc, hRgn);
经过将一个区域选进设备环境来建立本身的剪裁区域。
---------------------------------------------------------------------------------------------------------------------------
四 键盘
48 键盘基础
Windows程序得到键盘输入的方式:键盘输入以消息的形式传递给程序的窗口过程。
Windows用8种不一样的消息来传递不一样的键盘事件。
Windows程序使用“键盘加速键”来激活通用菜单项。加速键一般是功能键或字母同ctrl键的组合。Windows将这些键盘加速键转换为菜单命令消息。
程序用来从消息队列中检索消息的MSG结构包括hwnd字段。此字段指出接收消息的窗口句柄。消息循环中的DispatchMessage函数向窗口过程发生该消息,此窗口过程与须要消息的窗口相联系。当按下键盘上的键时,只有一个窗口过程接收键盘消息,而且此消息包括接收消息的窗口句柄。
接收特定键盘事件的窗口具备输入焦点。
窗口过程经过捕获WM_SETFOCUS和WM_KILLFOCUS消息来断定它的窗口什么时候拥有输入焦点。WM_SETFOCUS指示窗口正在获得输入焦点,WM_KILLFOCUS表示窗口正在失去输入焦点。
当用户按下并释放键盘上的键时,Windows和键盘驱动程序将硬件扫描码转换为格式消息。Windows在“系统消息队列”中保存这些消息。系统消息队列是单消息队列,它由Windows维护,用于初步保存用户从键盘和鼠标输入的信息。只有当Windows应用程序处理完前一个用户输入消息时,Windows才会从系统消息队列中取出下一个消息,并放入应用程序的消息队列。
49 击键和字符
应用程序从Windows接受的关于键盘事件的消息能够分为击键和字符两类。
按下键是一次击键,释放键也是一次击键。
对产生可显示字符的击键组合,Windows不只给程序发送击键消息,并且还发送字符消息。有些键不产生字符,对于这些键,Windows只产生击键消息。
50 击键消息
当按下一个键时,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入有输入焦点的窗口的消息队列。
当释放一个键时,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息队列。
能够有多个KEYDOWN,但相对来讲只有一个KEYUP。
经过调用GetMessageTime能够得到按下或者释放键的相对时间。
51 系统击键和非系统击键
WM_SYSKEYDOWN和WM_SYSKEYUP中的“SYS”表明“系统”,它表示该击键对Windows比对Windows应用程序更加剧要。
程序一般能够忽略WM_SYSKEYDOWN和WM_SYSKEYUP消息,并将它们传送到DefWindowProc。
若是想在本身的窗口过程当中包括捕获系统击键的代码,那么在处理这些消息以后再传送到DefWindowProc,Windows就仍然能够将它们用于一般的目的。
对全部4类击键消息,wParam是虚拟键代码,表示按下或释放的键。而lParam则包含属于击键的其余数据。
52 虚拟键码
虚拟键码保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP消息的wParam参数中。
53 lParam信息
在4个击键消息中,wParam消息参数含有虚拟键码,而lParam消息参数则含有对了解击键很是有用的其余信息。
在lParam的32位中,分为6个字段。
0~15:重复计数;
16~23:8位OEM;
24:扩展键标志;
29:环境代码;
30:键的先前状态;
31:转换状态。
(1)重复计数
重复计数是该消息所表示的击键次数。大多数状况下,重复计数设置为1。
在KEYDOWN消息中,重复计数能够大于1,表示该键重复n次。
在KEYUP消息中,重复计数老是1.
(2)OEM扫描码
OEM扫描码是由硬件(键盘)产生的代码。Windows程序可以忽略几乎全部的OEM扫描码,除非它取决于键盘的物理布局。
(3)扩展位标识
若是击键结构来自IBM加强键盘的附加键之一,那么扩展键标志为1.
(4)环境代码
环境代码在按下Alt键后为1。对WM_SYSKEYDOWN和WM_SYSKEYUP消息,这一位老是1;对WM_KEYDOWN和WM_KEYUP消息,这一位老是0;
可是有2个例外:
① 若是活动窗口最小化了,则它没有输入焦点。这时候全部的击键都产生WM_SYSKEYDOWN和WM_SYSKEYUP消息。
② 若是Alt键未按下,则环境代码域被设置为0.
(5)键的先前状态
若是在此以前键是释放的,则键的先前状态为0,不然为1.
对WM_KEYUP或者WM_SYSKEYUP消息,它老是设置为1.
对WM_KEYDOWN和WM_SYSKEYDOWN消息,此位能够是1,也能够是0.
(6)转换状态
若是键正在被按下,则转换状态为0;
若是键正在被释放,则转换状态为1.
对WM_KEYDOWN和WM_SYSKEYDOWN消息,此域为0;
对WM_KEYUP或者WM_SYSKEYUP消息,此域为1.
54 换挡状态
在处理击键消息是,可能须要知道是否按下了换挡键(Shift, Ctrl, Alt)或开关键(Caps Lock, Num Lock, Scroll Lock)。经过调用GetKeyState函数,就能得到此信息。
int iState;
iState = GetKeyState(VK_SHIFT);
若是按下了Shift,则iState值为负。(高位被置1)。
55 字符消息
在WinMain中,有这样一个消息循环:
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* GetMessage函数用队列中的下一个消息填充msg结构的字段。 DispatchMessage以此消息为参数调用适当的窗口过程。 TranslateMessage函数将击键消息转换为字符消息。若是消息为WM_KEYDOWN或WM_SYSKEYDOWN,而且击键与换挡状态相结合产生一个字符,
则TranslateMessage把字符消息放入消息队列中。此字符消息将是GetMessage从消息队列中获得的击键消息以后的下一个消息。 */
56 四类字符消息
字符消息能够分为四类:WM_CHAR、WM_DEADCHAR和WM_SYSCHAR、WM_SYSDEADCHAR。
其中,WM_CHAR、WM_DEADCHAR是从WM_KEYDOWN消息获得的。WM_SYSCHAR、WM_SYSDEADCHAR是从WM_SYSKEYDOWN消息获得的。
大多数状况下,Windowns程序会忽略除WM_CHAR消息以外的任何消息。
伴随四个字符消息的lParam参数与产生字符代码消息的击键消息的lParam参数相同。不过,参数wParam不是虚拟键码,而是ANSI或Unicode字符代码。
57 消息顺序
由于TranslateMessage函数从WM_KEYDOWN和WM_SYSKEYDOWN消息产生了字符消息,因此字符消息是夹在击键消息之间传递给窗口过程的。
(1)若是按下A键,再释放A键,将产生3个消息。
① WM_KEYDOWN “A”的虚拟键码(0x41)
② WM_CHAR “a”的字符代码(0x61)
③ WM_KEYUP “A”的虚拟键码(0x41)
(2)若是按下Shift键和A键,而后释放,将产生5个消息。
① WM_KEYDOWN 虚拟键码VK_SHIFT
② WM_KEYDOWN “A”的虚拟键码(0x41)
③ WM_CHAR “a”的字符代码(0x61)
④ WM_KEYUP “A”的虚拟键码(0x41)
⑤ WM_KEYUP 虚拟键码VK_SHIFT
(3)若是按住A键不释放,将自动重复产生一系列的击键,那么对每一个WM_KEYDOWN消息,就会获得一个字符消息。
① WM_KEYDOWN “A”的虚拟键码(0x41)
② WM_CHAR “a”的字符代码(0x61)
③ WM_KEYDOWN “A”的虚拟键码(0x41)
④ WM_CHAR “a”的字符代码(0x61)
⑤ WM_KEYDOWN “A”的虚拟键码(0x41)
⑥ WM_CHAR “a”的字符代码(0x61)
⑦ WM_KEYUP “A”的虚拟键码(0x41)
58 处理控制字符
处理击键和字符消息的基本规则是:若是须要读取输入到窗口的键盘字符,那么能够处理WM_CHAR消息。若是须要读取光标键、功能键、Delete键、Insert键、Shite键、Ctrl键、以及Alt键,那么能够处理WM_KEYDOWN消息。
对于删除、制表、回车、退出键,它们产生WM_CHAR消息和WM_KEYDOWN消息,应该将这些按键处理成控制字符而不是虚拟键码。
case WM_CHAR: { case ‘\b’: 删除键← ….. break; case ‘\t’: 制表键tab ….. break; case ‘\n’: 回车 ….. break; case ‘\r’: 换行 ….. break; default: ….. break; }
59 插入符函数(鼠标闪烁)
主要有8个插入符函数:
① CreateCaret 建立与窗口相关的插入符;
② SetCaret 在窗口中设置插入符的位置
③ ShowCaret 显示插入符
④ HideCaret 隐藏插入符
⑤ DestroyCaret 撤销插入符
⑥ GetCaretPos 获取插入符位置
⑦ GetCaretBlinkTime 获取插入符闪烁时间
⑧ SetCaretBlinkTime 设置插入符闪烁时间
在Windows中,插入符定义为水平线、与字符大小相同的方框,或者与字符等高的竖线。
只有当窗口有输入焦点时,窗口内显示插入符才有意义。
经过处理WM_SETFOCUS和WM_KILLFOCUS消息,程序就能够肯定它是否有输入焦点。窗口过程在有输入焦点的时候接受WM_SETFOCUS消息,失去输入焦点的时候接受WM_KILLFOCUS消息。
使用插入符的规则:窗口过程在WM_SETFOCUS消息期间调用CreateCaret,在处理WM_KILLFOCUS消息期间调用DestroyCaret。
插入符刚建立的时候是隐蔽的,必须显式使用ShowCaret函数将插入符设为可见。
当窗口过程处理一个非WM_PAINT消息并且但愿在窗口内绘制某些东西时,必须调用HideCaret隐藏插入符。在绘制完毕以后,再调用ShowCaret显式插入符。
---------------------------------------------------------------------------------------------------------------------------
五 鼠标
60 鼠标基础
用GetSystemMetrics函数来肯定鼠标是否存在:
fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
要肯定所安装鼠标上键的个数,可以使用:
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); //若是没有安装鼠标,返回0.
当Windows用户移动鼠标时,Windows在显示屏上移动一个称为“鼠标光标”的小位图。鼠标光标有一个指向显示屏上精确位置的单像素的“热点”。
Windows支持几种预约义的鼠标光标,程序可使用这些光标。最多见的是称为IDC_ARROW的斜箭头。热点在箭头的顶端。IDC_CROSS光标的热点在十字交叉线的中心。IDC_WAIT光标是一个沙漏,用于指示程序正在运行。
鼠标键动做的术语:
① 单击 按下并放开一个鼠标键
② 双击 快速按下并放开鼠标键两次
③ 拖曳 按住鼠标键并移动鼠标
对于三键鼠标,三个键分别被称为左键、中键、右键。在Windows头文件中定义的与鼠标有关的标识符使用缩写LBUTTON、MBUTTON、RBUTTON。
61 客户区鼠标消息
Windows只把键盘消息发送给拥有输入焦点的窗口。鼠标消息与此不一样,只要鼠标跨越窗口或者在某窗口中按下鼠标键,那么窗口过程就会收到鼠标消息,而无论该窗口是否活动或者拥有输入焦点。
当鼠标移过窗口的客户区时,窗口过程收到WM_MOUSEMOVE消息。当在窗口的客户区按下或者释放一个鼠标键时,窗口过程会收到以下消息:
键:
按下
释放
双击键
左:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
中:
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MBUTTONDBLCLK
右:
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_RBUTTONDBLCLK
仅当定义的窗口类能接收DBLCLK消息以后,窗口过程才能接收到双击消息。
对于全部这些消息来讲,其lParam值均含有鼠标的位置:低位是x坐标,高位是y坐标。这两个坐标是相对于窗口客户区左上角的位置。
wParam的值指示鼠标键和Shift和Ctrl键的状态。MK_前缀表明“鼠标键”。
MK_LBUTTON 按下左键
MK_MBUTTON 按下中键
MK_RBUTTON 按下右键
MK_SHIFT 按下Shift键
MK_CONTROL 按下Ctrl键
若是接收到WM_LBUTTONDOWN消息,并且值wParam & MK_SHIFT是TRUE,就知道当左键按下时,也按下了右键。
当把鼠标移过窗口的客户区时,Windows并不为鼠标的每一个可能的像素位置都产生一条WM_MOUSEMOVE消息。程序接收到WM_MOUSEMOVE消息的次数取决于鼠标硬件以及窗口过程处理鼠标移动消息的速度。
62 处理Shift键
要判断鼠标移动时,是否按下了Shift键,能够经过wParam进行判断,具体方法以下:
if (wParam & MK_SHIFT) { if (wParam & MK_CONTROL) { //按下了Shift和Ctrl键 } else { //按下了Shift键 } }
63 双击鼠标键
要肯定为双击,这两次单击必须发生在其相互的物理位置十分接近的情况下,默认时范围是一个平均系统字体字符的宽,半个字符的高,而且发生在指定的时间间隔内。
若是但愿窗口过程可以接收到双击键的鼠标消息,那么在调用RegisterClass初始化窗口类结构时,必须在窗口风格中包含CS_DBCLKS标识符。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBCLKS;
若是在窗口风格中未包含CS_DBCLKS,那么用户在短期内双击了鼠标键,窗口过程将接收到以下消息:
WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDOWN WM_LBUTTONUP //若是窗口类风格中包含了CS_DBCLKS,那么双击鼠标键时窗口过程将接收到以下消息: WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBCLK WM_LBUTTONUP
64 非客户区鼠标消息
在窗口的客户区内移动或按下鼠标键时,将产生10个消息。若是鼠标在窗口的客户区以外,但在窗口内,Windows将给窗口过程发生一个“非客户区”鼠标消息。窗口非客户区包括标题栏、菜单、和窗口滚动条。
一般不须要处理非客户区鼠标消息,而将这些消息传给DefWindowProc,从而使Windows执行系统功能。
消息中包含字母“NC”以表示是非客户区消息。
若是鼠标在窗口的非客户区移动,那么窗口过程接收到WM_NCMOUSEMOVE消息。其余动做产生以下消息:
键
按下
释放
双击
左
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCLBUTTONDBCLK
中
WM_NCMBUTTONDOWN
WM_NCMBUTTONUP
WM_NCMBUTTONDBCLK
右
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
WM_NCRBUTTONDBCLK
非客户区鼠标消息的wParam和lParam参数意义以下:
wParam:指明移动或者单击鼠标键的非客户区位置。以HT开头。
lParam:包含低位字的x坐标和高位字的y坐标,可是它们都是屏幕坐标,而不是客户区坐标。
//使用如下两个函数将屏幕坐标和客户区坐标互换。 ScreenToClient(hwnd, &pt); ClientToScreen(hwnd, &pt);
若是屏幕坐标点在窗口客户区的上面或者左边,客户区坐标x或y值就是负值。
65 命中测试
WM_NCHITTEST表明“非客户区命中测试”。此消息优先于全部其余的客户区和非客户区鼠标消息。lParam参数含有鼠标位置的x和y屏幕坐标。wParam参数没有用。
Windows应用程序一般把这个消息传送给DefWindowProc,而后Windows用WM_NCHITTEST消息产生基于鼠标位置的全部其余鼠标消息。对于非客户区鼠标消息,在处理WM_NCHITTEST消息时,从DefWindowProc返回的值将成为鼠标消息中的wParam参数,这个值能够是任意非客户区鼠标消息的wParam值再加上如下内容:
HTCLIENT 客户区
HTNOWHERE 不在窗口中
HTTRANSPARENT 窗口由另外一个窗口覆盖
HTERROR 使DefWindowProc产生蜂鸣声
若是DefWindowProc在其处理WM_NCHITTEST消息后返回HTCLIENT,那么Windows将把屏幕坐标转换为客户区坐标并产生客户区鼠标消息。
66 从消息产生消息
若是在一个Windows程序的系统菜单图标上双击一下,那么程序将会终止。双击产生一些了的WM_NCHITTEST消息。因为鼠标定位在系统菜单图标上,因此DefWindowProc将返回HTSYSMENU的值,而且Windows把wParam等于HTSYSMENU的WM_NCLBUTTONDBCLK消息放在消息队列中。
当DefWindowProc接收到wParam参数为HTSYSMENU的WM_NCLBUTTONDBCLK消息时,就把wParam参数为SC_CLOSE的WM_SYSCOMMAND消息放入消息队列中。一样,窗口过程也把这个消息传给DefWindowProc。DefWindowProc经过给窗口过程发生WM_CLOSE消息来处理该消息。
若是一个程序在终止前须要来自用户的确认,那么窗口过程就必须捕获WM_CLOSE,不然,DefWindowProc将调用DestroyWindow函数来处理WM_CLOSE。
67 捕获鼠标
捕获鼠标,只要调用SetCapture(hwnd);在这个函数调用以后,Windows将全部鼠标消息发给窗口句柄为hwnd的窗口过程。鼠标消息老是客户区消息,即便鼠标正在窗口的非客户区。lParam参数将指示鼠标在客户区坐标中的位置。不过,当鼠标位于客户区的左边或者上方的时候,这些x和y坐标能够是负的。
当须要释放鼠标时,调用ReleaseCapture();就可恢复正常。
若是鼠标被捕获,而鼠标键并无被按下,而且鼠标光标移到了另外一个窗口上,那么将不是由捕获鼠标的那个窗口而是由光标下面的窗口来接收鼠标消息。
68 鼠标轮
鼠标轮的转动产生一个WM_MOUSEWHEEL消息。
lParam参数将得到鼠标的位置,坐标是相对于屏幕左上角的,不是客户区的。
wParam参数低字包含一系列的标识,用于代表鼠标键和Shift与Ctrl键的状态。
wParam的高字中有一个“delta”值,该值默承认以是120或-120,这取决于滚轮是向前转动仍是向后转动。值120或-120代表文档将分别向上或向下滚动三行。
---------------------------------------------------------------------------------------------------------------
六 计时器
69 计时器基础
计时器是一种输入设备,它周期性地每通过一个指定的时间间隔就用WM_TIMER消息通知应用程序一次。
能够经过调用SetTimer函数为Windows应用程序分配一个计时器。SetTimer有一个时间间隔范围为1~4294967295毫秒的整型参数,这个值指示Windows每隔多长时间给程序发送WM_TIMER消息。
当程序用完计时器时,就调用KillTimer函数中止计时器消息。
KillTimer调用清除消息队列中还没有被处理的WM_TIMER消息,从而使程序在调用KillTimer以后就不会再受到WM_TIMER消息。
70 系统和计时器
Windows计时器是PC的硬件和ROM BIOS构造的计时器逻辑的一种相对简单的扩展。
BIOS的“计时器滴答”中断约每54.915毫秒或者大约每秒18.2次。
在Microsoft Windows NT中,计时器的分辨率为10毫秒。
Windows应用程序不能以高于这个分辨率的速率接收WM_TIMER消息。在SetTimer调用中指定的时间间隔老是截尾为时钟滴答的整数倍。例如,1000毫秒的间隔除以54.925毫秒≈18.207个时钟滴答,截尾后为18个时钟滴答,它其实是989毫秒。对每一个小于55毫秒的间隔,每一个时钟滴答都产生一个WM_TIMER消息。
71 计时器消息不是异步的
计时器是基于硬件计时器中断。但WM_TIMER消息却不是异步的。
WM_TIMER消息放在正常的消息队列之中,和其余消息一块儿参加排序,所以,若是在SetTimer调用中指定间隔为1000毫秒,那么不能保持程序每1000毫秒就会收到一个WM_TIMER消息。若是其余程序的运行事件超过一秒,在此期间,程序将不会收到任何WM_TIMER消息。
Windows不能持续向消息队列放入多个WM_TIMER消息,而是将多余的WM_TIMER消息合并成一个消息。
72 计时器的使用(方法一)
若是须要在整个程序期间使用计时器,那么能够在处理WM_CREATE消息时调用SetTimer,并在处理WM_DESTROY消息时调用KillTimer。
SetTimer函数以下所示:
SetTimer(hwnd, 1, uiMsecInterval, NULL); /* 第一个参数是其窗口过程将接收WM_TIMER消息的窗口的句柄; 第二个参数是计时器ID,只要是非0的整数就能够,若是设置多个计时器,那么各个计时器的ID应该不一样。 第三个参数是一个32位无符号整数,以毫秒为单位指定一个时间间隔。 第四个参数是回调函数的地址,若是处理WM_TIMER消息不是回调函数,那么设置为NULL。 */ //KillTimer函数调用以下: KillTimer(hwnd, 1); // 第一个参数是其窗口过程将接收WM_TIMER消息的窗口的句柄; //第二个参数是计时器ID。 //KillTimer用于在任什么时候刻中止WM_TIMER消息。
当窗口过程收到一个WM_TIMER消息时,wParam参数等于计时器的ID值,lParam参数为0.若是须要设置多个计时器,那么对每一个计时器都使用不一样的计时器ID。wParam的值将随传递到窗口过程的WM_TIMER消息的不一样而不一样。
73 计时器的使用(方法二)
第一种方法是把WM_TIMER消息发送到一般的窗口过程。
第二种方法是让Windows直接将计时器消息发送给程序的另外一个函数。
接收这些计时器消息的函数称为“回调函数”,这是一个在程序之中,可是由Windows而不是程序自己调用的函数。先告诉Windows这个函数的地址,而后Windows调用此函数。
窗口过程其实就是一种回调函数。回调函数必须定义为CALLBACK,由于它是由Windows从程序的代码段调用的。回调函数的参数和回调函数的返回值依赖于回调函数的目的。同计时器相关的回调函数中,输入参数同学口过程的输入参数同样。计时器回调函数不向Windows返回值,能够设置为VOID。
假设计时器回调函数称为TimerProc,那么能够定义以下:
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
WM_TIMER消息的处理过程;
}
TimerProc的参数hwnd是在调用SetTimer时指定的窗口句柄。Windows只把WM_TIMER消息消息送过TimerProc,所以消息参数老是等于WM_TIMER。iTimerID值是计时器ID,dwTimer值是与从GetTickCount函数的返回值兼容的值,是反映Windows启动后所通过的毫秒数。
在使用回调函数处理WM_TIMER消息时,窗口过程当中设置SetTimer的第4个参数由回调函数的地址取代,以下所示:
SetTimer(hwnd, iTimerID, iMsecInterval, TimerProc);
回调函数必须和窗口过程函数同样,一块儿被声明在程序的开始处,像TimerProc的函数声明以下:
VOID CALLBACK TimerProc(HWND, UINT, UINT, DWORD);
74 计时器的使用(方法三)(少用)
设置计时器的第三种方法相似于第二种方法,只是传递给SetTimer的hwnd参数被设置为NULL,而且第二个参数计时器ID也被忽略,设置为0,最后才函数返回计时器ID。
iTimerID = SetTimer(NULL, 0, iMsecInterval, TimerProc);
若是没有可用的计时器,那么从SetTimer返回的iTimerID值将为NULL。
KillTimer的第一个参数也必须是NULL,计时器ID必须是SetTimer的返回值。
传递给TimerProc计时器函数的hwnd参数也必须是NULL。
75 获取当前时间
首先介绍下SYSTEMTIME结构,以下所示:
typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME;
SYSTEMTIME结构包含日期和时间。月份由1开始递增,星期由0开始递增(星期天是0)。wDay是本月的当前日子,从1开始递增。
SYSTEM主要用于GetLocalTime和GetSystemTime函数。
GetSystemTime函数返回当前的世界时间(格林尼治时间)
GetSystemTime函数返回当地时间。
76 WM_SETTINGCHANGE消息
若是用户改变了任何系统设置,Windows会产生WM_SETTINGCHANGE消息,并传送给全部的应用程序。
------------------------------------------------------------------------------------------------------------------------
七 子窗口控制
77 子窗口控制概述
当子窗口的状态发生改变时,子窗口处理鼠标和键盘消息并通知父窗口。子窗口这时就变成了其父窗口高一级的输入设备。
能够创建本身的子窗口控制,也能够利用一些预约义的窗口类和窗口过程来创建标准的子窗口控制。
子窗口控制采用的形式有:按钮、复选框、编辑框、列表框、组合框、文本串、滚动条。
子窗口控制在对话框中最经常使用。
子窗口控制的位置和尺寸是在程序的资源描述文中的对话框模板中定义的。也可使用预约义的、位于普通窗口客户区表面的子窗口控制。能够调用一次CreateWindow来创建一个子窗口,并经过调用MoveWindow来调整子窗口的位置和尺寸。父窗口过程向子窗口控制发送消息,子窗口控制向父窗口过程返回消息。
78 按钮类
按钮属于窗口。
按钮窗口风格都以字母BS开头,它表明“按钮风格”。按钮使用CreateWindow建立。
HWND hwndButton; hwndButton = CreateWindow{ /* 参数1 ClassName 类名, 参数2 Window text 按钮显示的文本, 参数3 Window Style 窗口风格 有WS_CHILD | WS_VISIBLE | 按钮种类 参数4 x位置 参数5 y位置 说明按钮左上角相对于父窗口客户区左上角的位置 参数6 Width 宽度 按钮宽度 参数7 Height 高度 按钮的高度 参数8 父窗口句柄 参数9 子窗口ID(强制HMENU类型) 参数10 实例句柄((LPCREATESTRUCT)lParam -> hInstance) 参数11 额外参数(通常为NULL) */ }
注:参数3中的按钮种类分为:
下压按钮:BS_PUSHBUTTON、BS_DEFPUSHBUTTON 下压按钮
复选框:BS_CHECKBOX、BS_AUTOCHECKBOX
三状态复选框:BS_3STATE、BS_AUTO3STATE
单选按钮:BS_RADIOBUTTON、BS_AUTORADIOBUTTON
组合框:BS_GROUPBOX
自定义按钮:BS_OWNEDRAW
参数9的子窗口ID对于每一个子窗口都不一样。在处理来自子窗口的WM_COMMAND消息时,ID帮助窗口过程识别出相应的子窗口。子窗口ID必须被强制转换为HMENU。
参数10的实例句柄利用了以下事实:在处理WM_CREATE消息的过程当中,lParam其实是指向CREATESTRUCT结构的指针,该结构有一个hInstance成员。
也可使用GetWindowLong(hwnd, GWL_HINSTANCE)函数调用来获取实例句柄。
79 子窗口向父窗口发送消息
当用鼠标点击按钮时,子窗口控制就向其父窗口发送一个WM_COMMAND消息。父窗口过程捕获WM_COMMAND消息,其wParam和lParam消息参数含义以下:
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码
lParam 子窗口句柄
子窗口ID是在建立子窗口时传递给CreateWindow的值。
通知码详细代表了消息的含义。
当用鼠标单击按钮时,该按钮文本的周围会有虚线。这代表该按钮拥有了输入焦点,全部键盘输入都将传送给子窗口按钮控制。而后按钮即便拥有输入焦点,也只能处理空格键。
80 父窗口向子窗口发送消息
父窗口能够给子窗口发送消息,这些消息包括之前缀为WM开头的许多消息。另外,还有8个按钮说明消息,以BM开头。
BM_GETCHECK:获取复选框和单选按钮的选中标记
BM_SETCHECK:设置复选框和单选按钮的选中标记
BM_GETSTATE:获取按钮状态(正常仍是按下)
BM_SETSTATE:设置按钮状态
BM_GETIMAGE
BM_SETIMAGE
BM_CLICK
BM_SETSTYLE:容许在按钮建立后改变按钮风格
每一个子窗口控制都具备一个在其兄弟中惟一的窗口句柄和ID值,对于句柄和ID值这二者,知道其中一个就能够得到另外一个。
id = GetWindowLong(hwndChild, GWL_ID);
hwndChild = GetDlgItem(hwndParent, id);
81 下压按钮
下压按钮控制主要用来触发一个当即响应的动做,而不保**何开关指示。有两种类型的按钮控制窗口风格。分别是BS_PUSHBUTTON和BS_DEFPUSHBUTTON。
当用来设计对话框时,两种风格做用不一样,但当用做子窗口控制时,两种类型的按钮做用相同。
当按钮的高度为文本字符高度的7/4倍时,按钮的外观最好。
按钮的文本尺寸除了以前介绍的从TEXTMETRIC结构中获取以外,更简便的方法是经过GetDialogBaseUnits函数来得到默认字体字符的高度和宽度。
此函数返回一个32位的值,其中低字位表示宽度,高字位表示高度。
当鼠标在按钮中按下时,按钮使用三维阴影重画本身。
当鼠标放开时,就恢复按钮缘由,并向父窗口发送一个WM_COMMAND消息和BN_CLICKED通知码。
82 复选框
复选框是一个文本框,文本一般出如今复选框的右边。复选框一般用于容许用户对选项进行选择的应用程序中。复选框的经常使用功能如同一个开关:单击一次显示复选标记,再次单击清除复选标记。
复选框最经常使用的2种风格是BS_CHECKBOX和BS_AUTOCHECKBOX。在使用BS_CHECKBOX时,须要本身向该控制发送BM_SETCHECK消息来设置复选标记。wParam参数置1时设置复选标记,置0时清除复选标记。经过向该控制发送BM_GETCHECK消息,能够获得该复选框的当前状态。
能够用以下指令来翻转复选标记:
SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM) !SendMessage((HWND)lParma, BM_GETCHECK, 0, 0), 0);
对于BS_AUTOCHECKBOX风格,按钮本身触发复选标记的开和关,窗口过程能够忽略WM_COMMAND消息。当须要知道按钮的当前状态时,能够向控制发送BM_GETCHECK消息:
iCheck = (int)SendMessage(hwndButton, BM_GETCHECK, 0, 0);
若是按钮被选中,则iCheck返回非0值。
其他两种复选框风格是BS_3STATE和BS_AUTO3STATE,这两种风格能显示第三种状态——复选框内是灰色的——它出如今向控制发送wParam = 2的WM_SETCHECK消息时。
83 单选按钮
单选按钮的形状是个圆圈。圆圈内的加剧圆点表示该单选按钮已经被选中。单选按钮有窗口风格BS_RADIOBUTTON或BS_AUTORADIOBUTTON两种,后者只用于对话框。
当收到来自单选按钮的WM_COMMAND消息时,应该向它发送wParam等于1的BM_SETCHECK消息来显示其选中状态:
SendMessage(hwndButton, BM_SETCHECK, 1, 0);
对相同组中的其余全部单选按钮,能够经过向它们发送wParam等于0的BM_SETCHECK消息来显示其未选中状态。
SendMessage(hwndButton, BM_SETCHECK, 0, 0);
84 分组框
分组框即风格为BS_GROUPBOX的选择框,它不处理鼠标输入和键盘输入,也不向其父窗口发送WM_COMMAND消息。分组框是一个矩形框,窗口文本在其顶部显示。分组框经常使用来包含其余的按钮控制。
85 更改按钮文本
能够经过调用SetWindowText来更改按钮内的文本:
SetWindowText(hwnd, pszString);
hwnd是窗口句柄,pszString是一个指向NULL终结串的指针。
对于通常的窗口,更改的是窗口的标题栏文本,对于按钮控制来讲,更改的是按钮的显示文本。
能够获取窗口的当前文本:
iLength = GetWindowText(hwnd, pszBuffer, iMaxLength);
iMaxLength指定复制到pszBuffer指向的缓冲区中的最大字符数,该函数返回复制的字符数。
能够经过调用
iLength = GetWindowTextLength(hwnd);获取文本的长度。
86 可见和启用的按钮
为了接受鼠标和键盘输入,子窗口必须是可见的和被启用的。当窗口是可见的可是非启用时,窗口以灰色显示正文。
若是在创建子窗口时,没有将WS_VISIBLE包含在窗口类中,那么直到调用
ShowWindow(hwndChild, SW_SHOWNORMAL);
时子窗口才被显示出来。
调用ShowWindow(hwndChild, SW_HIDE);
将子窗口隐藏起来。
使用EnableWindow(hwndChild, TRUE)来启用窗口。
87 按钮和输入焦点
当Windows将输入焦点从一个窗口转换到另外一个窗口时,首先给正在失去输入焦点的窗口发送一个WM_KILLFOCUS消息,wParam参数是接收输入焦点的窗口的句柄。而后,Windows向正在接收输入焦点的窗口发送一个WM_SETFOCUS消息,同时wParam参数是正在失去输入焦点的窗口的句柄。
能够经过调用SetFocus来恢复输入焦点,如:
case WM_KILLFOCUS: if (hwnd == GetParent((HWND)wParam)) SetFoucs(hwnd); return 0;
88 静态类
在CreateWindow函数中指定窗口类为“static”,就能够创建静态的子窗口控制。这些子窗口既不接收鼠标或键盘输入,也不向父窗口发送WM_COMMAND消息。
当在静态子窗口上移动或按下鼠标时,这个子窗口将捕获WM_NCHITTEST消息,并将HTTRANSPARENT的值返回给Windows,这将使Windows向其下层窗口发送相同的WM_NCHITTEST消息。
89 滚动条类
滚动条类是能够在父窗口的客户区的任何地方出现的子窗口。可使用预先定义的窗口类“scrollbar”以及两个滚动条风格SBS_VERT和SBS_HORZ中的一个来创建子窗口滚动条控制。
与按钮控制不一样,滚动条控制不向父窗口发送WM_COMMAND消息,而是像窗口滚动条同样发送WM_VSCROLL和WM_HSCROLL消息。在处理滚动条消息时,能够经过lParam参数来区分开窗口滚动条与滚动条控制。对于窗口滚动条,其值是0.对于滚动条控制,其值是滚动条窗口句柄。对于窗口滚动条和滚动条控制来讲,wParam参数的高位字和低位字的含义相同。
窗口滚动条有固定的宽度,但滚动条控制能够本身修改尺寸。使用CreateWindow调用时,给出矩形尺寸来肯定滚动条控制的尺寸。
若是想创建与窗口滚动条相同的滚动条控制,那么可使用GetSystemMetrics获取水平滚动条的高度GetSystemMetrics(SM_CYHSCROLL)或者垂直滚动条的宽度GetSystemMetrics(SM_CXVSCROLL);
滚动条窗口风格标识符SBS_LEFTALIGN、SBS_RIGHTALIGN、SBS_TOPALIGN和SBS_BUTTOMALIGN给出滚动条的标准尺寸,可是这些风格只在对话框中对滚动条有效。
对于滚动条控制,可使用以下调用来设置范围和位置:
SetScrollRange(hwndScroll, SB_CTL, iMin, iMax, bRedraw)
SetScrollPos(hwndScroll, SB_CTL,iPos, bRedraw)
SetScrollInfo(hwndScroll, SB_CTL, &si, bRedraw)
滚动条两端按钮之间较大的区域颜色是有COLOR_BTNFACE和COLOR_BTNHIGHLIGHT一块儿来肯定的。
若是捕获了WM_CTLCOLORSCROLLBAR消息,那么能够在消息处理中返回画刷以取代该颜色。
90 窗口子类化
通常咱们的消息都是传给Windows程序的WndProc窗口过程的,可是当窗口内有子窗口控制时,咱们能够给这个子窗口设置一个新的窗口过程,这个技术叫作“窗口子类化”。它能让咱们给现存的窗口过程(新的)设置“钩子”,以便在程序中处理一些消息,同时将其余全部消息传递给旧的窗口过程(WndProc)。
Win32的子类化的原理是靠拦截Windows系统中的某些消息来本身进行处理,而不是交给WndProc或DefWindowProc。
将GWL_WNDPROC标识符做为参数来调用GetWindowLong,能够获得这个窗口过程的地址。
能够调用SetWindowLong给子窗口设置一个新的窗口过程。
能够用函数指针的办法,将咱们感兴趣的消息拦截下来,处理完以后再让预约义的窗口过程处理。这个过程大体以下:
WNDPROC OldProc;(用来保存旧的WndProc窗口过程)
OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (LONG)NewProc);
固然,这里的新窗口过程NewProc是预先由你实现好的。上述代码执行之后,系统在处理hwnd的窗口消息时,就会先进入你实现的NewProc回调过程,而后在处理过你感兴趣的消息以后,经过CallWindowProc函数和你预先保存的OldProc再次回到原来的回调过程当中完成剩余的工做。
91 编辑类
当创建子窗口时,CreateWindow第一个参数,即类名使用“edit”,根据CreateWindow调用中的x位置、y位置、宽度、高度等这些参数定义了一个矩形。此矩形含有可编辑文本。当子窗口控制拥有输入焦点时,能够输入文本,移动光标,使用鼠标或者Shift键与一个光标键来选取部分文本,也能够删除、剪切、复制、粘帖文本。
92 编辑类风格
① 是WS_CHILD风格。
② 是编辑控制中的文本对齐方式,能够左对齐ES_LEFT、右对齐ES_RIGHT、居中ES_CENTER。
③ 编辑控制是单行文本仍是多行文本,默认是单行,若是要处理回车键,须要增长风格ES_MULTILINE。
④ 滚动条功能,纵向是ES_AUTOVSCROLL,横向是ES_AUTOHSCROLL。
⑤ 边框,默认是没边框的,可使用风格WS_BORDER。
93 编辑控制通知
编辑控制给父窗口过程发生WM_COMMAND消息,wParam和lParam参数和按钮控制同样。
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码(EN开头)
lParam 子窗口句柄
通知码以下所示:
EN_SETFOCUS 得到输入焦点
EN_KILLFOCUS 失去输入焦点
EN_CHANGE内容将改变
EN_UPDATE 内容已经改变
EN_ERRSPACE 输入的文本超过30000个字符
EN_MAXTEXT 插入以后的文本超过30000个字符
EN_HSCROLL 编辑控制的水平滚动条被单击
EN_VSCROLL 编辑控制的垂直滚动条被单击
94 发送给编辑控制的消息
发送给编辑控制的消息运行剪切、复制、清除当前的选择。用户使用鼠标或Shift键减小光标控制键选择文本并进行上面的操做。
剪切:SendMessage(hwndEdit, WM_CUT, 0, 0); 复制:SendMessage(hwndEdit, WM_COPY, 0, 0); 清除;SendMessage(hwndEdit, WM_CLEAR, 0, 0); 粘帖:SendMessage(hwndEdit, WM_PASTE, 0, 0);
//获取当前选中文本的起始位置和末尾位置: SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd); //末尾位置实际是最后一个选择字符的位置加1。 //选择文本: SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd); //文本置换: SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szString); //获取多行文本的行数: iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0); //对任何特定的行,能够获取距离编辑缓冲区文本开头的偏移量: iOffset = SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0); //其中,行数从0开始计算,iLine值为-1时返回包含光标所在行的偏移量。 //获取行的长度: iLength = SendMessage (hwndEdit, EM_LINELENGTH, iLine, 0); //将一行复制到一个缓冲区: iLength = SendMessage (hwndEdit, EM_GETLINE, iLine, (LPARAM) szBuffer) ;
95 列表框类
列表框也属于子窗口控制。列表框是文本串的集合,这些文本串是一个矩形中能够滚动显示的列状列表。程序经过向列表框窗口过程发送消息,能够在列表中增长或者删除串。当列表框中的某项被选定时,列表框控制就向其父窗口发送WM_COMMAND消息,父窗口也就能够肯定选定是哪一项。
列表框能够是单选的,也能够是多选的。选定的项被加亮显示,而且是反显的。
在单项选择的列表框中,用户按空格键就能够选定光标所在位置的项。方向键移动光标和当前选择指示,而且可以滚动列表框的内容。
96 列表框风格
当使用CreateWindow创建列表框子窗口时,应该将“listbox”做为窗口类,将WS_CHILD做为窗口风格。可是,这个默认列表框风格不向其父窗口发送WM_COMMAND消息。因此,通常都要包括列表框风格标识符LBS_NOTIFY。它容许父窗口接收来自列表框的WM_COMMAND消息。若是但愿列表框对其中各项进行排序,那么可使用另外一个风格LBS_SORT。
若是想创建一个多选选择的列表框,那么可使用风格LBS_MULTIPLESEL。
默认的列表框是无边界的,因此通常都要加上WS_BORDER来加上边界。
使用WS_VSCROLL来增长垂直滚动条。
有一个列表框风格,综合了上述各类风格,那就是LBS_STANDARD风格。
97 将文本串放入列表框
将文本串放入列表框能够经过调用SendMessage给列表框窗口过程发消息来实现这一点。文本串一般经过以0开始计数的下标数来引用,其中0对应于最顶上的项。
通常子窗口列表框控制的句柄定义为hwndList
下标值定义为iIndex
在使用SendMessage传递文本串的状况下,lParam参数是指向null结尾串的指针。
当窗口过程存储的列表框内容超过了可用内存空间时,SendMessage将返回LB_ERRSPACE(定义为-2)。若是是其余缘由出错,那么将返回LB_ERR(-1).
若是采用LBS_SORT风格,那么填充列表框最简单的方法是借助LB_ADDSTRING消息:SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);
//若是没有采用LBS_SORT,那么可使用LB_INSERTSTRING指定一个下标值,将字符串插入到列表框中: SendMessage (hwndList, LB_INSERTSTRING, iIndex, (LPARAM) szString) ; //若是iIndex等于4,那么szString将变为下标值为4的串——从顶头开始算起的第5个串。下标值为-1时,将串增长在最后。 //能够在指定下标值的同时使用LB_DELETESTRING参数,这就能够从列表框中删除串: SendMessage (hwndList, LB_DELETESTRING, iIndex, 0); //可使用LB_RESETCONTENT清除列表框中的全部内容: SendMessage (hwndList, LB_RESETCONTENT, 0, 0);
98 选择和获取项
//获取列表框项数:(LB_GETCOUNT) iCount = SendMessage (hwndList, LB_GETCOUNT, 0, 0); //加亮选中项:(LB_SETCURSEL) SendMessage (hwndList, LB_SETCURSEL, iIndex, 0); //将lParam设置为-1,取消全部选择。 //根据项的第一个字母来选择:(LB_SELSECTSTRING) iIndex = SendMessage (hwndList, LB_SELECTSTRING, iIndex, (LPARAM) szSearchString); //iIndex等于-1时,从头开始搜索。 //当获得来自列表框的WM_COMMAND消息时,能够经过使用LB_GETCURSEL来肯定当前选项的下标: (LB_GETCURSEL) iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0); //能够肯定列表框中串的长度:(LB_GETTEXTLEN) iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0); //能够将某项复制到文本缓冲区:(LB_GETTEXT) iLength = SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) szBuffer);
99 接收来自列表框的消息
当用户用鼠标单击列表框时,列表框将接收输入焦点。
列表框控制向其父窗口发送WM_COMMAND消息,对按钮和编辑控制来讲wParam和lParam参数的含义是相同的。
LOWORD(wParam) 子窗口ID HIWORD(wParam) 通知码(LBN开头) lParam 子窗口句柄 //通知码及其值以下所示: LBN_ERRSPACE -2 表示列表框已经超出运行空间 LBN_SELCHANGE 1 代表当前选择已经被改变 LBN_DBLCLK 2 代表某项已经被鼠标双击 LBN_SELCANCEL 3 LBN_SETFOCUS 4 列表框得到焦点 LBN_KILLFOCUS 5 列表框失去焦点 //只有列表框窗口风格包括LBS_NOTIFY时,列表框控制才向父窗口发送LBN_SELCHANGE和LBN_DBLCLK码
100 文件列表
LB_DIR是功能最强的列表框消息,它用文件目录表填充列表框,而且能够选择将子目录和有效的磁盘驱动器也包括进来:
SendMessage(hwndList, LB_DIR, iAttr, (LPARAM)szFileSpec);
① 使用文件属性码:
当LB_DIR消息的iAttr值为DDL_READWRITE时,列表框列出普通文件、只读文件和具备归档位集的文件。
当值为DDL_DIRECTORY时,列表框除列出上述文件以外,还列出子目录,目录位于方括号以内。
当值为DDL_DRIVES | DDL_DIRECTORY时,那么列表将扩展到包括全部有效的驱动器,驱动器字母显示在虚线之间。
当值为DDL_EXCLUSIVE | DDL_ARCHIVE时,即将iAttr的最高位置位能够列出带标志的文件,而不包括普通文件。
② 文件列表的排序
lParam参数是指向文件说明串如“*.*”的指针,这个文件说明串不影响列表框中的子目录。
用户也许但愿给列有文件清单的列表框使用LBS_SORT消息,列表框首先列出符合文件说明的文件,再列出子目录名。列出的第一个子目录名将采用下面的格式:[..]这种两个点的子目录项容许用户向根目录返回一级。最后,具体的子目录名采用下面的形式:[SUBDIR]后面是如下面的形式列出的有效磁盘驱动器[-A-]