http://winprog.org/tutorial/zh/message_loop_cn.html
好像很多人都不知道这个经典的教程,转载其中一章打打广告。
theForger's Win32 API教程第二版(简体中文)
基础创建一个简单应用图形设备接口工具与文档附表 |
理解消息循环在写任何有实际价值的程序之前,都要理解整个消息循环和windows程序传递消息的结构.当前为止我们已经试了一些消息的处理,我还应该更进一步的看一下这个过程, 因为你如果不理解它们的话,后面的內容将会对你很难. 什么是消息?一个消息就是一个整数.如果你到头文件中看一下(了解API的好地方)你就会发现这样的內容: #define WM_INITDIALOG 0x0110
#define WM_COMMAND 0x0111
#define WM_LBUTTONDOWN 0x0201
... 等等..消息用来进行windows系统中的所有通信,至少在基本方面是这样的.如果你想要你的窗口或是控件(其实就一种特別的窗口)做点什么你就给它发个消息. 如果另外一个窗口要你做点什么它也发给你一个消息..如果发生了用戶按动了键盘,移动了鼠标,点击了一个按钮之类的事件,系统就向相关的窗口发送消息.你要是就是那个被传至消息的窗口,你就要处理这些消息. 每个windows消息可能拥有至多两个参数,wParam和lParam.最初wParam是16bit,lParam是32bit,但在Win32平台下两者都是32bit.不是每个消息使用了这些参数,而且每个消息以不同的方式来使用.比如,WM_CLOSE不使用它们任一个,所以你应该忽略它们.WM_COMMAND消息两个都使用,wParam有两个部分,HIWORD(wParam)中含有提示消息(如果有的话),LOWORD(wParam)含有发送消息的控件或菜单的标识号.lParam含有发送消息的控件的HWND(窗口的句柄)或者为NULL,当消息不是由控件发送. HIWORD()和LOWORD()是windows定义的两个宏,用来从一个32bit值中分离出高字(0xffff0000)和低字(0x0000ffff)的两字节.在Win32中,一个WORD是16bit,所以一个DWORD为32bit. 可以用PostMessage()或SendMessage()来发送消息.PostMessage()把消息放入消息队列再立即返回.就是说你调用了PostMessage()后消息可能被处理了,也可能还沒有被处理. SendMessage()则真接把消息送往窗口并且在窗口沒有结束处理消息之前不返回.如果我们想关闭一个窗口我们可以发送一个WM_CLOSE消息:PostMessage(hwnd,WM_CLOSE,0,0);这跟我们点击窗口顶部的 按钮一样的效果.注意wParam和lParam都为0.这是因为我们刚才说了WM_CLOSE并不用它们. 对话框一旦你开始使用对话框,你将需要向此控件发送消息以与它们通信.你可以先使用GetDlgItem()来用ID得到控件的句柄然后用SendMessage(),也可以直接用SendDlgItemMessage()这个一步到位的函数.你给它一个窗口的句柄和一个子窗口的ID,它就会得到字窗口的句柄并向它发送消息.SendDlgItemMessage()和类似的API,如GetDlgItemText()可以用在所有的窗口上面,而不是仅仅在对话框上. 什么是消息队列打个比方,你正在处理WM_PAINT消息,此时突然用戶在键盘上敲了一大堆的东西.这时候会怎样?你应该中止你的工作去响应键盘的输入,还是简单地把这些输入给忽略掉? 错!显然两种方式都不能接受,所以我们引入了消息队列的概念,消息被发送时就被放入队列,被处理后就从队列中删除.这就保证了你不会丟掉消息,你在处理某个的时候,另外的就在队列中等待你来取走它们. 什么是消息循环while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
这是windows程序中一个非常重要的概念..你的窗口过程并不是由系统来神奇地调用的,实际上你在DispatchMessage()中自己间接地调用了它.如果你愿意,你可以对消息调用GetWindowLong()得到它的窗口过程再直接调用它! while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
我对前面的例子用了这种方法,可以工作,但是有很多的方面这种方法沒有注意到,比如Unicode/ANSI转換,时钟回调函数等等,它很可能在我们的简单试验中可以工作.所以试下就可以了,不要在实际的代码中用它:) 注意我们用GetWindowLong()来从窗口来查找它的窗口过程.为什么我们不直接调用WndProc()?因为我们的消息循环为我们程序中的所有窗口的消息服务,包括按钮,列表框这样拥有自己的窗口过程的窗口,所以我们要我们调用了正确的窗口过程.因为多个窗口可能使用一个窗口过程, 第一个参数(窗口的句柄)告诉窗口过程某个消息是为那个窗口的. 你可以看到,你的应用程序的主要时间就是在消息循环这里打转,你很高兴地向那些快乐的窗口发消息让他们处理.如果你要退出程序你怎么办?因为我们使用了一个while循环,如果GetMessage()返回了FLASE(就是0),循环就结束我们就可以到WinMain()的结束点.这就是PostQuitMessage()做的工作.它向队列发送一个WM_QUIT消息,GetMessage()就向Msg结构体填充数据并返回0,而不返回正的值. 这个地方,Msg的wParam成员含有你传向PostQuitMessage()的数据,你可以忽略它,也可以从WinMain()返回当这个进程的结束码. 要点:GetMessage()遇到错误后返回-1.你要记住这点,说不定哪次你在这里会犯错...即使GetMessage()被定义为返回一个BOOL值,它还是会返回TRUE和FLASE之外的值,因为BOOL被定义为UINT(unsigned int).下面的代码可能看起来可工作,但是不能正确处理某些情況: while(GetMessage(&Msg, NULL, 0, 0))
while(GetMessage(&Msg, NULL, 0, 0) != 0)
while(GetMessage(&Msg, NULL, 0, 0) == TRUE)
上面的写法都是错误的!可能你注意到我在这个教程中使用了第一个,刚刚提到了,如果GetMessage()不失败,并不会出错,你代码要是正确的话是不会出错.但是你要是在读本教程的话我并不能以此做为前提,你的代码可能有很多错误,GetMessage()会在某些点出错:)这种写法我已经更正了,如果我漏了某些地方请原谅. while(GetMessage(&Msg, NULL, 0, 0) > 0)
应该始终使用这段拥有相同的效果的代码. 我希望你对windows消息循环有了进一步的了解,如果沒有,不用怕,你使用它们一些时间后就会更清楚了. |
Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.