windows 消息与消息队列的使用

参考Using Messages and Message Queues - Win32 apps | Microsoft Learn

creating message loop

系统不会自动为每个线程都创建消息队列,而是只是为需要消息队列的线程创建。如果一个线程创建了一个或多个窗口,则线程必须要有message loop,此message loop 从各线程的消息队列中获取消息并将消息分配正确地window procedure。

HINSTANCE hinst; 
HWND hwndMain; 
 
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpszCmdLine, int nCmdShow) 
{ 
    MSG msg;
    BOOL bRet; 
    WNDCLASS wc; 
    UNREFERENCED_PARAMETER(lpszCmdLine); 
 
    // Register the window class for the main window. 
 
    if (!hPrevInstance) 
    { 
        wc.style = 0; 
        wc.lpfnWndProc = (WNDPROC) WndProc; 
        wc.cbClsExtra = 0; 
        wc.cbWndExtra = 0; 
        wc.hInstance = hInstance; 
        wc.hIcon = LoadIcon((HINSTANCE) NULL, 
            IDI_APPLICATION); 
        wc.hCursor = LoadCursor((HINSTANCE) NULL, 
            IDC_ARROW); 
        wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
        wc.lpszMenuName =  "MainMenu"; 
        wc.lpszClassName = "MainWndClass"; 
 
        if (!RegisterClass(&wc)) 
            return FALSE; 
    } 
 
    hinst = hInstance;  // save instance handle 
 
    // Create the main window. 
 
    hwndMain = CreateWindow("MainWndClass", "Sample", 
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 
        CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, 
        (HMENU) NULL, hinst, (LPVOID) NULL); 
 
    // If the main window cannot be created, terminate 
    // the application. 
 
    if (!hwndMain) 
        return FALSE; 
 
    // Show the window and paint its contents. 
 
    ShowWindow(hwndMain, nCmdShow); 
    UpdateWindow(hwndMain); 
 
    // Start the message loop. 
 
    while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
    { 
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    } 
 
    // Return the exit code to the system. 
 
    return msg.wParam; 
}

Examining a message queue

应用程序偶尔需要在message loop 外查看message queue中内容。例如,一个应用程序的window procedure正在执行一个耗时的绘图任务,你希望用户能打断此任务。除非应用程序在此绘图任务执行期间周期性的检测message queue 中的鼠标、键盘相关message,否则,在此绘图任务期间,应用是不会对键盘、鼠标进行响应。原因是,message loop中DispatchMessage函数在window procedure返回之前是不会返回的。
应用可以在一个耗时的操作执行期间使用PeekMessage来检测消息队列的情况。PeekMessage与GetMessage的功能类似,都是检测消息队列中的消息是否符合filter设置的条件,如果有就将消息复制到MSG结构。两者的最大不同是,只有在消息队列中找到符合条件的消息,GetMessage才会返回,而PeekMessage不管消息队列中有没有符合条件的消息就直接返回。

HWND hwnd; 
BOOL fDone; 
MSG msg; 
 
// Begin the operation and continue until it is complete 
// or until the user clicks the mouse or presses a key. 
 
fDone = FALSE; 
while (!fDone) 
{ 
    fDone = DoLengthyOperation(); // application-defined function 
 
    // Remove any messages that may be in the queue. If the 
    // queue contains any mouse or keyboard 
    // messages, end the operation. 
 
    while (PeekMessage(&msg, hwnd,  0, 0, PM_REMOVE)) 
    { 
        switch(msg.message) 
        { 
            case WM_LBUTTONDOWN: 
            case WM_RBUTTONDOWN: 
            case WM_KEYDOWN: 
                // 
                // Perform any required cleanup. 
                // 
                fDone = TRUE; 
        } 
    } 
}

还有其他函数能够对消息队列中的内容进行检测。例如GetQueueState、GetInputState。GetQueueState返回一个flag数组,这些flag用以说明消息队列中消息的类型。当消息队列中有鼠标、键盘消息,则GetInputState会返回TRUE。

Posting a Message

PostMessage函数将消息放入线程的消息队列的队尾后便立即返回,而不等待线程对消息做处理。此函数的参数包括 窗口handle, 消息id和两个消息参数。 系统将这几个参数值拷贝到MSG结构里,然后填充time和位置信息消息,之后将此结构放入消息队列。应用 可以调用PostThreadMessage函数来将消息post到特定的线程消息队列中。

sending a messsage

SendMessage 用于将消息直接发送到window procedure, 它会调用一个 window procedure,并等待window procedure 处理完消息并返回。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容