Sometimes you *want* to interfere with the keyboard's state buffer

by Michael S. Kaplan, published on 2006/09/10 15:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2006/09/10/748775.aspx


Sebastian asked me via the Contact link:

Hi Michael,

I have come across your (very intersting) blog while searching for information on the WM_DEADCHAR message.  I am working on a relatively big/old application which receives deadkey messages but should really be treated as normal chars.  The input context can sometimes be viewed as a single-key input model.  In other words, the application catches all key strokes; sometimes the keys are hotkeys (e.g. when the user presses a key to start a command -- the key is associated to the command through a keybord map), sometimes keys come from edit text controls (e.g. when the user enters a string).  When using an international keyboard (w/ dead keys), the user might press a diacritic key ('[' for instance, which would be the grave accent key), thinking it is a hotkey, but in fact, Windows is merely sending a WM_DEADCHAR message and sets the state so that the next WM_CHAR message will most likely be modified or the WM_CHAR message will be sent out twice if the user presses the '[' key again.

I would like to know if we can cancel the current deadkey state once we receive the WM_DEADCHAR message.  I can always catch the deadchar message and treat it as a WM_CHAR message -- and ideally, I would like to receive the next (possibly a WM_CHAR) message as if no deadkey was pressed before, but Windows keeps an internal state which will still modify the next WM_CHAR I receive.

Is there anything I can do about this situation?  Doing the work when I receive WM_KEYDOWN messages is probably my solution but I can't modify the mechanism currently in place, the application code is huge and fragile.

Thank you!

Sebastian is correct that a buffer is kept when a dead key is entered that can impact the next keystroke. And he is also correct that the best place to handle this is in the WM_KEYDOWN notification is sent.

But the problem is that there is a huge difference between that one and both the WM_CHAR notification and the WM_DEADCHAR notification. Let's look at some of the subtle distinctions in the documentation of the various messages (we will focus on just four of them and stay away from the syschar type messages for the sake of simplicity):

WM_KEYDOWN

The WM_KEYDOWN message is posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when the ALT key is not pressed.

Windows 2000/XP: Applications must pass wParam to TranslateMessage without altering it at all.

WM_KEYUP

The WM_KEYUP message is posted to the window with the keyboard focus when a nonsystem key is released. A nonsystem key is a key that is pressed when the ALT key is not pressed, or a keyboard key that is pressed when a window has the keyboard focus.

Windows 2000/XP: Applications must pass wParam to TranslateMessage without altering it at all.

WM_CHAR

The WM_CHAR message is posted to the window with the keyboard focus when a WM_KEYDOWN message is translated by the TranslateMessage function. The WM_CHAR message contains the character code of the key that was pressed.

Because there is not necessarily a one-to-one correspondence between keys pressed and character messages generated, the information in the high-order word of the lParam parameter is generally not useful to applications. The information in the high-order word applies only to the most recent WM_KEYDOWN message that precedes the posting of the WM_CHAR message.

WM_DEADCHAR

The WM_DEADCHAR message is posted to the window with the keyboard focus when a WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR specifies a character code generated by a dead key. A dead key is a key that generates a character, such as the umlaut (double-dot), that is combined with another character to form a composite character. For example, the umlaut-O character (Ö) is generated by typing the dead key for the umlaut character, and then typing the O key.

The WM_DEADCHAR message typically is used by applications to give the user feedback about each key pressed. For example, an application can display the accent in the current character position without moving the caret.

Because there is not necessarily a one-to-one correspondence between keys pressed and character messages generated, the information in the high-order word of the lParam parameter is generally not useful to applications. The information in the high-order word applies only to the most recent WM_KEYDOWN message that precedes the posting of the WM_DEADCHAR message.

By the time you get the WM_*CHAR message, there is not much that is documented that you can do with the current buffer related to the dead key; it is basically contaminated. You really do need to be looking at the messages from which TranslateMessage is called, not the ones that are generated by TranslateMessage itself.

Of course that can also be hard -- look at how the need to check both WM_KEYDOWN and WM_KEYUP would exist (ignore the weird mention of WM_KEYDOWN in the WM_DEADCHAR docs; that looks like a bit of a copy/paste error).

So truly, an easier way to clear out that buffered information would be better, and not just for the application architecture concerns that Sebastian expressed. Truly!

Luckily, there is a way.

If you look in the blog archives back to January of 2005 and the post He's dead [keys], Jim, you'll see how Naushad was reporting an application bug where a WH_GETMESSAGE hook proc that used the ToUnicode function was causing dead keys to stop working.

I explained how ToUnicode and ToUnicodeEx can clear that buffer, the one Naushad did not want to clear.

Well, it turns out that Sebastian wants to clear that buffer, doesn't he? :-)

In this particular case, a return value from ToUnicode of any positive value is all you need here (it does not matter whether it is 1 or 2, since you are throwing out the results anyway). The only thing to look out for is if the return value is -1 (which can happen in the case of chained dead keys, discussed previously). The key here is to keep trying to pass stuff to ToUnicode until -1 is not returned.

And then one reader's bug becomes another reader's feature! :-)

 

This post brought to you by (U+0d1b, a.k.a. MALAYALAM LETTER CHA)


no comments

referenced by

2011/04/16 Chain Chain Chain, Chain of Dead Keys

2008/04/22 Premature keystroke processing? Don't worry, it happens to everyone....

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