三.窗口和消息
程序通常不直接调用窗口消息处理程序,窗口消息处理程序通常由windows本身调用。
通过调用SendMessage函数,程序能够直接调用它自己的窗口消息处理程序。

前缀 类别
CS 窗口类别样式
CW 建立窗口 
DT 绘制文字
IDI 图示ID
IDC 游标ID
MB 消息框
SND 声音
WM 窗口消息
WS 窗口样式
 
结构 含义
MSG 消息结构
WNDCLASS 窗口类别结构
PAINTSTRUCT 绘图结构
RECT 矩形结构

标识符 含义
HINSTANCE 执行实体(程序自身)句柄
HWND 窗口句柄
HDC 设备内容句柄


四.输出文字
Windows应用程序一般使用两种方法来取得设备内容句柄,以备在屏幕上绘图。除CreateDC建立的设备内容外,其余设备内容句柄获取和释放必须成对出现。
BeginPaint和EndPaint,GetDC和ReleaseDC必须成对出现。
1.
hdc=BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
2.要得到窗口显示区域的设备内容句柄,可以调用GetDC来取得句柄,在使用完后调用ReleaseDC
hdc=GetDC(hdc);
ReleaseDC(hwnd,hdc);
与从BeginPaint传回设备内容句柄不同,GetDC传回的设备内容句柄具有一个剪取矩形,它等于整个显示区域。
GetDC不会使任何无效区域变为有效。如果需要使整个显示区域有效,可以调用ValidateRect(hwnd,NULL);


TextOut(hdc,x,y,psText,iLength);是用于显示文字最常用的GDI函数。


滚动条:在CreateWindow的第三个参数中包含窗口样式(WS)标示符 WS_VSCROLL(垂直滚动条)或WS_HSCROLL(水平滚动条)即可。


处理完滚动条后,调用IncalidateRect(hwnd,NULL,TRUE);使显示区域失效,这将导致Windows将一个WM_PAINT消息放入消息队列中。
(WM_PAINT是低优先级消息,所以可能会有延迟)。如果希望立即更新无效区域,可以在调用InvalidateRect之后调用UpdateWindow(hwnd);//如果显示区域的任一部分无效,则UpdateWindow将导致Windows用WM_PAINT消息调用窗口消息处理程序。这时WM_PAINT消息不进入消息队列,直接由Windows调用窗口消息处理程序。


在窗口大小改变时,Windows给窗口消息处理程序发送一个WM_SIZE消息。传给窗口消息处理程序的lParam参数的低字组中包含显示区域的宽度,高字组中包含显示区域的高度。
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0;


五.图形基础
取得设备内容句柄:
1.WM_PAINT消息中使用BeginPaint和EndPaint函数。
2.非WM_PAINT消息的GetDC和ReleaseDC函数。
3.适用于整个窗口(包括标题列、菜单、滚动条)的设备内容句柄,GetWindowDC和ReleaseDC。不常用,用时必须拦截处理WM_NCPAINT消息(此消息在窗口的非显示区域上绘图)。
4.CreateDC和DeleteDC函数来取得整个屏幕的设备内容句柄。


取得设备内容信息:
GetDeviceCaps(hdc,index);


保存设备内容:
SaveDC(hdc);//未保存返回值
RestoreDC (hdc,-1);//这就将设备内容恢复到最近由SaveDC函数保存的状态中。


画点和线:
理论上,只要提供SetPixel和GetPixel函数,就可以使用图形设备驱动程序绘制一切东西。
SetPixel(hdc,x,y,crColor);


画笔:
Windows程序以句柄来使用画笔。HPEN即画笔的句柄。
HPEN hPen=GetStockObject(WHITE_PEN);
选进设备内容:SetleteObject(hdc,hPen);


建立自己的画笔:使用CreatePen或CreatePenIndirect建立逻辑画笔(这仅仅是对画笔的描述,逻辑画笔是一种GDI对象),这些函数传回画笔的句柄,然后,调用SelectObject将画笔选进设备内容。
在使用画笔等GDI对象时,应遵守以下三条规则:
最后删除自己建立的所有GDI对象(DeleteObject(hPen););当GDI对象正在一个有效的设备内容中使用时,不要删除它;不要删除现有对象。


