Microsoft Windows的消息循环

https://zh.wikipedia.org/wiki/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])。

 

 

https://en.wikipedia.org/wiki/Message_loop_in_Microsoft_Windows

The message loop is an obligatory section of code in every program that uses a graphical user interface under Microsoft Windows.

Windows programs that have GUI are event-driven. Windows maintains an individual message queue for each thread that has created a window. Usually only the first thread creates windows. Windows places messages into that queue whenever mouse activity occurs on that thread's window, whenever keyboard activity occurs while that window has focus, and at other times. A process can also add messages to its own queue. To accept user input, and for other reasons, each thread with a window must continuously retrieve messages from its queue, and act on them. A programmer makes the process do that by writing a loop that calls GetMessage (which blocks for a message and retrieves it), and then calls DispatchMessage (which dispatches the message), and repeats indefinitely. This is the message loop. There usually is a message loop in the main program, which runs on the main thread, and additional message loop in each created modal dialog. Messages for every window of the process pass through its message queue, and are handled by its message loop. A message loop is one kind of event loop.

A basic message loop appears as follows:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;

    while(1)
    {
        bRet = GetMessage(&msg, NULL, 0, 0);

        if (bRet > 0)  // (bRet > 0 indicates a message that must be processed.)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else if (bRet < 0)  // (bRet == -1 indicates an error.)
        {
            // Handle or log the error; possibly exit.
            // ...
        }
        else  // (bRet == 0 indicates "exit program".)
        {
            break;
        }
    }
    return msg.wParam;
}

It is conventional for the event loop to call TranslateMessage on each message which can translate virtual keystrokes into strings. Calling TranslateMessage is not technically required, but problems can result if it is not called. The message loop must call DispatchMessage.

The message loop does not directly act on the messages that it handles. It dispatches them by calling DispatchMessage, which transfers the message to the "window procedure" for the window that the message was addressed to. (The "window procedure" is a callback procedure, which got associated with the window class when it was registered.) (More than one window can use the same window procedure.)

Code can also send messages directly to a window procedure. These are called nonqueued messages.

A strict message loop is not the only option. Code elsewhere in the program can also accept and dispatch messages. PeekMessage is a non-blocking call that returns immediately, with a message if any are waiting, or no message if none is waiting. WaitMessage allows a thread to sleep until a message is in the queue.

Modern graphical interface frameworks, such as Windows FormsWindows Presentation FoundationMFCDelphiQt, and others do not require applications to code a Windows message loop, because they automatically route events such as key presses and mouse clicks to their appropriate handlers as defined within the framework. However, each framework implements a message loop somewhere, and the message loop can usually be accessed or replaced when more direct control is required.

 

 https://zh.wikipedia.org/wiki/事件环

在计算机领域,事件环,或者被称为消息分发器,消息环,消息泵或者运行环这些定义不过是一个程序结构体,用以在程序中等待,分发事件或者消息。它的工作方式是向内部或者外部的“事件提供方”发出请求(通常采取封锁请求的方式,直到有事件发生),然后再呼叫相应的事件处理器(又称“事件的分发“)。 事件环通常于编程设计模式” 反应器模式“相结合,前提是事件提供方遵循相同的文件接口, 这样事件提供方就可以被选择, '被轮询' (Unix系统这样用被动方式称呼,现在也可以直接叫 轮询). 事件环几乎总是对消息发出方进行异步操作。

当一个事件流被用作程序的中心控制流程, 事实上它通常做这个用途, 这时它又可以被称为”主环“或者”主事件环“。本文标题称为事件环贴切一点,因为这样的事件环一直是处在程序的最上的控制层面的。

https://en.wikipedia.org/wiki/Event_loop

In computer science, the event loop is a programming construct or design pattern that waits for and dispatches events or messages in a program. The event loop works by making a request to some internal or external "event provider" (that generally blocks the request until an event has arrived), then calls the relevant event handler ("dispatches the event"). The event loop is also sometimes referred to as the message dispatchermessage loopmessage pump, or run loop.

