《Windows程序设计》读书笔记

第三章 窗口和消息

自己的窗口:CreateWindow的第一个参数就是所谓的窗口类别名称,并且该窗口类别连接所谓的窗口消息处理程序。

大写字母标识符 :

读者可能注意到,HELLOWIN.C中有几个大写的标识符,这些标识符是在Windows表头文件中定义的。有些标识符含有两个字母或者三个字母的前缀,这些前缀后头接着一个底线:

CS_HREDRAW DT_VCENTER SND_FILENAME IDC_ARROW WM_CREATE CW_USEDEFAULT IDI_APPLICATION

这些是简单的数值常数。前缀指示该常数所属的类别,如下所示。

前缀 类别
CS 窗口类别样式
CW 建立窗口
DT 绘制文字
IDI 图示ID
IDC 游标ID
MB 消息框
SND 声音
WM窗口消息
WS窗口样式

VK_ 虚拟键码的名称

BS 开头,它表示「按钮样式」

ES编辑样式

EN 编辑控件


WPARAM和LPARAM,这些名字的来源有点历史背景:当Windows还是16位系统时,WndProc的第三个参数被定义为一个WORD,这是一个16位的 无正负号短(unsigned
short)整数
,而第四个参数被定义为一个LONG,这是一个32位有正负号长整数,从而导致了文字「PARAM」前面加上了前置前缀「W」和「L」 。当然,在32位的Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG(这就是C中的long整数型态),因此窗口消息处理程序的这两个参数都是32位的值。

四种数据结构

结构 含义
MSG 消息结构
WNDCLASS 窗口类别结构
PAINTSTRUCT 绘图结构
RECT 矩形结构

句柄简介