CreatePen函数语法:
hPen=CreatePen(iPenStyle,iWidth,crColor);
选进设备内容:
SeleteObject(hdc,hPen);  //可以将CreatePen和SeleteObject调用组合到一个叙述中:SeleteObject(hdc,CreatePen(PS_DASH,0,RGB(255,0,0)));建立画笔并选进设备。
在处理WM_DESTROY消息期间(或者调用EndPaint释放设备内容之后),删除建立的画笔:
DeleteObject(hPen);


填入空隙:
空隙颜色取决于:背景模式和背景颜色。
模式为:OPAQUE(不透明),将使用背景色填入空隙
       TRANSPARENT(透明),忽略背景色,不填入空隙。
SetBkColor(hdc,crColor);//可改变背景色。对应的GetBkColor用来取得设备内容定义的目前背景色。
SetBkMode(hdc,TRANSPARENT);//可改变模式。对应的GetBkMode可用来取得目前背景模式。


HBRUSH画刷句柄类似于HPEN。//Windows定义六种现有画刷:WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH(HOLLOW_BRUSH)。
HBRUSH hBrush=GetStockObject(GRAY_BRUSH);//定义画刷句柄,并初始化为GetStockObject取得的现有画刷句柄:GARY_BRUSH。
SelectObject(hdc,hBrush);//选进设备内容
要画没有边界框的图形,用NULL_BRUSH画刷。
当然,类似自定画笔,也可自定画刷。
hBrush=CreateSolidBrush(crColor);//用RGB色彩建立画刷
hBrush=CreateHatchBrush(iHatchStyle,crColor);//用影线标记(6种风格)来建立画刷
……
逻辑坐标和设备坐标:
逻辑坐标是独立于设备的,它与设备点的大小无关。使用逻辑单位,是实现"所见即所得"的基础。
 Windows将GDI函数中指定的逻辑坐标映射为设备坐标,在所有的设备坐标系统中,单位以像素点为准,水平值从左到右增大,垂直值从上到下增大。//http://www.cnblogs.com/fujinliang/archive/2012/10/31/2748290.html
第三遍了都,一直纠结于这个。这么一句话,立马就懂了。感谢。
DPtoLP(hdc,pPoints,iNumber);//设备点转为逻辑点
LPtoDP(hdc,pPoints,iNumber);//逻辑点转为设备点


视端口和窗口:
映像方式用于定义从[窗口](逻辑坐标)到[视窗口口](设备坐标)的映像。
[视端口]是依据设备坐标(像素)的。
[窗口]是依据逻辑坐标的,逻辑坐标可以是像素、毫米、英寸或者其他单位。


Windows提供了函数SetViewportOrgEx和SetWindowOrgEx,用来改变视端口和窗口的原点,这些函数都具有改变轴的效果,以致(0,0)不再指左上角。
我们来看一看这些函数有何效果:如果将视端口原点改变为(xViewOrg,yViewOrg),则逻辑点(0.0)就会映像为设备点(xViewOrg,yViewOrg)。如果将窗口原点改变为(xWinOrg,yWinOrg),则逻辑点(xWinOrg,yWinOrg)将会映像为设备点(0,0),即左上角。不管对窗口和视端口原点作什么改变,设备点(0,0)始终是显示区域的左上角。
//终于懂了555,以前都没仔细看的。


SetViewportOrgEx的参数总是使用设备单位
SetWindowOrgEx参数总是使用逻辑单位


例:假设显示区域为cxClient个像素宽和cyClient个像素高。如果想将逻辑点(0,0)定义为显示区域中心,可以:
SetViewportOrgEx(hdc,cxClient/2,cyClient/2,NULL);
//则逻辑点(0,0)映像为设备点(cxClient/2,cyClient/2)。显示区域的右下角为逻辑点(cxClient/2,cyClient/2)。

同样的效果也可以使用SetWindowOrgEx实现:
SetWindowOrgEx(hdc,-cxClient/2,-cyClient/2,NULL);
//则逻辑点(-cxClient/2,-cyClient/2)映像为设备点(0,0)。即显示区域左上角


下面两函数取得目前视端口和窗口的原点:
GetViewportOrgEx(hdc,&pt);//传回设备坐标
GetWindowOrgEx(hdc,&pt);  //传回逻辑坐标