The event-loop may be used in conjunction with a reactor, if the event provider follows the file interface, which can be selected or 'polled' (the Unix system call, not actual polling). The event loop almost always operates asynchronously with the message originator.

When the event loop forms the central control flow construct of a program, as it often does, it may be termed the main loop or main event loop. This title is appropriate, because such an event loop is at the highest level of control within the program.

Message passing[edit]

Message pumps are said to 'pump' messages from the program's message queue (assigned and usually owned by the underlying operating system) into the program for processing. In the strictest sense, an event loop is one of the methods for implementing inter-process communication. In fact, message processing exists in many systems, including a kernel-level component of the Mach operating system. The event loop is a specific implementation technique of systems that use message passing.

Alternative designs[edit]

This approach is in contrast to a number of other alternatives:

  • Traditionally, a program simply ran once, then terminated. This type of program was very common in the early days of computing, and lacked any form of user interactivity. This is still used frequently, particularly in the form of command-line-driven programs. Any parameters are set up in advance and passed in one go when the program starts.
  • Menu-driven designs. These still may feature a main loop, but are not usually thought of as event driven in the usual sense[citation needed]. Instead, the user is presented with an ever-narrowing set of options until the task they wish to carry out is the only option available. Limited interactivity through the menus is available.

Usage[edit]

Due to the predominance of graphical user interfaces, most modern applications feature a main loop. The get_next_message() routine is typically provided by the operating system, and blocks until a message is available. Thus, the loop is only entered when there is something to process.

function main
    initialize()
    while message != quit
        message := get_next_message()
        process_message(message)
    end while
end function

File interface[edit]

Under Unix, the "everything is a file" paradigm naturally leads to a file-based event loop. Reading from and writing to files, inter-process communication, network communication, and device control are all achieved using file I/O, with the target identified by a file descriptor. The select and poll system calls allow a set of file descriptors to be monitored for a change of state, e.g. when data becomes available to be read.

For example, consider a program that reads from a continuously updated file and displays its contents in the X Window System, which communicates with clients over a socket (either Unix domain or Berkeley):

main():
    file_fd = open ("logfile")
    x_fd = open_display ()
    construct_interface ()
    while changed_fds = select ({file_fd, x_fd}):
        if file_fd in changed_fds:
            data = read_from (file_fd)
            append_to_display (data)
            send_repaint_message ()
        if x_fd in changed_fds:
            process_x_messages ()

Handling signals[edit]

One of the few things in Unix that does not conform to the file interface are asynchronous events (signals). Signals are received in signal handlers, small, limited pieces of code that run while the rest of the task is suspended; if a signal is received and handled while the task is blocking in select(), select will return early with EINTR; if a signal is received while the task is CPU bound, the task will be suspended between instructions until the signal handler returns.

Thus an obvious way to handle signals is for signal handlers to set a global flag and have the event loop check for the flag immediately before and after the select() call; if it is set, handle the signal in the same manner as with events on file descriptors. Unfortunately, this gives rise to a race condition: if a signal arrives immediately between checking the flag and calling select(), it will not be handled until select() returns for some other reason (for example, being interrupted by a frustrated user).

The solution arrived at by POSIX is the pselect() call, which is similar to select() but takes an additional sigmask parameter, which describes a signal mask. This allows an application to mask signals in the main task, then remove the mask for the duration of the select() call such that signal handlers are only called while the application is I/O bound. However, implementations of pselect() have only recently[when?] become reliable; versions of Linux prior to 2.6.16 do not have a pselect() system call, forcing glibc to emulate it via a method prone to the very same race condition pselect() is intended to avoid.

An alternative, more portable solution, is to convert asynchronous events to file-based events using the self-pipe trick,[1] where "a signal handler writes a byte to a pipe whose other end is monitored by select() in the main program".[2] In Linux kernel version 2.6.22, a new system call signalfd() was added, which allows receiving signals via a special file descriptor.

 

 

 

 

 

posted @ 2020-01-11 18:14  papering  阅读(613)  评论(0编辑  收藏  举报