揭开.NET消息循环的神秘面纱
发布时间:2006-03-20 11:20:50 来源:程序员杂志 网友评论 0 条 曾经在Win32平台下奋战的程序员们想必记得,为了弄清楚“消息循环”的概念,度过多少不眠之夜。尽管如今在应用程序代码的编写过程中,我们已经不再需要它,但是深刻理解Windows平台内部的消息流转机制依然必要..
在早年直接用Win32/Win16 API写程序的时代,消息循环是我们必须搞懂的第一个观念。现在,不管你用是Windows上面的哪一套Application Framework(MFC、VCL、VB、.NET Framework),甚至Unix、Linux、MacOSX上面的Application Framework,都不太容易看到消息循环。事实上,消息循环依然存在,只是被这些ApplicationFramework包装起来,深深地埋藏在某个角落。
本文章试图唤起大家对于消息循环的回忆,也试图解释消息循环如何被封装进.NET Framework的Windows Forms中。虽然Windows Forms将这一切都藏起来,但是也留下许多空间,让我们可以自行处理Win32的消息。
传统的Windows 程序
传统的Windows程序,只利用Win32 API撰写,下面是一个程序范例,为了节省篇幅,我将其中许多程序代码省略:
1、从_tWinMain内,程序进入主消息循环;
2、消息循环从消息队列(Message Queue)中取得一个消息(透过调用GetMessage())。每个执行中的程序都有一个属于自己的消息队列;
3、消息循环根据消息内容来决定消息应该送给哪个Windows Procedure(WndProc),.. 这就称为消息分发(Message Dispatch)。通常“每一种”窗口或控件(control)都有一个Windows Procedure,来处理该种窗口/控件的行为;
4、Windows Procedure根据消息内容来决定应该调用哪个函数(利用Switch/Case语法);..
5、Windows Procedure处理完,控制权回到消息循环。继续进行2、3、4、5的动作;
6、当消息队列为空的时候,GetMessage()无法取得任何消息,就会进入Idle(空闲)状态,进入睡眠状态(而不是Busy Waiting)。当消息队列不再为空的时候,程序会自动醒过来,继续进行2、3、4、5的动作;
7、当取得的消息是WM_QUIT,GetMessage()就会得到0的返回值,因而离开消息循环,程序结束。程序会利用调用PostQuitMessage()来将WM_QUIT放置进消息队列中,来造成稍后结束,而不会直接贸然跳离开循环来结束。
虽名为队列(queue),.. 但是消息队列中的消息并非总是先进先出(First In First Out,FIFO),有一些特例:
. 只要消息队列中有WM_QUIT ,就会先取出WM_QUIT,导致程序结束。
. 只有在没有其它消息的时候,WM_PAINT 和WM_TIMER才会被取出。且多个WM_PAINT可能会被合并成一个,WM_TIMER也是如此。
. 利用TranslateMessage()来处理消息,可能会造成新消息的产生。例如:TranslateMessage()可以辨识出WM_KEYDOWN(按键按下)加上WM_KEYUP(按放开)就产生WM_CHAR(字符输入)。
在早年直接用Win32/Win16 API写程序的时代,消息循环是我们必须搞懂的第一个观念。现在,不管你用是Windows上面的哪一套Application Framework(MFC、VCL、VB、.NET Framework),甚至Unix、Linux、MacOSX上面的Application Framework,都不太容易看到消息循环。事实上,消息循环依然存在,只是被这些ApplicationFramework包装起来,深深地埋藏在某个角落。
本文章试图唤起大家对于消息循环的回忆,也试图解释消息循环如何被封装进.NET Framework的Windows Forms中。虽然Windows Forms将这一切都藏起来,但是也留下许多空间,让我们可以自行处理Win32的消息。
传统的Windows 程序
传统的Windows程序,只利用Win32 API撰写,下面是一个程序范例,为了节省篇幅,我将其中许多程序代码省略:
| // 程序进入点 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ MSG msg; if (!InitInstance (hInstance, nCmdShow)){ return FALSE; } // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } // 函数: WndProc(HWND, unsigned, WORD, LONG) // 用途: 处理主窗口的消息。 LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message){ case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 剖析菜单选取项目: switch (wmId){ case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message,wParam,lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此加入任何绘图程序代码... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message,wParam, lParam); } return 0; } // [关于] 方块的消息处理例程。 LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){ switch (message){ case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL){ EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } |
1、从_tWinMain内,程序进入主消息循环;
2、消息循环从消息队列(Message Queue)中取得一个消息(透过调用GetMessage())。每个执行中的程序都有一个属于自己的消息队列;
3、消息循环根据消息内容来决定消息应该送给哪个Windows Procedure(WndProc),.. 这就称为消息分发(Message Dispatch)。通常“每一种”窗口或控件(control)都有一个Windows Procedure,来处理该种窗口/控件的行为;
4、Windows Procedure根据消息内容来决定应该调用哪个函数(利用Switch/Case语法);..
5、Windows Procedure处理完,控制权回到消息循环。继续进行2、3、4、5的动作;
6、当消息队列为空的时候,GetMessage()无法取得任何消息,就会进入Idle(空闲)状态,进入睡眠状态(而不是Busy Waiting)。当消息队列不再为空的时候,程序会自动醒过来,继续进行2、3、4、5的动作;
7、当取得的消息是WM_QUIT,GetMessage()就会得到0的返回值,因而离开消息循环,程序结束。程序会利用调用PostQuitMessage()来将WM_QUIT放置进消息队列中,来造成稍后结束,而不会直接贸然跳离开循环来结束。
虽名为队列(queue),.. 但是消息队列中的消息并非总是先进先出(First In First Out,FIFO),有一些特例:
. 只要消息队列中有WM_QUIT ,就会先取出WM_QUIT,导致程序结束。
. 只有在没有其它消息的时候,WM_PAINT 和WM_TIMER才会被取出。且多个WM_PAINT可能会被合并成一个,WM_TIMER也是如此。
. 利用TranslateMessage()来处理消息,可能会造成新消息的产生。例如:TranslateMessage()可以辨识出WM_KEYDOWN(按键按下)加上WM_KEYUP(按放开)就产生WM_CHAR(字符输入)。
- 推荐阅讯
- ASP.NET2.0中Gridview中数据操作技巧
- ASP.NET数据库编程之Access连接失败
- ASP.NET2.0数据操作之创建数据访问层
- ASP.NET2.0数据操作之创建业务逻辑层
- ASP.NET 2.0服务器控件开发之简单属性
- 用ASP.NET 2.0设计网络在线投票系统
- ASP.NET 2.0的视图状态持久性机制
- ASP.NET 2.0移动开发入门之使用样式
- 10天学会ASP.net之第七天
- 开发ASP.NET下的MP3小偷程序
- 阅读排行
- 1.用ASP.NET 2.0设计网络在线投票系统
- 2.在ASP.Net 2.0中实现多语言界面的方法
- 3.轻松加密ASP.NET 2.0 Web程序配置信息
- 4.在ASP.NET中使用AJAX的简单方法
- 5..NET 2.0中的企业库异常处理块简述
- 6.面向.NET开发人员的Ajax 技术平台策略
- 7.揭开ASP.NET中Cookie编程的奥秘
- 8.ASP.NET2.0服务器控件之创建自定义控件
- 9.ASP.NET2.0中Gridview中数据操作技巧
- 10.ASP.NET 2.0发送电子邮件全面剖析之二
- 专题教程
- Windows Server-Windows Server文档-Windows Server新闻-Windows Ser PostgreSQL-PostgreSQL文档-PostgreSQL新闻-PostgreSQL专家
- WebLogic-WebLogic文档-WebLogic新闻-WebLogic专家 FreeBSD-FreeBSD文档-FreeBSD新闻-FreeBSD专家
- Linux-内核 GUI KDE Gnome DNS FTP 安全 安装-Linux专区 Windows-AD IIS ServerCore 虚拟化 安全 HPC-Windows专区
- 大话G游 专题:手机病毒揭密
- ARP攻击防范与解决方案 路由故障处理手册