[度量]映像方式:
由于x轴和y轴的逻辑坐标映像为相同的实际单位,这些映像方式使得可以画出不变形的圆和矩形。


[自行决定]的映像方式:
MM_ISOTROPIC
MM_ANISOTROPIC
这两种映像方式可以用来改变视端口和窗口范围,即可以改变 视端口/设备坐标窗口范围(xViewExt,yViewExt)和 窗口/逻辑坐标窗口范围(xWinExt,yWinExt)
.1 MM_ISOTROPIC 若想保证两个轴上的逻辑单位相同,则MM_ISOTROPIC映像方式最理想。
.2 MM_ANISOTROPIC 在MM_ISOTROPIC映像方式下设定窗口和视端口范围时,Windows会调整范围,以便两条轴上的逻辑单位具有相同的实际尺度。在MM_ANISOTROPIC映射方式下,Windows不对您所设定的值进行调整,这就是说,MM_ANISOTROPIC不需要维持正确的纵横比。


SetMapMode (hdc, MM_ANISOTROPIC) ;
SetWindowExtEx (hdc, 32767, 32767, NULL) ;
SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;
SetViewportOrgEx (hdc, 0, cyClient, NULL) ;
在MM_ISOTROPIC方式下,相似的程序代码导致显示区域的一部分在轴的范围之外。但是对于MM_ANISOTROPIC,不论其尺度多大,显示区域的右上角总是(32767, 32767)。如果显示区域不是正方形的,则逻辑x和y的单位具有不同的实际尺度。 


[补]SIZE结构://书中此时出现了SIZE结构,前面还未出现过。
结构SIZE表示一个矩形的长度和宽度,其定义为:
typedef struct tagSIZE{
LONG cx;
LONG cy;
} SIZE;
其中 cx、cy分别是宽度和高度。  --来源CSize百科。
和POINT差不多:
typedef struct tagPOINT {
   LONG x;
   LONG y;
} POINT;      --来源 SIZE,POINT,CPoint, CSize浅谈




矩形、区域和剪裁:
Windows提供了9个函数,用来更方便的操作RECT结构。
要将RECT结构的四个字段设定为特定值,通常使用如下的程序段:
rect.left          = xLeft ;        
rect.top           = xTop ;        
rect.right         = xRight ;
rect.bottom        = xBottom ;
SetRect函数可以得到相同的效果:
SetRect(&rect,xLeft,xTop,xRight,xBottom);
本来都不打算细看下面的8个函数了,不过这个很有用的样子:
OffsetRect(&rect,x,y); //将矩形沿x轴和y轴移动几个单元。
bInRect=PtInRect(&rect,point);//确定点是否在矩形内。
游戏中可以用到。
PeekMessage函数:
PeekMessage(&msg,NULL,0,0,PM_REMOVE);//前面四个参数(MSG结构指针、窗口句柄、两个值指示消息范围)与GetMessage的参数相同。PeekMessage的最后一个参数设定为PM_REMOVE,表示要将消息从消息队列中删除。如果不希望删除消息,则使用PM_NOREMOVE。
普通的消息循环使用的GetMessage不将控制传回给程序,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现。当又一个消息时,PeekMessage传回TRUE,并按通常方式处理消息,当队列中没有消息时,PeekMessage传回FALSE。
普通的消息循环:
while (GetMessage (&msg, NULL, 0, 0)){
    TranslateMessage (&msg) ;        
    DispatchMessage (&msg) ;        
}        
return msg.wParam ;
替换为下面的循环:
while (TRUE){       
    if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)){        
            if (msg.message == WM_QUIT)        
                   break ;        
            TranslateMessage (&msg) ;        
            DispatchMessage (&msg) ;        
    }
    else{
        
            // 完成某些工作的其它行程序   
    }        
}
return msg.wParam ;


InvalidateRect:
InvalidateRect函数使显示的一个矩形区域失效,并产生一个WM_PAINT消息。例如,您可以使用InvalidateRect函数来清除显示区域并产生一个WM_PAINT消息:

InvalidateRect (hwnd, NULL, TRUE) ;

//第五章真够虐心的。