Windows 程序设计第五版学习笔记
第二章
1、WindowsNT以上的版本从底层支持Unicode,Windows98只是小部分支持Unicode。
2、Unicode和DBCS的区别:
l 前者中的每个字符都是16位宽而不是8位宽,在Unicode中,8位数值没有意义。
l DBCS中仍然处理8位数值,有些字节自身定义一个字符,而某些字节则需要和另一个字节共同定义一个字符。
第三章
1、DefWindowProc处理完消息后还会产生其他的消息。例如,用户点击普通窗口的关闭按钮,或者假设用键盘或鼠标从系统菜单中选择了close,DefWindowProc处理这一键盘或鼠标输入,在检测到用户选择了CLOSE选项后,它给窗口过程发送一个WM_SYSCOMMAND消息。WndProc将这个消息传个DefWindowProc。DefWindowProc调用DestroyWindow来响应这个WM_CLOSE消息。DestroyWindow导致Windows给窗口过程发送一个WM_DESTROY消息。WndProc再调用PostQuitMessage,将一个WM_QUIT消息放入消息队列中,以此来响应此消息。这个消息导致WinMain中的消息循环终止,然后程序结束。
2、消息能够被分成“进队列”和“不进队列”两种。队列消息是由Windows放入程序消息对列的。不进对列消息是在Windows调用窗口过程时直接送给窗口过程的。即进队列的消息被“发送”给消息队列,不进队列消息被“发送”给窗口过程。
队列消息基本上是用户输入的结果。
不进队消息许多情况下来自调用特定的Windows函数。
第四章
1、设备描述符表是GDI内部保存的数据结构,设备描述符表与特定的设备(如显示器打印机)相关,对于视频显示器,设备描述符表总是与显示器上的特定窗口相关。当程序要绘图时,必须先获取设备描述符表句柄,获取后,windows用默认的属性值填充内部设备描述符表结构,可以利用GDI函数获取这些属性的当前值和改变默认值。
2、windows将一个WM_PAINT消息放到消息队列中,是因为客户区的一部分无效,如果不调用BeginPaint和EndPaint(或者ValidateRect),则Windows不会使该区域变为有效,相反,Windows将发送另一个WM_PAINT消息,并会一直发送下去。
3、如果要在窗口过程中自己定义一些背景的擦除,可以处理WM_ERASEBKGND消息。
第五章
1、对于打印机,通常用“每英寸点数”表示分辨率。
视频显示器的分辨率是以水平和垂直的总的像素数来表示的。
本书中“分辨率”指每英寸像素点数,“像素大小”和“像素尺寸”表示设备水平和垂直显示的总像素数。“度量大小”和“度量尺寸”是以英寸或mm为单位的设备显示区域的大小。
“像素大小”除以“度量大小”就得到分辨率了。
在传统的排版中,字体的字母大小由“磅”表示,1磅大约1/72英寸,在计算机排版中,1磅正好为1/72英寸。理论上,字体的磅值是从字体中最高的字符顶部到例如jpqy等字母下部的字符底部的距离,其中不包括重音符号。根据TEXTMETRIC结构,字体的磅值等于tmHeight字段减去tmInternalLeading字段。12磅的行距指文本连续行的基线应该间隔12/72英寸。不应该为10磅字体使用10磅行距,因为文本的连续行会碰到一起。
windows系统字体,假设是10磅字体12磅行距,为什么字体都是10磅还分大字体和小字体?解答:当在“控制面板”的“显示”程序上显示小字体或大字体时,实际是选择了一个假定的视频显示分辨率,单位是每英寸的点数,当选择小字体时,windows假定视频显示分辨率为每英寸96点,当选择大字体时,即要windows假定视频显示分辨率是每英寸120点。
用LOGPIXELSX和LOGPIXELSY从GetDeviceCaps得到的值是用户在“控制面板”的“显示”程序中选择的以每英寸的点数为单位的假定分辨率。这两个标志指逻辑像素,意思是”不以每英寸的像素数为单位的实际分辨率”。
用HROZSIZE和VERTSIZE索引从GetDeviceCaps得到的设备能力指物理屏幕的宽度和高度,单位mm。
2、色彩
大多数彩色图形显示设备要么使用过多个色彩平面,要么每像素有多个色彩位,但是不能两者兼得;即下面两个调用必有一个返回1。
iPtanes = GetDeviceCaps(hdc, PLANES); //获取显示平面数
iBitsPixel = GetDeviceCaps(hdc, BITSPIXEL); //获取每个像素的色彩位数
视频适配器能够表示的色彩数计算如下:iColors = 1<<( iPtanes* iBitsPixel)
这个值可能与iColors = GetDeviceCaps(hdc, NUMCOLORS)得到的值不一样,因为256色的视频适配器使用色彩调色板,那种情况下,用GetDeviceCaps得到的是Windows保留的色彩数为20,剩余的236中颜色可以有Windws程序用调色板管理器设置。对于高彩色和全色显示分辨率,带有NUMCOLORS参数的GetDeviceCaps通常返回-1,因此,应该用上面的公式计算。
3、当调用GetDC或BeginPaint时,windows用默认值创建一个新的设备描述附表,对该设备描述附表属性所做的一切改变在调用ReleaseDC或EndPaint时都丢失,如果想保存的话:在注册窗口类的时候加上CS_OWNDC标志,
如wndclass.style = CS_HREDRAW|CS_VRTDRAW|CS.OWNDC
现在,基于这个窗口类所创建的每个窗口都将拥有自己的设备描述附表,它一直存在,直到窗口被删除,如果使用这个标志,就只需要初始化设备描述符表一次。
这个风格只影响GetDC和BeginPaint获得的设备描述符表,不影响其他函数如GetWindowDC函数获得的设备描述附表。
保存设备描述符表的函数SaveDC,RestoreDC;
可以由程序员创建的六种GDI对象是:画笔、刷子、位图、区域、字体、调色板。
4、映射方式
TextOut(hdc, x, y, psText, iLength);以及其他的GDI函数中,坐标值使用的都是一种“逻辑单位”windows必须将逻辑单位转换为“设备单位”即像素。因为默认的映射方式是MM_TEXT,这种模式下的逻辑单位和物理单位相同。
windows对所有消息(WM_MOVE、WM_SIZE等)对所有的非GDI函数,甚至一些GDI函数,永远使用设备坐标。但是GetTextMetrics调用中返回的TEXTMETRIC结构的值是使用逻辑单位的。
5、PeekMessage消息不能从消息队列中删除WM_PAINT消息,但是这并不是什么大不了的问题。毕竟GetMessage并不从消息队列中删除WM_PAINT消息,从队列中删除WM_PAINT消息的为一方法是令窗口客户区的失效区域变得有效,这可以用ValidateRect和ValidateRgn或者BeginPaint和EndPaint对来完成。
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));这句代码从消息队列中删除WM_PAINT之外的所有消息,如果队列中有一个WM_PAINT消息,程序就会永远得陷在while循环中。
第六章 键盘
1、系统消息队列是单消息队列,它由windows维护,用于初步保存用户从键盘和鼠标输入的信息。只有当windows应用程序处理完前一个用户输入消息时,windows才会从系统消息队列中取出下一个消息。
对于产生可显示字符的击键组合,windows不仅给程序发送击键消息,还发送字符消息。对于某些不产生字符的键,windows只产生击键消息。
WM_SYSKEYDOWN和WM_SYSKEYUP消息中的SYS表示该击键对windows比对windows应用程序重要,一般由与Alt相组合的击键产生。
如果活动窗口最小化了,则它没有输入焦点,这时候所有的击键都产生WM_SYSKEYUP和WM_SYSKEYDOWN消息。
2、虽然可以借助击键消息和换挡键状态信息就能将击键消息转换为字符消息,但是不要这么做,因为会遇到国际键盘间的不同带来的问题,如,你得到wParam为0x33的WM_KEYDOWN消息,你就知道用户按下了3,但是,如果用GetKeyState发现shift键被按下,您就可能会认为用户输入了#号,这可不一定,英国用户输入的就是法郎符号。
3、因为TranslateMessage函数从WM_KEYDOWN和WM_SYSKEYDOWN消息产生了字符消息,所以字符消息是夹在击键消息之间传递给窗口过程的。
第七章鼠标
1、windows并不为鼠标的每个可能的像素位置都产生一条WM_MOUSEMOVE消息,程序接收到WM_MOUSEMOVE消息的次数依赖于鼠标硬件以及窗口过程处理鼠标移动消息的速率。即windows不会用未处理的WM_MOUSEMOVE消息来填充消息队列。
2、如果希望窗口过程能够收到双击键的鼠标消息,那么在调用RegisterClass初始化窗口类结构中,必须在窗口风格中包含CS_DBLCLKS标识符。
3、非客户区鼠标消息lParam参数中的坐标是屏幕坐标而不是客户区坐标。
4、WM_NCHITTEST非客户区点击测试,此消息优先于所有其他的客户区和非客户区鼠标消息,windows应用程序通常把这个消息传送给DefWindowProc,然后Windows用WM_NCHITTEST消息产生基于鼠标位置的所有其他鼠标消息,对于非客户区鼠标消息,当处理WM_NCHITTEST时,从DefWindowProc返回的值将成为鼠标消息中的wParam参数,这个值可以是任意非客户区鼠标消息的wParam值再加上以下值HTCLIENT、HTNOWHERE、HTTRANSPARENT、HTERROR,如DefWindowProc在处理其WM_NCHITTEST消息后返回HTCLIENT,那么windwos将把屏幕坐标转换成客户区坐标并产生客户去鼠标消息。
case WM_NCHITTEST: return(LRESULT)HTNCWHERE;会使鼠标键失效。
5、SetCapture(hwnd)函数调用之后,Windows将所有鼠标消息发给窗口句柄为hwnd的窗口过程,鼠标消息总是客户区消息,即使鼠标正在窗口的非客户区。
第八章 计时器
1、windows对WM_TIMER消息的处理非常类似于WM_PAINT消息的处理,这两个消息都是低优先级的,程序只有在消息队列中没有其他消息时才接收它们,而且windows不能持续向消息队列中放入多个WM_TIMER消息,而是将多余的WM_TIMER消息组合成一个消息。
第九章 子窗口控件
1、子窗口控件没有利用tab键或方向键将输入焦点从一个控件移到另一个控件的内部的功能,子窗口控件可以获得输入焦点,但是获得后,将不能把输入焦点返回给父窗口。
2、在CreateWindow函数中指定窗口类为"static",您就可以建立静态的子窗口控制。这些子窗口非常文静。它既不接受鼠标或键盘输入,也不向父窗口发送WM_COMMAND消息。当在静态子窗口上移动或按下鼠标时,这个子窗口将捕获WM_NCHITTEST消息,并将HTTRANSPARENT的值返回给Windows,这将使Windows向下层窗口发送相同的WM_NCHITTEST消息。父窗口常常将该消息传递给DefWindowProc,在这里,它被转换成客户区的鼠标消息。
3、与按钮控件不同,滚动条控件不向父窗口发送WM_COMMAND消息,而是像窗口滚动条那样发送WM_VSCROLL和WM_HSCROLL消息。处理时可以通过L参数区分是窗口滚动条还是滚动条控件。
第十章 菜单及其他资源
1、如果你想在程序运行的时候动态地更改程序的图标,可以使用SetClassLong来达到目的。同样,也有GetClassLong函数。
2、LoadIcon是获取句柄但不需要句柄被清楚的少数几个函数之一。实际有一个DestroyIcon函数,但是它与CreateIcon、CreateIconIndirect、CreateIconFromResource连在一起使用,使得程序能够动态地创建图标图像。
3、在系统菜单中添加的命令ID值必须小于0xF000,还要记住,当为这些新菜单项在窗口过程中处理WM_SYSCOMMAND消息时,您必须把其他的WM_SYSCOMMAND消息发送给DefWindowProc,如果不这么做,那么实际上是禁用了系统菜单上的所有常规选项.
第十一章 对话框
1、对话框消息处理程序和窗口处理程序的区别:
窗口消息处理程序返回值LRESULT,对话框消息处理程序返回值BOOL
如果窗口消息处理程序不处理某个特定的消息,那么它会调用DefWindowProc,如果对话框程序处理一个消息,那么它传回TRUE,否则FALSE。
对话框程序不需要处理WM_PAINT,WM_DESTROY消息,对话框不接收WM_CREATE消息,而是在特殊的WM_INITDIALOG消息期间,对话框程序执行初始化操作。
2、WM_INITDIALOG是对话框接收到的第一个消息,如果对话框程序传回TRUE,那么windows将输入焦点设定给对话框中第一个具有WS_TABSTOP样式的子窗口控件。对话框程序也可以在处理WM_INITDIALOG时使用SetFocus来将输入焦点设定为对话框中的某个子窗口控件,然后返回FALSE。
3、模态对话框的消息不通过您的程序的消息队列,所以不必担心对话框中键盘快捷键的影响。
即使显示对话框时,WndProc也可以继续接收消息,实际上你可以从对话框程序内部给WndProc发送消息。
4、对话框模板中指定大小的单位为平均字符宽度的1/4,及平均字符高度的1/8。
5、模态对话框的建立用DialogBox,非模态对话框的建立用CreateDialog。非模态对话框一般需要ShowWindow才能显示。
6、与模态对话框和消息框的消息不同,非模态对话框的消息要经过程序的消息队列,要将这些消息传送给对话框消息处理程序,则必须改变程序消息队列。方法如下,CreateDialog保存句柄hDlgModeless,然后修改如下:
while (GetMessage (&msg, NULL, 0, 0))
{
if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
如果消息是发送给非模态对话框的,那么IsDialogMessage将它发送给对话框中窗口处理程序,并返回TRUE,否则返回FALSE。
最后需要注意的一点是,使用DestroyWindow而不是EndDialog来结束非模态对话框。
第十二章 剪切板
1、把数据拷贝到剪切板上的代码
hGlobal = GlobalAlloc (GHND | GMEM_SHARE, iLength + 1) ;
pGlobal = GlobalLock (hGlobal) ;
for (i = 0 ; i < wLength ; i++)
*pGlobal++ = *pString++ ;
GlobalUnlock (hGlobal) ;
OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (CF_TEXT, hGlobal) ;
CloseClipboard () ;
在处理同一个消息的过程中调用OpenClipboard和CloseClipboard,不需要时,不要打开剪切板;不要把锁定的内存句柄交给剪切板;当调用SetClipboardData
后,不要再继续使用该内存块,它不再属于使用者程序,不许把句柄看成是无效的,如果继续使用的话,可以制作数据的副本,或从剪切板中读取它,你也可以在SetClipboard和CloseClipboard之间继续使用内存块,但是不要使用传递给SetClipboardData函数的全局句柄,事实上,此函数也传回一个全局句柄,必须锁定这些代码以存取内存,在调用CloseClipboard之前,应该先为此句柄解锁。
2、从剪切板上取得数据.
bAvailable = IsClipboardFormatAvailable (CF_TEXT) ;
OpenClipboard (hwnd) ;
hGlobal = GetClipboardData (CF_TEXT) ;
GlobalUnlock (hGlobal) ;
pGlobal = GlobalLock (hGlobal) ;
CloseClipboard () ;
从GetClipboardData得到的句柄并不属于使用者程序,它属于剪切板,仅在GetClipboardData和CloseClipboard调用之间这个句柄才有效.
3、任何时候只有一个程序可以打开剪切板。
4、在处理你想要的文字格式时,只需要调用SetClipboardData和GetClipboardData,windows将处理剪切板中的所有文字转换,例如,在windowsNT中,如果一个程序用SetClipboardData来处理CF_TEXT剪切板数据形态,程序也能用CF_OEMTEXT调用GetClipboardData,同样的,剪切板也能CF_OEMTEXT数据转换为CF_TEXT。
5、当打开剪贴簿并把数据传送给它时,必须先呼叫EmptyClipboard,通知Windows释放或删除剪贴簿上的内容。不能在现有的剪贴簿内容中附加其它东西。所以,从这种意义上说,剪贴簿每次只能保留一个数据项。但是,可以在EmptyClipboard和CloseClipboard呼叫之间多次呼叫SetClipboardData,每次都使用不同的剪贴簿格式.
OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (CF_TEXT, hGlobalText) ;
SetClipboardData (CF_BITMAP, hBitmap) ;
SetClipboardData (CF_METAFILEPICT, hGlobalMFP) ;
CloseClipboard () ;
当这三种格式的数据同时位于剪贴簿上时,用CF_TEXT、CF_BITMAP或CF_METAFILEPICT参数呼叫IsClipboardFormatAvailable将传回TRUE.
hGlobalText = GetClipboardData (CF_TEXT) ;
hBitmap = GetClipboardData (CF_BITMAP) ;
hGlobalMFP = GetClipboardData (CF_METAFILEPICT) ;
可以获取这些句柄.
在将不同的文字格式、不同的位图格式或者不同的metafile格式添加到剪贴簿时,不要使用这种技术。只使用一种文字格式、一种位图格式以及一种metafile格式.
EnumClipboardFormats程序可以确定剪切板储存的所有格式.CountClipboardFormats可以获得目前在剪切板中不同格式的数目.
文字和字体
1、GetStockObject中指定SYSTEM_FIXED_FONT可以获得等宽字体的句柄。
如果您将新字体选入设备内容时,必须使用GetTextaMetrics计算字符的高度和平均宽度,如果选择了调和字体,那么一定要注意,字符的平均宽度只是个平均值,某些字符会比它宽或比它窄。
2、windows支持两大类字体,GDI字体和设备字体。GDI字体储存在硬盘文件中而设备字体是输出设备本来就有的。通常打印机都有内建的设备字体集
GDI字体有三种:点阵字体,笔划字体,TrueType字体。
点阵字体是通过字体名称识别的,点阵字体名称为:
System(SYSTEM_FONT) FixedSys(SYSTEM_FIXED_FONT) Terminal(OEM_FIXED_FONT)
Courier、MS Serif、MS Sans Serif(DEFAULT_GUI_FONT)、Small Fonts
每个点阵字体只有几种大小(不超过六种)。Courier字体是定宽字体,外形与用打字机打出的字体相似。「Serif」指字体字母笔划在结束时拐个小弯。「sans serif」字体不是serif类的字体
3、TrueType字体的单个字符是通过填充直线和曲线的轮廓来定义的,Windows可以通过改变定义轮廓的坐标对TrueType字体进行缩放。
最初Windows是用了13种TrueType字体
Courier New、Courier New Bold、Courier New Italic、Courier New Bold Italic、Times New Roman、Times New Roman Bold、Times New Roman Italic、Times New Roman Bold Italic、Arial、Arial Bold、Arial Italic、Arial Bold Italic、Symbol在新的windows中这个列表更长了.
GetTextFace函数使程序能够确定目前选入设备内容的字体名称。
GetTextFace(hdc, sizeof(szFaceName)/sizeof(TCHAR), szFaceName);
详细的字体信息可以从GetTextMetrics中获取:
GetTextMetrics(hdc, &textmetric);
LOGGONT用于定义逻辑字体, 而TEXTMETRIC用于取得目前选入设备内容的字体的信息。
浙公网安备 33010602011771号