One problem I discovered when my nieces ran my initial ABC program was that they had a habit of holding down a key, thereby triggering autorepeat. I had instructed them not to mash the keyboard but rather to press only one key at a time, and while they were good at adhering to the "one key at a time" rule, they also interpreted it as "type really slowly" and ended up autorepeating a lot.

So let's disable keyboard autorepeat.

Of course, one way to do this would be to change the system keyboard autorepeat setting, but that would be using global state to manage a local problem. Instead, we just filter the autorepeats out of our edit control:

LRESULT CALLBACK EditSubclassProc(
    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (uMsg) {
  case WM_NCDESTROY:
    RemoveWindowSubclass(hwnd, EditSubclassProc, uIdSubclass);
    break;
  case WM_CHAR:
    if ((lParam & 0x40000000) && wParam != VK_BACK) return 0;
    break;
  }
    return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
  ...

  SetWindowSubclass(g_hwndChild, EditSubclassProc, 0, 0);
  SetWindowFont(g_hwndChild, g_hfEdit, TRUE);

  return TRUE;
}

Bit 30 in the lParam of a WM_CHAR message says whether the key was already down. If we see that bit set, then we know that the message was an autorepeat and we throw the message away. (But I let the backspace key through because that lets me erase a lot of text quickly.)

It's important that the subclass procedure be removed before the window is destroyed. One way of doing this is to remove the subclass procedure in the parent window's WM_DESTROY handler, but since I don't have one, and I'm too lazy to make one, I go for the alternate method of doing just-in-time deregistration by removing the subclass procedure in the subclass procedure itself.

This version of the program managed to keep my nieces happy for quite some time. We'll tinker with it some more next week.