第一章:起步
1.1 Windows环境
P6:所有GUI都用位图视频显示器显示图形
P7:一个操作系统如果没有内存管理就不能实现多任务
P7:windows工作原理的中心思想就是动态链接的概念
P8:3个主模块。1:内核:负责操作系统的那些传统工作,包括内存管理、文件输入/输出及任务管理等。2:用户:指的是用户界面,负责所有的窗口管理。3:GDI:就是图形设备接口,负责在屏幕或者打印机上显示文本和图形。
P8:C语言库函数的机器代码会直接连接到程序代码中,而windows的函数这是放在程序之外的DLL中。当一个windows程序被装入内存后,程序中的函数调用都被解析成DLL函数入口的指针,同时这些被调用的函数也被装入内存(如果不在内存的话)
P8:当链接windows程序以生成执行文件时,一定得链接你的编译环境所提供的特殊的导入库。这些导入库包含所有windows函数调用要碰到的动态链接库的名字及引用信息。
1.2 Windows编程选项
1.3 你的第一个Windows程序
P14:几个最重要也是最基本的头文件
Windows.h
windef.h:基本数据结构定义
winnt.h:支持Unicode的类型定义
WinBase.h:内核函数
WinUser.h:用户界面函数
wingdi.h:图形设备接口函数
P15:WinMain()的第二个参数用于16位系统,在现在通常定义为NULL
第二章:Unicode简介
P19:Unicode是ASCII字符编码的一个拓展,用的是16位字符编码,ASCII用的是7位表示一个字符。
2.1 字符集简史
P20:ASCII码有26个小写字母,26个大写字母,10个数字,32个符号,33个控制码,1个空格码,公众有128个代码
P23:使用代码页会有代码页混淆的问题
P23:双字节字符集(DBCS)
P24:Unicode被认为是宽字符,最开始的128个Unicode字符是ASCII码,而之后的128个字符是ISO 8859-1 ASCII扩展码。Unicode只有一个字符集,避免了二义性。
2.2 宽字符和C语言
P25:宽字符不一定是Unicode,Unicode只是宽字符编码的一种实现。
P26:
wchar_t是16位宽。L表示这个字符串将用宽字符存储。
typedef unsigned short wchar_t;
wchar_t *p=L"Hello!";
wchar_t是16位宽。L表示这个字符串将用宽字符存储。
P27:wcslen是strlen的宽字符版本。
P28:TCHAR.H头文件,其中定义的每个函数和宏都有一个下划线前缀,当_Unicode的标示符被定义了,这是用Unicode方式,否则使用非Unicode版本。
2.3 宽字符和Windows
P29:windows NT从底层支持Unicode,内部使用16位字符的字符串。
P30:Windows.h要放在第一个。
P31:用字符串做参数的每个Win32函数中,都在操作系统中有两个入口点。
P33:不能再windows程序中使用printf
第三章:窗口与消息
3.1 窗口的创建
3.1.1 系统结构概述
P38:操作系统调用应用程序正是Windows体系结构的基础。
P38:应用程序说创建的每个窗口都有一个与之相关联的窗口过程,这个窗口过程可以是应用程序的一个函数,也可以位于一个动态链接库中。Windows正是通过调用该窗口过程来向窗口传递消息。
P38:窗口总还是依据窗口类来创建。窗口类表示了用于处理传递给窗口的消息的窗口过程。窗口类的使用允许多个窗口共享同一窗口类,因为多个窗口可以使用相同的窗口过程。
P38:当Windows程序开始执行时,windows首先为改程序创建一个消息队列。windows应用程序中一般都包含一小段成为消息循环的代码,该代码用于从消息队列中检索信息,并将其分发给相应的窗口过程。
3.1.2 HELLOWIN程序
3.1.3 通盘考虑
前缀 | 常量 |
CS | 类风格选项 |
CW | 创建窗口选项 |
DT | 文本绘制选项 |
IDI | 图标的ID号 |
IDC | 光标的ID号 |
MB | 消息框选项 |
SND | 声音选项 |
WM | 窗口消息 |
WS | 窗口风格 |
P44:匈牙利标记法:以表明该变量数据结构的小写字母开始。
前缀 | 数据类型 |
c | char,WCHAR,TCHAR |
by | BYTE(无符号字符) |
n | short |
i | int |
x,y | int,表示x坐标,y坐标 |
cx,cy | int,表示XY长度 |
B或者f | BOOL,flag |
w | WORD(无符号短整型) |
l | LONG |
dw | DWORD |
fn | 函数 |
s | 字符串 |
sz | 已零结尾的字符串 |
h | 句柄 |
p | 指针 |
3.1.4 窗口类的注册
P49:由于windows98很少支持Unicode,所以运行Unicode程序很容易出错
P50:GetLastError函数用于获取当函数调用失败时的扩展错误信息
3.1.5 窗口的创建
3.1.6 窗口的显示
3.1.7 消息循环
3.1.8 窗口过程
P54:窗口过程定义
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
P55:窗口过程几乎总是由Windows自身调用的
3.1.9 消息的处理
3.1.10 声音文件的播放
3.1.11 WM_PAINT消息
P57:设备环境是指物理输出设备及其设备驱动程序
P57:DefWindowProc所做的只是简单地依次调用BeginPaint和EndPaint,以便使客户区变为有效。
3.1.12 WM_DESTORY消息
3.2 Windows编程中的若干难点
3.2.1 究竟是谁调用谁
P60:WndProc和DelWindowProc之间的消息传递形式举例。当用鼠标点击关闭按钮时,DelWindowProc会对该键盘或鼠标输入进行处理,当该函数检测到你已选择关闭选项时,便会向窗口过程发送一条WM_SYSCOMMAND消息,WndProc会将该消息传递给DelWindowProc。作为响应,DelWindowProc又会给窗口过程发送一条WM_CLOSE消息。WndProc再次将该消息传给DelWindowProc,此时DelWindowProc会通过调用DestroyWindow来对WM_CLOSE做出响应。DestoryWindow则会导致windows向窗口过程发送一条WM_DESTORY消息。WndProc最终又会通过调用PostQuitMessage将一条WM_QUIT消息投递到消息队列中来作为对该消息的响应。
3.2.2 队列消息和非队列消息
P60:队列消息是指那些由windows放入程序的消息队列中的消息。非队列消息则是由Windows对窗口过程的直接调用而产生的。我们一般说队列消息是被投递到消息队列中,非队列消息是被发送到窗口过程。
P60:队列消息主要由用户的输入产生,包含按键消息,由按键产生的字符消息,鼠标移动,鼠标单击,定时器消息,重绘消息,退出消息等。
P61:消息和硬件中断不同,在窗口过程处理某一消息时,程序不会被其他消息突然中断。消息循环与窗口过程不是并发运行的。
P61:窗口过程可以调用为其发送其他消息的函数。窗口过程必须是可重入的。
3.2.3 速战速决
第四章:文本输出
4.1 绘制与重绘
4.1.1 WM_PAINT消息
P64:在以下任何一个时间发生时,窗口过程都会受到一个WM_PAINT消息:
1:用户移动一个窗口,导致原来被遮盖的部分窗口暴露出来
2:用户调整窗口的大小
3:程序调用ScrollWindow或ScrollDC函数滚动客户区
4:程序调用InvalidateRect或InvalidateRgn函数显示生成WM_PAINT
在以下情形,Windows有时会发送一条WM_PAINT消息:
1:Windows关闭一个覆盖了部分窗口的对话框或者消息框
2:下拉菜单被拿下然后收回
3:显示提示信息
在少数情况下,Windows总是会保存被覆盖部分的显示内容,然后再恢复。这些情况如下:
1:鼠标指针在客户区内移动
2:在酷虎区内拖动图标
4.1.2 有效矩形和无效矩形
P65:在客户区中有一个无效区域将导致windows在应用程序的消息队列中放置一条WM_PAINT消息。
P65:每个窗口都保存一个绘制信息结构,这个结构保存着一个可以覆盖无效区域的最小矩形的坐标和一些其他信息。
P65:若消息队列中已有WM_PAINT消息,这不增加消息,计算能够覆盖2个无效区域的无效矩形,否则增加信息。
4.2 GDI简介
4.2.1 设备环境
P66:设备环境与特定的显示设备相关联。程序在绘制前必须获得一个设备环境句柄。
P66:程序必须在处理同一条消息的过程中获取句柄和释放句柄,不能再两条消息中间窗体一个设备环境句柄,唯一的例外是通过调用CreateDC函数创建的设备环境。
4.2.2 获取设备环境句柄:方法一
P66:BeginPaint函数通常会擦去无效区域的背景以便绘图。同时还会填充PAINTSTRUCT结构的各个字段。函数的返回值就是设备环境句柄。调用一次EndPaint函数将释放设备环境句柄
P67:窗口过程在处理WM_PAINT消息时必须成对的调用BeginPaint和EndPaint。若不调用这两个函数,则windows不会将无效区域有效化。
4.2.3 绘制信息结构
4.2.4 获取设备环境句柄:方法二
P69:使用GetDc()函数获取句柄,使用ReleaseDC()函数释放句柄。必须成对出现,且要在处理同一条消息时。从GetDC中获得的设备环境句柄中的裁剪矩形是整个客户区。
P69:GetDC()返回的是窗口客户区的设备环境句柄,GetWindowDC()返回的是整个窗口的设备环境句柄。
4.2.5 TEXTOUT函数详解
TextOut(hdc,x,y,psText,iLengh)
P70:若psText指向Unicode字符串,则占用字节数是iLength的两倍。字符串中不能包含ASCII控制字符。TextOut并不认为字符串尾部值为0,利用iLength参数决定字符串长度。
P70:从GetDC得到的设备环境句柄中,该裁剪区域就是整个客户端。从BeginPaint函数获得的设备环境句柄中,裁剪区域是无效区域。
4.2.6 系统字体
P71:系统字体是点阵字体
4.2.7 字符大小
P72:可以调用GetSystemMetrics函数来获取用户界面的尺寸,通过调用GetTextMetric函数可以获取字体尺寸。需要用到结构TEXTMETRIC,此结构中字段的值的单位取决于设备环境句柄中当前选定的映射模式。默认的映射模式是MM_TEXT,则单位是像素。
4.2.8 文本尺寸的度量
\\TEXTMETRIC结构
tpyedef struct tagTEXTMETRIC{
LONG tmHeight;\\整体高度
LONG tmAscent;\\基线以上高度
LONG tmDescent;\\基线以下高度
LONG tmInternalLeading;\\该间距常用于显示重音符号
LONG tmExternalLeading;\\建议行间距
LONG tmAveCharWidth;\\小写字符的加权平均宽度
LONG tmMaxCharWidth;\\最宽字符宽度
}
P73:大写字符的平均宽度通常为小写字符平均宽度的1.5倍
4.2.9 文本的格式化
P74:wsprintf与TextOut结合使用:
int iLength;
TCHAR szBuffer[40];
iLength=wsprintf(szBuffer,TEXT("The sum of %i and %i is %i"),iA,iB,iA+iB);
TextOut(hdc,x,y,szBuffer,iLength);
\\或者可以合并为如下:
TextOut(hdc,x,y,szBuffer,wsprintf(szBuffer,TEXT("The sum of %i and %i is %i"),iA,iB,iA+iB));
4.2.10 综合使用
4.2.11 SYSMETS1.C窗口过程
4.2.12 空间不够
4.2.13 客户区的尺寸
P81:当消息为WM_SIZE时,lParam变量的地位字是客户区的宽度,高位字是高度。LOWORD(lParam);HIWORD(lParam);
4.3 滚动条
4.3.1 滚动条的范围和位置
P83:滚动条默认范围为0-100。或者使用SetScrollRange()函数来设置
4.3.2 滚动条消息
P85:当滚动条是窗口的一部分时,可以忽略lParam参数,它只用于滚动条是子窗口时,通常实在对话框中。wParam的低字位代表了鼠标在滚动条上的动作,这个值被称为通知码。
P85:当松开鼠标键时,程序会受到一条带有SB_ENDSCROLL通知码的消息,程序通常会忽略这个消息。将鼠标放在花快上然后按下鼠标键时,可以移动滑块,这将会生成带SB_THUMBTRACK和SB_THUMBPOSITION通知码的滚动条消息。当wParam的低位字是SB_THUMBTRACK时,高位字是用户拖动滑块的当前位置。当低位字是SB_THUMBPOSITION时,高位字是用户松开鼠标键时滑块的最终位置
4.3.3 加入滚动条的SYSMETS
4.3.4 程序的绘制代码的结构
P90:如果客户区有任何一部分是无效的,UpdateWindow函数将使窗口过程立刻收到一条WM_PAINT消息。
P90:当一个窗口被创建时,它的整个客户区都是无效的。
4.4 效果更好的滚动
4.4.1 滚动条信息函数
P91:当收到通知码为SB_THUMBTRACK或者SB_THUMBPOSITION的WM_VSCROLL或者WM_HSCROLL消息时,只有16位用来制定滑块信息,而GetScrollInfo函数可以得到实际的32值。
4.4.2 最远可以卷动到哪里
4.4.3 新的SYSMETS
P98:ScrollWindow函数用来滚动窗口客户区的内容,而不是重绘。最后两个参数为NULL表示滚动整个客户区。此时,windows自动将新滚动出现的地方无效化,从而产生一条WM_PAINT消息。它是少数几个能改变窗口的客户区显示的非GDI函数
第五章:绘图基础
5.1 GDI的结构
5.1.1 GDI的结构
P99:GDI的一个主要目的就是支持与设备无关的图形
P99:图形输出设备分为两类:光栅设备和矢量设备。大多数PC出书设备是光栅设备。矢量设备使用线条来绘制图形,通常指绘图机。
P100:GDI总体上来说只是一个静态显示系统,对动画的支持很有限。
5.1.2 GDI函数调用
P101:GDI函数分为以下几类
1:获取(或建立)和释放(或销毁)设备环境的函数:GetDC(),ReleaseDC()。BeginPaint和EndPaint属于USER模块
2:获取设备环境信息的函数:如GetTextMetrics。
3:绘图函数
4:设置和获取设备环境属性的函数
5:使用GDI“对象”的函数
5.1.3 GID的基本图形
5.1.4 其他
P102:图元文件:是以二进制形式存储的GDI命令的集合。调色板:仅在支持256种颜色时,才可以使用自定义的调色板。
5.2 设备环境
5.2.1 获取设备环境句柄
P104:调用BeginPaint使rcPaint的区域有效。
P104:CreateDC(pszDriver,pszDevice,pszOutput,pData)与DeleteDC()
P104:使用CreateIC获取信息上下文,可以获取信息,但是往设备上写东西的时候不能使用。
P104:处理位图时,可能会用到内存设备环境:CreateCompatibleDC(hdc)和DeleteDC()
P105:图元文件:CreateMetaFile(pszFilename);CloseMetaFile();使用hdcMeta所做的任何事情GDI调用都不会被直接显示出来,它们会变成图元文件的一部分。CloseMetaFile()会使图元文件设备环境句柄无效,该函数返回一个图元文件句柄。
5.2.2 获取设备环境的信息
P105:
iValue=GetDeviceCaps(hdc,iIndex);
//iIndex为WINGDI.H头文件中的29个标示符之一
5.2.3 DEVCAPS1程序
5.2.4 设备的尺寸
P108:打印机的分辨率通常用每英寸的点数表示,视频显示器的分辨率是以水平和垂直方向上的总数给出。
P110:字体字符的大小是有点值(磅值)来表示,1点通常假定刚好是1/72英寸
P112:HORZSIZE和VERTSIZE的值是根据HORZRES,VERTRES,LOGPIXELSX,LOGPIXELSY计算出来的。
5.2.5 色彩ABC
P113:真彩每像素24位,高彩每像素16位(5-6-5RGB)
P114:256色的视频适配器会使用颜色调色板,在这种情况下,使用NUMCOLORS参数的GetDeviceCaps函数会返回windows保留的色彩数,为20,windows程序可以使用调色板管理器设定剩余的235种色彩。对高彩和真彩视频适配器,使用NUMCOLORS参数的GetDeviceCaps函数通常会返回-1,所以它对于确定色彩数来说不是一个可靠的函数。应该使用PLANES和BITSPIXEL的值来确定。
5.2.6 设备环境属性
P115:表格
5.2.7 保存设备环境
P116:当设备环境调用ReleaseDC或者EndPaint函数时,对属性所做的任何改变都将丢失。要使改变生效,在注册窗口时间CS_OWNDC标志作为窗口类样式的一部分即可。CS_OWNDC只影响通过GetDC和BeginPaint函数获得的设备句柄。可以使用SaveDC和RestoreDC,类似于push和pop.
5.3 点和线的绘制
5.3.1 设定像素
P117:
SetPixel(hdc,x,y,crColor);
GetPixel(hdc,x,y);
5.3.2 直线
P118:为了画一条直线,要调用两个函数:
MoveToEx(hdc,xbeg,ybeg,null);//最后一个参数表示返回运行该函数之前的位置,是一个直线POINT结构的指针。
LineTo(hdc,xEnd,yEnd);
P120:将数组的点连成线,不会头尾相连
Polyline(hdc,apt,4);
P120:头尾相连,将当前位置设置为最后一根线的终点
PolylineTo(hdc,apt,4);
5.3.3 边框绘制函数
P123:严格来说,Rectangle函数并不是一个画线函数,它会填充这个矩形。在默认情况下,GDI使用白色来填充区域,所以看不出填充效果。
5.3.4 贝塞尔样条曲线
P131:
P133:6种GDI对象:画笔,画刷,位图,区域,字体,调色板,除了调色板以外,所有这些对象都通过SelectObject对象选入。
PolyBezier(hdc,apt,iCount);//前4个点表示第一条曲线的四个控制点,之后每三个与前一条曲线最后一个点形成4个控制点
PolyBezierTo(hdc,apt,iCount);//取当前位置为第一个点.
5.3.5 使用现有画笔
P132:HPEN 画笔,使用GetStockObject()来获取系统画笔,SelectObject(hdc,hPen)来选入画笔,返回先前的画笔
5.3.6 创建、选择和删除画笔
P133:三条规则:1:最终应当删除你所创建的所有GDI对象2:当GDI对象被选入一个有效的设备环境时,不要删除它3:不要删除备用对象
P133:CreatePen(iPenStyle,iWidth,crColor)
P133:对于PS_SOLID、PS_NULL、PS_INSIDEFRAME样式,参数iWidth表示画笔的宽度。当iWidth值为0时,windows把画笔的宽度设定为1个像素。如果指定使用虚线或者点线样式,同时把画笔宽度设定为大于1个像素,那么windows会使用实心的画笔来替代。crColor指定画笔颜色,对所有除了PS_INSIDEFRAME之外的画笔样式,当画笔选入设备环境时,windows将该颜色转换成设备所能表示的最近的纯色。PS_INSIDEFRAME画笔样式是唯一能够使用抖动色的画笔样式,并且只有当画笔宽度大于1时才如此。同时,PS_INSIDEFRAME用于填充区域的函数时有奇特之处。当不使用此样式时,如果用于绘制轮廓的画笔宽度大于1个像素,那么画笔的中心会处于边界之上,这样画出的轮廓线部分将会在边框外面。但对于PS_INSIDEFRAME整个轮廓线都会在边框内。
P134:也可以使用CreatePenIndirect函数。需先定义一个LOGPEN结构。LOGPEN包含3个字段:lopnStyle表示画笔样式,lopnWidth一个POINT结构,便是宽度,仅使用x字段,y字段会被忽略。
P134:获得LOGPEN结构中个字段成员值:GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen).
P134:获得当前选入设备环境的画笔句柄:hPen=GetCurrentObject(hdc,OBJ_PEN);
5.3.7 填充空隙
P135:空隙指点与虚线之间的空隙。它的颜色由设备环境的两个属性,背景模式和背景颜色决定。默认的背景模式是不透明的,默认的背景颜色是白色。设置背景颜色SetBkColor(hdc,crColor);设置背景模式SetBkMode(hdc,TRANSPARENT)设置为透明,透明情况会忽略背景颜色从而不填充空隙。mode为TRANSPARENT或者QPAQUE
5.3.8 绘图模式
P136:当windows使用一个画笔绘制直线时,实际上是在将画笔的像素颜色和目标显示表面的像素颜色进行布尔运算。对像素颜色执行一个按位布尔运算成为光栅操作(ROP),绘制一条直线涉及2种像素颜色,所以这里的操作也叫ROP2。windows有16种ROP2运算码,在默认的设备环境中,绘图模式是R2_COPYPEN,即简单的赋值画笔像素的颜色到目标像素。
P137:SetROP2(hdc,iDrawMode);GetROP2(hdc);
P137:R2_NOTCOPYPEN绘图模式下,若画笔为黑色,则绘制成白色,若画笔为白色,则绘制成黑色。R2_NOT绘图模式总是将目标颜色取反来确定绘制的线的颜色,而不管画笔颜色。
5.4 绘制填充区域
P138:如果需要绘制一个不含边框的多边形,将NULL_PEN选入设备环境。同样,若只要边框不要填充,将NULL_BRUSH选入设备环境。
5.4.1 Polygon函数和多边形填充模式
P139:Polygon(hdc,apt,iCount);PolyPolygon(hdc,apt,aiCounts,iPolyCount);//最后两个参数指每个多边形的点数和多边形总个数
P139:设置多边形填充模式:SetPolyFillMode(hdc,iMode);多边形默认填充模式是ALTERNATE.
P139:ALTERNATE模式:往无穷远处发射射线,穿过基数条边框的区域才填充。WINDING模式:穿过基数条边框填充,对于偶数条,不同方向的边框线(相对于射线方向)的数目相同则不填充,否则填充
5.4.2 用画刷填充内部
P143:建立逻辑画刷:CreateSolidBrush(crColor);CreateHatchBrush(iHatchStyle,crColor);
P143:对于画笔和画刷的颜色,在选入后,windows都会把这种颜色转换为显示器上可用的最近的纯色。
5.5 GDI映射模式
P145:设置映射模式:SetMapMode(hdc,iMapMode)
P145:在MM_TEXT模式下,逻辑单位和物理单位相同
5.5.1 设备坐标和逻辑坐标
P146:windows对所有消息,所有的非GDI函数,甚至一些GDI函数,都继续使用设备坐标。
5.5.2 设备坐标系统
P146:客户区坐标与屏幕坐标转换:ClientToScreen,ScreenToClient.
P146:在所有的设备坐标系统中,单位都是以像素的形式表示
5.5.3 视口与窗口
P148:将获取的客户区大小转换到逻辑坐标:
GetClientRect(hwnd,&rect);
DPtoLP(hdc,(PPOINT)&rect,2);
5.5.4 使用MM_TEXT
P148:MM_TEXT模式下,窗口范围和视口范围都是(1,1)且不可改变
P149:无论怎么修改窗口和视口原点,设备点(0,0)始终都位于客户区的左上角。若想将视口原点改为(x,y),那么逻辑点(0,0)江北映射到设备点(x,y)。如果想将窗口原点改为(x,y),那么逻辑点(x,y)将映射到设备点(0,0);
5.5.5 度量映射模式
5.5.6 自定义的映射模式
P153:只有MM_ISOTROPIC和MM_ANISOTROPIC下,windows才允许改变我们改变视口和窗口的范围。
P155:MM_ISOTROPIC要求x,y逻辑单位表示的物理尺度相同,windows会自动调整较大的一方。
5.6 矩形、区域和剪裁
5.6.1 处理矩形
P162:关于矩形操作的函数
FillRect(hdc,&rect,hBrush);
FrameRect(hdc,&rect,hBrush);
InvertRect(hdc,&rect):区域内所有像素翻转,1变0,0变1.。
OffsetRect(&rect,x,y):
InflateRect(&rect,x,y):增大或减小矩形的尺寸。
SetRectEmpty(&rect)
CopyRect(&DestRect,&SrcRect1,&SrcRect2)
IntersectRect(&DestRect,&SrcRect1,&SrcRect2)
UnionRect(&DestRect,&SrcRect1,&SrcRect2)
IsRectEmpty(&rect)
PtInRect(&rect,point)
5.6.2 随机矩形
P163:PeekMessage(&msg,NULL,0,0,PM_REMOVE);它允许一个程序检查程序队列中的下一个消息,而不是真实的获得并删除它看到的消息。第二个参数为窗口句柄,后两个表示信息范围,这三个参数设置为NULL,0,0表示我们想使用函数返回程序中所有窗口的所有信息。PM_REMOVE删除队列中的消息,不想删除,则设置为PM_NOREMOVE.
P163:GetMessage函数并不把控制权交换给程序,除非它从程序的消息队列中获得了消息,但是PeekMessage函数却总是立即返回。对于WM_QUIT消息,GetMessage函数的返回值是FALSE,但是PeekMessage函数返回值表示队列中是否有消息,因此需检查消息是否为WM_QUIT。
P164:使客户区的无效区域变成有效是从队列中删除WM_PAINT消息的唯一办法。
5.6.3 建立和绘制区域
P166:区域也是GDI对象。建立区域:hRgn=CreateRectRgn(xLeft,yTop,xRight,yBottom)或者hRgn=CreateRectRgnIndirect(&rect)
P166:区域结合:iRgnType=CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine);
iCombine值 | 新的区域 |
RGN_AND | 公共部分 |
RGN_OR | 全部 |
RGN_XOR | 全部减去公共部分 |
RGN_DIFF | SRC1不在SRC2中部分 |
RGN_COPY | src1的全部 |
iRgnType值 | 含义 |
NULLREGION | 空区域 |
SIMPLEREGION | 简单的矩形、椭圆或者多边形 |
COMPLEXREGION | 矩形、椭圆或多边形的组合 |
ERROR | 有错误发生 |
5.6.4 矩形与区域的剪裁
P168:可以将一个区域选入到设备环境来创建自己的剪裁区域。SelectObject(hdc,hRgn)或者SelectClipRgn(hdc,hRgn)
5.6.5 CLOVER程序
第六章:键盘
6.1 键盘基础
6.1.1 忽略键盘
6.1.2 谁获得了焦点
P174:接收到这个键盘事件的窗口称为有输入焦点的窗口。具有输入焦点的窗口要么是活动窗口,要么是活动窗口的子孙窗口。
P175:窗口过程通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来确定自己的窗口是否具有输入焦点。
6.1.3 队列和同步
P175:系统消息队列是一个单独的消息队列,它被windows用来初步存储用户从键盘和鼠标输入的消息。仅当windows应用程序完成了对前一个用户输入消息的处理后,windows才能从系统消息队列中取出下一条消息,并把它放入应用程序消息队列。
6.1.4 击键和字符
P175:引用程序从windows接受的关于键盘事件的消息可分为击键和字符两种。对可产生可显示字符的击键组合,windows在发送击键消息的同时还发送支付消息。
6.2 击键消息
P176:按下一个键时,windows将WM_KEYDOWN或者WM_SYSKEYDOWN消息放入具有输入焦点的窗口的消息队列中。释放时,windows把WM_KEYUP或者WM_SYSKEYUP消息放入相应的消息队列中。
P176:如果按下一个键不放时,则被认为发生了一次连续按键行为,windows将发送给窗口过程一连串的WM_KEYDOWN或WM_SYSKEYDOWN消息。
6.2.1 系统键击和非系统键击
P176:应用程序通常忽略WM_SYSKEYUP和WM_SYSKEYDOWN消息,把它们交付给DelWindowProc函数完成默认处理。
P177:对所有四类击键消息,wParam是虚拟键代码,用于表示哪个键被按下或释放,而lParam包含属于本次击键的一些其他数据
6.2.2 虚拟键代码
P179:数字键和字母键的虚拟键代码就是ASCII码。
6.2.3 lparam信息
![](http://img237.ph.126.net/MhhSsOACXNaHU196AMbhPQ==/1372190511466070986.jpg)
P180:重复计数大于1表明此时连续击键的速度快于程序的处理能力。如果击键结果乃至于IBM加强型键盘的附加键,则扩展键标记为1。如果在击键的同时也按下了Alt,则内容代码为1,WM_SYSKEYUP和WM_SYSKEYDOWN消息的此位始终为1,而WM_KEYUP和WM_KEYDOWN消息的此位始终为0,但是有两种例外:1:如果活动窗口最小化了,则它不具有输入焦点,所有的击键将产生WM_SYSKEYUP和WM_SYSKEYDOWN消息,如果Alt未按下,则内容代码为0。2:在某些非英语的键盘上,一些字符是通过shift键,ctrl键同另一个键的组合产生,此时,内容代码被设置为1,但消息并不是系统击键消息。如果键以前是处于释放状态的,则键的先前状态为0,否则为1。如果键正在被按下,转换状态为0,如果键正在被释放,转换状态为1。
6.2.4 转义状态
P181:iState=GetKeyState(VK_SHIFT),它并非实时的检查键盘状态,它反映了一个到目前为止的键盘状态,并包含了正在被处理的当前消息。
6.2.5 使用击键消息
P182:windows会把菜单快捷键转换为菜单命令消息,所以引用程序不必自己处理这些击键。
6.2.6 为SYSMETS加上键盘处理功能
P183:SendMessage(hwnd,message,wParam,lParam)
6.3 字符消息
P189:TranslateMessage函数负责把击键消息抓乱为字符消息。
6.3.1 四类字符消息
P189:WM_CHAR,WM_DEADCHAR,WM_SYSCHAR,WM_SYSDEADCHAR。前两个消息乃至于WM_KEYDOWN消息,后两个来自WM_SYSKEYDOWN消息。此时,wParam代表ANSI或者Unicode支付吗,lParam和击键的意义一样。大多数情况,windows程序仅处理WM_CHAR消息。
P190:fUnicode=IsWindowUnicode(hwnd);
6.3.2 消息排序
6.3.3 控制字符的处理
P191:如果需要读取输入到窗口的键盘字符,则处理WM_CHAR消息,如果需要读取光标键、功能键、Delete键、Insert键、Shift键、Ctrl键和Alt键,则处理WM_KEYDOWN消息。
P191:控制字符:TAB键,回车键,空格键,ESC键。处理WM_CHAR。
6.3.4 死字符消息
P192:给字母加上音调的键称为死键。
6.4 键盘消息和字符集
6.4.1 KEYVIEW1程序
6.4.2 非英语键盘问题
6.4.3 字符集和字体
P199:windows支持3种字体:位图字体:每个字符由对应于显示器的像素的一组位值定义,windows在标题栏,菜单,按钮和对话框中使用位图字体。矢量字体:字符由简单的线条组成,这些线条没有定义填充区域,矢量字体可以缩放至任意大小,但字符看起来有些单薄。TrueType字体:由填充区域来定义字符的轮廓字体,windows利用TrueType字体达到真正的所见即所得。
P200:在控制面板中选择小字体时,使用的是以VGA开头的字体文件,大字体为以8514开头的字体文件。
P203:ASCII是7位码,从0x20到0x7E。控制字符从0x00-0x1F,以及0x7F。
P204:在Unicode中,编码0x0000-0x007F同样是ASCII,0x0080-0x009F复制了从0x0000到0x001F的控制字符,0x00A0-0x00FF同windows中使用的ANSI字符集是一样的。
6.4.4 Unicode解决方案
6.4.5 TrueType字体和大字体
P208:我们使用的位图字体最多包含256个字符。
6.5 插入符号(不是光标)
P214:光标特指表示鼠标位置的位图图像,即鼠标指针。
6.5.1 一些关于插入符号的函数
P214:五个基本的插入符号函数
函数 | 作用 |
CreateCaret | 创建和窗口关联的插入符号 |
SetCaretPos | 设置窗口类的插入符号位置 |
ShowCaret | 显示插入符号 |
HideCaret | 隐藏插入符号 |
DestroyCaret | 销毁插入符号 |
P214:不应该简单的在窗口过程的WM_CREATE消息中创建它并在WM_DESTROY消息中销毁它。原因是一个消息队列仅能够支持一个插入符号,如果程序有多于一个窗口,则多个窗口必须有效的共享同一个插入符号。
P214:当窗口过程收到输入焦点时,接收到一个WM_SETFOUS消息。失去焦点时,收到一个WM_KILLFOCUS消息。在窗口过程处理WM_SETFOCUS消息时调用CreateCaret函数,处理WM_KILLFOCUS消息时调用DestroyCaret函数。
P214:创建的插入符号是隐藏的。如果窗口过程处理的是一个非WM_PAINT消息,但是要在窗口内绘制某些东西时,它必须调用HideCaret隐藏插入符号。当它结束在窗口内的绘制之后,再调用ShowCaret来显示。HideCaret的效果是叠加的,如果调用了多次,但从来没有调用过ShowCaret,那么必须调用同样次数的ShowCaret才能使插入符号可见。
6.5.2 TYPER程序
第七章:鼠标
7.1 鼠标的基础知识
P222:判断是否连接了鼠标
fMouse=GetSystemMetrics(SM_MOUSEPRESENT);\\TRUE表示有连接鼠标
cButtons=GetSystemMetrics(SM_CMOUSEBUTTONS);\\确定所安装的鼠标按钮个数
7.1.1 一些基本术语
P222:鼠标指针具有一个单像素精度的热点,热点在显示色别上指示了一个精确的位置。
7.1.2 鼠标的复数形式是什么
7.2 客户区鼠标消息
P223:windows只把键盘消息发送到当前具有输入焦点的窗口。鼠标消息则不同:当鼠标经过窗口火灾窗口内被单击,则即使该窗口是非活动窗口或不带输入焦点,窗口过程还是会受到鼠标消息。
P224:参数lParam包含了鼠标的位置信息,其中低字位表示x坐标,高字位表示y坐标。参数wParam表示鼠标按钮、shift键和Ctrl键的状态。
P224:鼠标经过窗口的客户区时,windows不会为鼠标经过的每个像素位置都产生WM_MOUSEMOVE消息,程序收到的WM_MOUSEMOVE消息个数取决于鼠标硬件和窗口过程处理鼠标移动消息的速度。如果消息队列中海油未处理的WM_MOUSEMOVE消息,windows就不会重复向消息队列中添加该消息。
7.2.1 简单的鼠标处理示例
7.2.2 处理shift键
P228:GetKeyState函数返回的是当前鼠标或键盘的状态。对于键盘中为按下的键以及没有按下鼠标,不能使用GetKeyState
7.2.3 鼠标双击
P229:如果想让窗口过程接收鼠标双击消息,则wndclass的style要加上CS_DBLCLKS.
7.3 非客户区鼠标消息
P230:非客户区消息wParam表示非客户区鼠标移动或单击的位置,是以HT为首的标示符。lParam与客户区的一致,表示鼠标的位置。但是,坐标是屏幕坐标。非客户区消息一般由DelWindowProc处理
7.3.1 击中测试消息
P231:WM_NCHITTEST表示非客户区击中测试,这个消息的优先级高于其他所有的客户区和非客户区鼠标消息。lParam表示鼠标位置的屏幕坐标xy,wParam没有用到。
7.3.2 消息引发消息
P231-232
7.4 程序中的击中测试
7.4.1 一个假想的例子
7.4.2 一个简单的程序
7.4.3 使用键盘模仿鼠标操作
7.4.4 在CHECKER中增加键盘接口
7.4.5 在击中测试中使用子窗口
P240:每个子窗口过程只接收与自身窗口有关的鼠标消息,鼠标消息的参数lParam中包含的坐标是相对于子窗口客户区左上角的,而不是相对于父窗口的客户区。