标识符 含义
HINSTANCE 执行实体(程序自身)句柄
HWND 窗口句柄
HDC 设备内容句柄
句柄是一个(通常为32位的)整数,它代表一个对象。Windows中的句柄类似传统C或者MS-DOS程序设计中使用的文件句柄。程序几乎总是通过呼叫Windows函数取得句柄。程序在其它Windows函数中使用这个句柄,以使用它代表的对象。代号的实际值对程序来说是无关紧要的。但是,向您的程序提供代号的Windows模块知道如何利用它来使用相对应的对象。
匈牙利表示法
szCmdLine中的sz代表「以0结尾的字符串」 。在hInstance和hPrevInstance中的h前缀表示「句柄」 ;在iCmdShow中的i前缀表示「整数」;由于变量名既描述了变量
的作用,又描述了其数据型态,就比较容易避免产生数据型态不合的错误。
列出了在本书中经常用到的变量前缀。
前缀 数据型态
c char或WCHAR或TCHAR
by BYTE (无正负号字符)
n short
i int
x, y int分别用作x坐标和y坐标
cx, cy int分别用作x长度和y长度;C代表「计数器」
b或f BOOL (int);f代表「旗标」
w WORD (无正负号短整数)
l LONG (长整数)
dw DWORD (无正负号长整数)
fn function(函数)
s string(字符串)
sz 以字节值0结尾的字符串
h 句柄
p 指标
注册窗口类别
在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一 个参数,即一个指向型态WNDCLASS的结构指针。
在这里提示一下数据型态和匈牙利表示法:其中的lpfn前缀代表「指向函数的长指标」 。(在Win32 API中, 长指标和短指标(或者近程指标)没有区别。 这只是16位Windows的遗物。 )cb前缀代表「字节数」而且通常作为一个常数来表示一个字节的大小。h前缀是一个句柄,而hbr前缀代表「一个画刷的代号」 。lpsz前缀代表「指向以0结尾字符串的指针」 。
message 消息标识符。这是一个数值,用以标识消息。对于每个消息,均有一个对应的标识符,这些标识符定义于Windows表头文件(其中大多数在WINUSER.H中) ,以前缀WM( 「window message」 ,窗口消息)开头。Windows就在消息队列中放入一个消息,该消息的message字段等于WM_LBUTTONDOWN
wParam 一个32位的「message parameter(消息参数) 」,其含义和数值根据消息的不同而不同。
lParam 一个32位的消息参数,其值与消息有关。
time 消息放入消息队列中的时间。
pt 消息放入消息队列时的鼠标坐标。
只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012) ,GetMessage就传回一个非零值。WM_QUIT消息将导致GetMessage传回0。
TranslateMessage (&msg) ;将msg结构传给Windows, 进行一些键盘转换。
DispatchMessage (&msg) ; 又将msg结构回传给Windows。
然后, Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。在HELLOWIN中,这个窗口消息处理程序就是WndProe函数。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。
窗口消息处理程序
注册窗口类别,建立窗口,然后在屏幕上显示窗口,程序进入消息循环,然后不断从消息队列中取出消息来处理。
在HELLOWIN中,窗口消息处理程序是命名为WndProc的函数。
窗口消息处理程序总是定义为如下形式:LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
注意, 窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。 第一个参数hwnd是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。对于与HELLOWIN相似的程序(只建立一个窗口),这个参数是程序所知道的唯一窗口句柄。如果程序是依据同一窗口类别(同时也是同一窗口消息处理程序)建立多个窗口,则hwnd标识接收消息的特定窗口。
第二个参数与MSG结构中的message字段相同,它是标识消息的数值。最后两个参数都是32位的消息参数,提供关于消息的更多信息。这些参数包含每个消息型态的详细信息。有时消息参数是两个存放在一起的16位值,而有时消息参数又是一个指向字符串或数据结构的指针。程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。我们将在后面的章节讨论SendMessage函数。
处理消息
窗口消息处理程序所接受的每个消息均是用一个数值来标识的,也就是传给窗口消息处理程序的message参数。Windows表头文件WINUSER.H为每个消息参数定义以「WM」 (窗口消息)为前缀开头的标识符。
一般来说,Windows程序写作者使用switch和case结构来确定窗口消息处理程序接收的是什么消息,以及如何适当地处理它。窗口消息处理程序在处理消息时,必须传回0。窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数。从DefWindowProc传回的值必须由窗口消息处理程序传回。
在HELLOWIN中,WndProc只选择处理三种消息:WM_CREATE、WM_PAINT和WM_DESTROY。窗口消息处理程序的结构如下:
switch (iMsg)
{
case WM_CREATE :
处理WM_CREATE消息
return 0 ;
case WM_PAINT :
处理WM_PAINT消息
return 0 ;
case WM_DESTROY :
处理WM_DESTROY消息
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
呼叫DefWindowProc来为窗口消息处理程序不予处理的所有消息提供内定处理,这是很重要的。不然一般动作,如终止程序,将不会正常执行。
播放声音文件
窗口消息处理程序接收的第一个消息-也是WndProc选择处理的第一个消息-是WM_CREATE。当Windows在WinMain中处理CreateWindow函数时,WndProc接收这个消息。就是说,在HELLOWIN呼叫CreateWindow时,Windows将做一些它必须做的工
作。在这些工作中,Windows呼叫WndProc,将第一个参数设定为窗口句柄,第二个参数设定为WM_CREATE。WndProc处理WM_CREATE消息并将控制传回给Windows。Windows然后可以从CreateWindow呼叫中传回到HELLOWIN中,继续在WinMain中进行下一步的处理。
通常,窗口消息处理程序在WM_CREATE处理期间进行一次窗口初始化。HELLOWIN对这个消息的处理中播放一个名为HELLOWIN.WAV的声音文件。它使用简单的PlaySound函数来做到这一点。该函数说明在/Platform SDK/Graphics and Multimedia
Services/Multimedia Audio/Waveform Audio中, 而文件在/Platform SDK/Graphicsand Multimedia Services/Multimedia Reference/Multimedia Functions中。PlaySound的第一个参数是声音文件的名称(它也可能是在Control Panel的Sounds中定义的一种声音的别名,或者是一个程序资源)。第二个参数只有当声音文件是一种资源时才被使用。第三个参数指定一些选项。在这个例子中,我指定第一个参数是一个文件名,并且异步地播放声音,即PlaySound函数呼叫在声音文件开始播放时立即传回,而不会等待它的完成。在这种方法下,程序能够继续初始化。WndProc通过从窗口消息处理程序中传回0,结束了整个WM_CREATE的处理。
WM_PAINT消息
WM_DESTROY消息
该函数在程序的消息队列中插入一个WM_QUIT消息。前面提到过,GetMessage对于除了WM_QUIT之外的从消息队列中取出的所有消息都传回非0值。而当GetMessage得到一个WM_QUIT消息时,它传回0。这将导致WinMain退出消息循环,并终止程序。然后程序执行下面的叙述:
return msg.wParam ;

一般地,处理WM_PAINT消息的形式如下:
caseWM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
使用GDI函数
EndPaint (hwnd, &ps) ;
return 0 ;

绘图信息结构
前面提到过,Windows为每个窗口保存一个「绘图信息结构」 ,这就是PAINTSTRUCT,定
义如下:
typedef struct tagPAINTSTRUCT
{
HDC hdc ;
BOOL fErase ;
RECT rcPaint ;
BOOL fRestore ;
BOOL fIncUpdate ;
BYTE rgbReserved[32] ;
} PAINTSTRUCT ;

要得到窗口显示区域的设备内容句柄,可以呼叫GetDC来取得句柄,在使用完后呼叫
ReleaseDC:
hdc = GetDC (hwnd) ;
使用GDI函数
ReleaseDC (hwnd, hdc) ;
与BeginPaint和EndPaint一样,GetDC和ReleaseDC函数必须成对地使用。如果在处理某消息时呼叫GetDC,则必须在退出窗口消息处理程序之前呼叫ReleaseDC。不要在一个消息中呼叫GetDC却在另一个消息呼叫ReleaseDC。
与从BeginPaint传回设备内容句柄不同,GetDC传回的设备内容句柄具有一个剪取矩形,它等于整个显示区域。可以在显示区域的某一部分绘图,而不只是在无效矩形上绘图(如果确实存在无效矩形)。与BeginPaint不同,GetDC不会使任何无效区域变为有效。如果需要使整个显示区域有效,可以呼叫ValidateRect (hwnd, NULL) ;
GetDC传回用于写入窗口显示区域的设备内容句柄,而GetWindowDC传回写入整个窗口的设备内容句柄。例如,您的程序可以使用从GetWindowDC传回的设备内容句柄在窗口的标题列上写入文字。然而,程序同样也应该处理WM_NCPAINT ( 「非显示区域绘制」)消息。
TextOut:细节
TextOut是用于显示文字的最常用的GDI函数。语法是:
TextOut (hdc, x, y, psText, iLength) ;

以下将详细地讨论这个函数。
第一个参数是设备内容句柄,它既可以是GetDC的传回值,也可以是在处理WM_PAINT消息时BeginPaint的传回值。
设备内容的属性控制了被显示的字符串的特征。
例如,设备内容中有一个属性指定文字颜色,内定颜色为黑色;内定设备内容还定义了白色的背景。在程序向显示器输出文字时,Windows使用这个背景色来填入字符周围的矩形空间(称为「字符框」 )。 该文字背景色与定义窗口类别时设置的背景并不相同。窗口类别中的背景是一个画刷,它是 一种纯色或者非纯色组成的画刷,Windows用它来擦除显示区域,它不是设备内容结构的 一部分。在定义窗口类别结构时,大多数Windows应用程序使用WHITE_BRUSH,以便内 定设备内容中的内定文字背景颜色与Windows用以擦除显示区域背景的画刷颜色相同。
psText参数是指向字符串的指针,iLength是字符串中字符的个数。如果psText指向Unicode字符串, 则字符串中的字节数就是iLength值的两倍。
字符串中不能包含任何ASCII控制字符(如回车、换行、制表或退格), Windows会将这些控制字符显示为实心块。 Text0ut不识别作为字符串结束标志的内容为零的字节(对于Unicode,是一个短整数型态的0) ,而需要由nLength参数指明长度。
TextOut中的x和y定义显示区域内字符串的开始位置,x是水平位置,y是垂直位置。字符串中第一个字符的左上角位于坐标点(x,y)。在内定的设备内容中,原点(x和y均为0的点)是显示区域的左上角。如果在TextOut中将x和y设为0,则将从显示区域左上角开始输出字符串。

GetPixel函数传回指定坐标处的图素颜色:
crColor = GetPixel (hdc, x, y) ;


SelectObject将传回设备内容中上一次选择的画笔句柄。所以,您可以通过呼叫SelectObject将BLACK_PEN选进设备内容,并删除从SelectObject传回的值:
当您想释放鼠标时,呼叫: ReleaseCapture () ;
通过呼叫GetParent,子窗口消息处理程序能确定其父窗口的窗口句柄:
hwndParent = GetParent (hwnd) ;
其中,hwnd是子窗口的窗口句柄。它可以向其父窗口消息处理程序发送消息:
SendMessage (hwndParent, message, wParam, lParam) ;
您也可以使用:
id = GetDlgCtrlID (hwndChild) ;

虽然函数中的「Dlg」部分指的是对话框,但实际上这是一个通用的函数。
知道ID和父窗口句柄,您就能获得子窗口句柄:
hwndChild = GetDlgItem (hwndParent, id) ;
以下的操作将导致按钮被按下:
SendMessage (hwndButton, BM_SETSTATE, 1, 0) ;

下面的呼叫使按钮恢复正常:
SendMessage (hwndButton, BM_SETSTATE, 0, 0) ;
hwndButton窗口句柄是从CreateWindow呼叫传回的值。
您也可以向按键发送BM_GETSTATE消息,子窗口控件传回按钮目前的状态:如果按钮被按下,则传回TRUE;如果按钮处于正常状态,则传回FALSE。但是,绝大多数应用并不需要这一消息。因为按钮不保留任何开/关信息,所以BM_SETCHECK消息和BM_GETCHECK消息不会被用到。
您可以通过SetWindowText来改变按钮(或者其它任何窗口)内的文字:
SetWindowText (hwnd, pszString) ;
其中hwnd是欲改变窗口的句柄,pszString是一个指向以null为终结的字符串指针。对于一般的窗口来说,这个文字是标题列的文字;对于按钮控件来说,它是随着该按钮显示的文字。
您也可以取得窗口目前的文字:
iLength = GetWindowText (hwnd, pszBuffer, iMaxLength) ;

iMaxLength指定复制到pszBuffer指向的缓冲区中的最大字符数。该函数传回复制的字符数。您可以首先通过下面的呼叫来获得特定文字的长度:
iLength = GetWindowTextLength (hwnd) ;
ShowWindow时子窗口才会被显示出来:
ShowWindow (hwndChild, SW_SHOWNORMAL) ;
如果您将WS_VISIBLE包含在窗口类别中,就没有必要呼叫ShowWindow。但是,您可以
通过呼叫ShowWindow将子窗口隐藏起来:
ShowWindow (hwndChild, SW_HIDE) ;
您可以通过下面的呼叫来确定子窗口是否可见:
IsWindowVisible (hwndChild) ;
您也可以使子窗口被启用或者不被启用。在内定情况下,窗口是被启用的。您可以通过下面
的呼叫使窗口不被启用:
EnableWindow (hwndChild, FALSE) ;
您可以通过下面的呼叫使子窗口再次被启用:
EnableWindow (hwndChild, TRUE) ;
您还可以使用下面的呼叫来确定子窗口是否被启用:
IsWindowEnabled (hwndChild) ;
如果您想建立与窗口滚动条尺寸相同的滚动条控件, 那么可以使用GetSystemMetrics取得水平滚动条的高度:
GetSystemMetrics (SM_CYHSCROLL) ;

或者垂直滚动条的宽度:
GetSystemMetrics (SM_CXVSCROLL) ;
我们可以使用GetWindowWord来得到子窗口的ID:
i = GetWindowLong ((HWND) lParam, GWL_ID) ;
由于子窗口的句柄在建立时就被储存在数组中,所以WndProc就能对相对应的滚动条消息进行处理,并通过呼叫SetScrollPos来设定相对应的新值:
SetScrollPos (hwndScroll[i], SB_CTL, color[i], TRUE) ;
WndProc也改变滚动条底部子窗口的文字:
wsprintf (szBuffer, TEXT ("%i"), color[I]) ;
SetWindowText (hwndValue[i], szBuffer) ;
为了给滚动条提供全面的键盘接口,还需要另外一些工作。首先,WndProc窗口消息处理程序必须使滚动条拥有输入焦点,它是通过处理WM_SETFOCUS消息来完成这一点的,该WM_SETFOCUS消息是当滚动条获得输入焦点时其父窗口接收到的。WndProc给其中一个滚动条设定输入焦点。
SetFocus (hwndScroll[idFocus]) ; 其中idFocus是一个整体变量。
SendMessage (hwndEdit, WM_CUT, 0, 0) ;
SendMessage (hwndEdit, WM_COPY, 0, 0) ;
SendMessage (hwndEdit, WM_CLEAR, 0, 0) ;
WM_CUT将目前选择的文字从编辑控件中移走,并将其发送到剪贴簿中;WM_COPY将选择的文字复制到剪贴簿上并保持编辑控件中的内容完好无损; WM_CLEAR将选择的内容从编辑控件中删除,但是不向剪贴簿中发送。
您也可以将剪贴簿上的文字插入到编辑控件中的光标位置: SendMessage (hwndEdit, WM_PASTE, 0, 0) ;
将图标添加到程序

将资源添加到程序中需要Visual C++ Developer Studio的一些附加功能。对于图示来说,可以使用「Image Editor」(也称为「Graphics Editor」 )来绘制图标的图像。该图像被储存在扩展名为.ICO的图示文件中。Developer Studio还产生一个资源描述档 (扩展名为.RC的文件,有时也称作资源定义文件),它列出了程序的所有资源和一个让程序引用资源的表头文件(RESOURCE.H) 。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值