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 Basic与Qt基本上是不会要求应用程序直接拥有视窗程序的消息循环，但是会以键盘与鼠标的按键动作来作为事件的处理机制。在这些架构底下，消息循环的痕迹还是可以被找到的。
注意：在上述的源代码里，尤其在while循环大于零的条件。即使GetMessage函数的传回值类型是英文字大写的BOOL，但是在Win32视窗程序里，它是被定义成int整数类型，它有两个值，TRUE是整数的1，FALSE是整数的0。整数 -1代表error（例如第二个参数为输出的窗口句柄但取不到值的时候），整数0值当GetMessage获取到WM_QUIT消息。假如有其他消息，那么非零值会当成传回值（有消息的传回值通常是正值，但是有些程序设计的帮助文档不一定会说明的很详细）。

历史

16位Windows系统为非抢先单线程模式，应用程序没有发送消息队列，向窗口发送一个消息总是按同步方式执行，也即发送程序要在接受消息的窗口完全处理完消息之后才能继续运行。这通常是一个所期望的特性。但是，如果接收消息的窗口花很长的时间来处理消息或者出现挂起，则发送程序就不能再执行。这意味着系统是不强壮的。如果应用程序消息队列（只用于存放投寄的消息）为空，由于没有虚拟输入消息队列，SendMessage或PeekMessage函数访问系统事件队列查取可用的鼠标或键盘输入消息。如果系统队列中没有需要处理的事件，SendMessage或PeekMessage函数扫描所有窗口以处理需要修改重绘的区域。如果没有需要重绘的区域，则交出CPU控制权。恢复CPU控制权时，查看是否有定时器过期。至此如果没有消息可返回，SendMessage进入睡眠，直至被输入事件唤醒；PeekMessage如果没有设置PM_NOYIELD标记，则会让出CPU控制权，但不会让线程休眠，重新获得CPU后PeekMessage将控制权返回到线程，并返回一个空值指出这个线程没有要处理的消息了。
本文主要关注Win32系统的消息处理机制。

UI线程

Windows系统规定，窗口和钩子（hook）这两种User对象分别由创建窗口和安装钩子的线程所拥有，一旦该线程结束，操作系统会自动删除窗口或卸载钩子。而其他的User对象（图标icon、光标cursor、窗口类WndClass、菜单、加速键表等）则归进程所有，进程结束时操作系统会自动删除这些对象。
创建窗口的线程必须就是处理窗口所有消息的线程，即UI线程（User Interface Thread）创建了窗体及窗体上的各种控件，系统为UI线程分配一个消息队列用于窗口消息的派送（dispatch）。为了使窗口处置这些消息，线程必须有它自己的“消息循环”。只有当一个线程调用Windows API中的GDI（Graphics Device Interface）和User函数时，操作系统才会将其看成是一个UI线程，并为它分配一些另外的资源，创建一套线程消息队列；否则，操作系统把非UI线程视作普通工作线程（Workhorse），不会为它创建消息队列。因此，调用PostThreadMessage前，这个线程必须是UI线程从而有投寄消息的队列，通常可在该线程中调用一次PeekMessage函数以达到这个目的。
如果一个UI线程结束运行，操作系统会自动回收它所创建的所有窗体。

窗体过程

窗体过程（Window Procedure）是一个函数，每个窗体有一个窗体过程，负责处理该窗体的所有消息。
UI控件也是独立的“Window”，拥有自己的“窗体过程”。

GetMessage伪算法

BOOL GetMessage(MSG *lpMsg, HWND hWnd , UINT wMsgFilterMin, UINT wMsgFilterMax){
         //查看QS_SENDMESSAGE标志，如果有的话循环处理，直到没有消息位置
         DWORD dwRetVal = 0;
         ThreadInfo threadInfo;
 
FLAG_SENDPROCLOOP:
         GetThreadInfo(GetCurrentThreadId(), &threadInfo);
         while (threadInfo.QS_SENDMESSAGE == QS_SIGNALSET) {
                   //从发送消息队列中获取消息
                   dwReturnVal = GetMsgFromQueue(QUEUE_SEND, lpMsg, hWnd,wMsgFilterMin, wMsgFilterMax);
                   //判断是否取到消息,有则调用窗口函数，无则复为QS_SENDMESSAGE标志
                   If (dwReturnVal == GETMESSAGE_HASMESSAGE) {
                            //调用指定窗口的窗口函数
                            CallWindowProc(hWnd, &threadInfo, lpMsg);
                   }
                   else {
                            QS_SENDMESSAGE = QS_SIGNALRESET;
                            break;
                   }
         }
         //在继续处理之前再次检查发送消息队列
         if (threadInfo.QS_SENDMESSAGE == QS_SIGNALSET) goto FLAG_SENDPROCLOOP;
         //检查发送消息队列, 如果有消息则取发送消息
         //判断是否还有发送消息，没有了则复位QS_POSTMESSAGE标志
         if (threadInfo.QS_POSTMESSAGE == QS_SIGNALSET) {
                   dwReturnVal = GetMsgFromQueue(QUEUE_POST, lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
                   if (dwReturnVal == GETMESSAGE_LASTMESSAGE)
                            threadInfo.QS_POSTMESSAGE = QS_SIGNALRESET;
                  
                   return TRUE;
         }       
 
         //如果退出标志被置位
         if (threadInfo.QS_QUIT == QS_SIGNALSET) {
                   threadInfo.QS_QUIT = QS_SIGNALRESET;
                   FillMessage(lpMsg, MESSAGE_QUIT);
                   return FALSE;
         }
 
         //检查输入消息队列
         if (threadInfo.QS_INPUT == QS_SIGNALSET) {
                   DWORD dwRetVal = GetMessageFromQueue(QUEUE_INPUT, lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
                   //检查是否有键盘，鼠标消息
                   if (Test(dwRetVal, QS_KEY) == QS_LASTMOUSEKEYMESSAGE)
                            threadInfo.QS_KEY = QS_SIGNALRESET;
                   if (Test(dwRetVal, QS_MOUSEBUTTON) == QS_LASTMOUSEMESSAGE)
                            threadInfo.QS_MOUSEBUTTON = QS_SIGNALRESET;
 
                   return TRUE;
         }
 
         //测试QS_PAINT
         if (threadInfo.QS_PAINT == QS_SIGNALSET) {
                   //填充MSG，如果没有窗口过程确认窗口，则复位QS_PAINT标志
                   //...
                   //返回TRUE
                   threadInfo.QS_PAINT = QS_SIGNALRESET;
                   return TRUE;
         }
 
         if (threadInfo.QS_TIMER == QS_SIGNALSET) {
                   //填充MSG，如果没有定时器报时，则复位QS_TIMER标志
                   //...
                   //返回TRUE
                   return TRUE;
         }
 
         //等待有消息到达
         dwRetVal = MsgWaitForMultipleObjectsEx(...);
         if (...)
                   goto FLAG_SENDPROCLOOP;
 
         //等待失败
         return FALSE;}