Microsoft Windows的訊息迴圈

维基百科,自由的百科全书
跳转至: 导航搜索

微軟視窗操作系统是以事件驅動做為程式設計的基礎。程式的執行緒会从作業系統获取訊息。應用程式會不斷循环呼叫GetMessage函式(或是PeekMessage函式)來接收這些訊息,這個循环稱之為“事件迴圈”。基本上事件迴圈的程式碼如下所示(C語言 / C++程式語言):

MSG msg; //用于存储一条消息
BOOL bRet;
 
//从UI线程消息队列中取出一条消息
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
   if (bRet == -1)
   {
       //错误处理代码,通常是直接退出程序
    }
    else
    {
       TranslateMessage(&msg); //转换消息格式
       DispatchMessage(&msg); //分发消息给相应的窗体
     }
}

雖然在程序上並沒有很嚴格的規定與要求,但是一般來說,它的事件迴圈通常會呼叫TranslateMessage函式與DispatchMessage函式,這兩個函式會傳遞訊息給回呼函式,以及调用相应視窗的消息处理函数。

現在的繪圖介面架構程式設計,例如Visual BasicQt基本上是不會要求應用程式直接拥有視窗程式的訊息迴圈,但是會以鍵盤與滑鼠的按鍵動作來作為事件的處理機制。在這些架構底下,訊息迴圈還的痕迹是可以被找到的。

注意:在上述的原始碼裡,尤其在while迴圈大於零的條件。即使GetMessage函式的傳回值型態是英文字大寫的BOOL,但是在Win32視窗程式裡,它是被定義成int整數型態,它有兩個值,TRUE是整數的1,FALSE是整數的0。整數 -1代表error,整數0值当GetMessage获取到WM_QUIT訊息。假如有其他訊息,那麼非零值會當成傳回值(有訊息的傳回值通常是正值,但是有些程式設計的說明文件不一定會說明的很詳細[1][2])。

背景[编辑]

UI线程[编辑]

UI线程(User Interface Thread)的线程函数中创建了窗体及窗体上的各种控件。只有UI线程具有“消息循环”的代码。只有当一个线程调用Win32 API中的GDI(Graphics Device Interface)和User函数时,操作系统才会将其看成是一个UI线程,并为它创建一个消息队列。如果一个UI线程结束运行,操作系统会自动回收它所创建的所有窗体。

非UI线程,也可称作“工作线程”,不具有“消息循环”的代码。

窗体过程[编辑]

窗体过程(Window Procedure)是一个函数,每个窗体有一个窗体过程,负责处理该窗体的所有消息。

UI控件也被视为一个“Window”,拥有自己的“窗体过程”。

消息队列[编辑]

Windows操作系统的系统空间中有一个系统消息队列(system message queue),以及多个UI线程消息队列(Thread message queue)。每个正在运行的UI线程在系统空间中有各自的消息队列。在发生输入事件之后,Windows将事件转换为一个「消息」投寄到系统消息队列;操作系统的一个专门线程从系统消息队列取出消息,分发到各个UI线程的消息队列中。

应用程序中有一段称之为“消息循环”的代码,通过GetMessage系统调用(或是PeekMessage系统调用)访问系统空间中的对应的UI线程消息队列,然后通过DispatchMessage系统调用把消息分发给相应窗口的消息处理函数。

由此可见,Windows的事件驱动模式,并不是操作系统把消息主动分发给应用程序;而是由应用程序通过“消息循环”代码从UI线程消息队列获取消息。

需要注意的是,GetMessage如果在应用程序消息队列未获取消息,则GetMessage调用不返回,该线程挂起,CPU使用权交给操作系统。即GetMessage为阻塞调用。

队列化消息与非队列化消息[编辑]

消息可分为两类:

  • 队列化消息:由操作系统把消息加入到应用程序消息队列。一般是用户输入事件形成的消息,如击键(WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标(WM_MOUSEMOVE、WM_LBUTTONDOWN等),...。队列化消息还包括时钟消息(WM_TIMER)、绘制消息(WM_PAINT)和退出消息(WM_QUIT)等。
  • 非队列化消息:操作系统直接调用相应窗口的消息处理函数,把该消息处理掉。非队列化消息可能来自呼叫特定的Windows系统调用,如:CreateWindow产生的WM_CREATE、ShowWindow产生的WM_SIZE和WM_SHOWWINDOW、UpdateWindow产生的WM_PAINT。还有WM_COMMAND等。

PostMessage系统调用向当前应用程序消息队列中加入一条消息,直接调用返回。SendMessage系统调用则直接由操作系统调用相应窗口的消息处理函数,把作为参数的消息处理掉,之后才调用返回。

參考資料[编辑]

相關條目[编辑]

外部連結[编辑]