This is a continuation from What is the correct, modern way to handle arbitrary text input in a custom control on Windows? WM_CHAR? IMM? TSF?.

So after experimenting with a non-IME layout (US English), a non-TSF IME (the Japanese FAKEIME from the Windows XP DDK), and a TSF text service (anything that comes with Windows 7), it appears that if the active input processor profile is not a TSF text service (that is, it is a TF_PROFILETYPE_KEYBOARDLAYOUT), I'll still have to handle keystrokes and WM_CHAR messages to do text input.

My problem is that my architecture needs a way to be told that it can ignore the current key message because it was translated into a text input message. It does not care whether this happens before or after the translation; it just needs to know that such a translation will or has happened. Or in pseudocode terms:

// if I can suppress WM_CHAR generation and synthesize it myself (including if the translation is just dead keys)
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
    if (WillTranslateMessage())
        InsertChar(GenerateEquivalentChar());
    else
        HandleRawKeyEvent();
    break;

// if I can know if a WM_CHAR was generated (or will be generated; for instance, in the case of dead keys)
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
    if (!DidTranslateMessage())
        HandleRawKeyEvent();
    break;
case WM_CHAR:
case WM_SYSCHAR:
    InsertChar(wParam);
    break;

The standard way of handling text input, either from a keyboard or through a non-TSF IME, is to let TranslateMessage() do the WM_KEYDOWN-to-WM_CHAR translation. However, there's a problem: MSDN says

If the message is WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP, the return value is nonzero, regardless of the translation.

which means that I cannot use it to determine if a translation has occurred.

After reading some Michael Kaplan blog posts, I figured I could use ToUnicode() or ToUnicodeEx() to do the conversion myself, passing in the state array from GetKeyboardState(). The wine source code seems to agree, but it has two special cases that I'm not sure if they are wine-specific or need to be done on real Windows as well:

  • VK_PACKET — generates a WM_CHAR directly out of the message's LPARAM
  • VK_PROCESS — calls a function ImmTranslateMessage(), which seems to either be a wine-specific function or an undocumented imm32.dll function; I can't tell which is true

And wine also does nothing with WM_KEYUP and WM_SYSKEYUP; again, I don't know if this is true for wine only.

But do I even need to worry about these cases in a program that uses TSF? And if I do, what's the "official" way to do so? And even then, what would I do on WM_KEYUP/WM_SYSKEYUP; do I need to send those to ToUnicode() too? Do I even need to catch WM_KEYUPs in my windows specially if there was a WM_CHAR?

Or am I missing something that is not in any of the MSDN TSF samples that will allow me to just have TSF take care of the TF_PROFILETYPE_KEYBOARDLAYOUT processors? I thought TSF did transparent IME passthrough, but my experiment with the FAKEIME sample showed otherwise...? I see both Firefox and Chromium also check for TF_PROFILETYPE_KEYBOARDLAYOUT and even use ImmGetIMEFileName() to see if the keyboard layout is backed by an IME or not, but I don't know if they actually take care of input themselves in these cases...

My minimum version right now is Windows 7.

Thanks.

UPDATE The original version of this question included needing to know about associated WM_KEYUPs; on second look through my equivalent code on other platforms this won't be necessary after all, except for the details of TranslateMessage(); I've adjusted the question accordingly. (On OS X you don't even give key-release events to the text input system; on GTK+ you do but it seems keypresses that insert characters don't bother with releases and so they don't get handled anyway, at least for the input methods I've tried (there could be some that do...).) That being said, if I missed something, I added another sub-question.

2

There are 2 answers

8
Eric Brown On BEST ANSWER

In general, it's not a good idea to try to duplicate Windows internals. It's tedious, error-prone, and likely to change without notice.

The edit controls that I have source access to pick off arrow keys (and other specific keys) in the WM_KEYDOWN handler and pass everything else off to the default handler, which will (eventually) generate WM_CHAR or TSF input calls (if your control supports TSF, which it should).

You would still need WM_CHAR in the case where there is no TSF handler involved. However, you can always have your WM_CHAR handler call your ITextStoreACP::InsertTextAtSelection method.

0
lb90 On

my architecture needs a way to be told that it can ignore the current key message because it was translated into a text input message

I believe you can use TranslateMessageEx, perhaps with flags bit 1 set. Otherwise you may call PeekMessage(PM_NOREMOVE) and check if it returns a WM_CHAR message for the same window.