简体   繁体   中英

Understanding different strategies for message handling in the Windows API

I am working on a home project to play with DirectX and WINAPI. I just found an interesting behavior.

In the code below, if I use my wndd->h_ (type: HWND), what was the result of CreateWindowEx() , in the PeekMessage() as second parameter, then the main loop starts to working on a single core with heavier load, while leaving the second parameter as 0, then the calculation spreads out well.

void WinLoop() {
    while (!wndd->exit_) {
      //if (PeekMessage(&(wndd->msg_), 0, 0, 0, PM_REMOVE)) {        //NOTE : Frequently changing what core is loaded
        if (PeekMessage(&(wndd->msg_), wndd->h_, 0, 0, PM_REMOVE)) { //NOTE : Heavy load on one core
            TranslateMessage(&(wndd->msg_));
            DispatchMessage(&(wndd->msg_));
        }

        //TODO : update
        //TODO : draw
        //TODO : calculate statistic
    }
}

Everything seems to work just fine, but I can not find any information about this. What is under the hood?

According to the documentation for the PeekMessage function, the second parameter is…

hWnd [in, optional]

Type: HWND

A handle to the window whose messages are to be retrieved. The window must belong to the current thread.

If hWnd is NULL , PeekMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL , both window messages and thread messages are processed.

If hWnd is -1, PeekMessage retrieves only messages on the current thread's message queue whose hwnd value is NULL , that is, thread messages as posted by PostMessage (when the hWnd parameter is NULL ) or PostThreadMessage .

It is also important to pay attention to what the PeekMessage function does . In short, it retrieves the first message (matching the specified criteria, if applicable) that exists in the queue. Because you've specified the PM_REMOVE flag, it also removes that message from the queue.

More interesting is what happens if there are no messages for you in the queue. In that case, PeekMessage immediately returns false.

You now have enough information to understand the difference in behavior between the two function calls. In the first case, you're passing NULL as the second parameter, so the PeekMessage function is retrieving messages for any window that belongs to the current thread, as well as thread messages. In the second case, you're passing a handle to a specific window as the second parameter, so the PeekMessage function is retrieving messages only for that window. Because there are a lot fewer messages to retrieve for that one window, as compared to all windows and threads, the PeekMessage function is mostly just returning false. Little to no time is spent processing messages. And as soon as it returns false, your while loop kicks in, looping back to the beginning and calling the PeekMessage function again.

Basically, you've created a tight loop that just sits and burns CPU time, polling the PeekMessage function continuously without doing anything, which interferes with the operating system's execution scheduler. The app can still get pre-empted, of course, but you are guaranteed to use 100% of your allocated time-slice.

There is a purpose for this behavior, like when writing a game. A PeekMessage loop is also commonly used in applications that are doing lengthy background processing, but want to continue pumping messages to keep the UI responsive. But there is no reason to use PeekMessage in a normal application's message loop where you don't need real-time processing. Instead, you want the GetMessage function, which actually waits until it receives a message before returning. This is far more efficient in terms of CPU time because it isn't constantly polling. You get the time you need, but no more. It is also much more battery-friendly .

I assume that, if you're playing with DirectX, you probably are trying to write a game or screensaver or some other quasi-real-time thing, which is why you're using the PeekMessage function in the first place. But you will probably want to pass NULL as the function's second parameter, because you want to process all window and thread messages, not just the ones for your main window.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM