I want to create an edit control where users can only type in floating point numbers, but I want to also be able to copy/paste/cut text in this edit. So I subclassed an edit control with the following window procedure:
LRESULT CALLBACK FloatTextboxWindowProc(HWND windowHandle, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR subclassId, DWORD_PTR refData)
{
switch (msg)
{
case WM_CHAR:
// If the character isn't a digit or a dot, rejecting it.
if (!(('0' <= wparam && wparam <= '9') ||
wparam == '.' || wparam == VK_RETURN || wparam == VK_DELETE || wparam == VK_BACK))
{
return 0;
}
else if (wparam == '.') // If the digit is a dot, we want to check if there already is one.
{
TCHAR buffer[16];
SendMessage(windowHandle, WM_GETTEXT, 16, (LPARAM)buffer);
// _tcschr finds the first occurence of a character and returns NULL if it wasn't found. Rejecting this input if it found a dot.
if (_tcschr(buffer, TEXT('.')) != NULL)
{
return 0;
}
}
default:
return DefSubclassProc(windowHandle, msg, wparam, lparam);
}
}
This works, aside from the fact that copy/paste/cut operations are blocked. Nothing happens when I try to do them.
This is confusing to me, because Microsoft says these operations are handled by WM_COPY, WM_PASTE, and WM_CUT messages, which I'm not even overriding. But I tested and found that when I type Ctrl+C, Ctrl+V, and Ctrl+X into the edit, it fires a WM_CHAR message with the key codes VK_CANCEL, VK_IME_ON, and VK_FINAL (may be respectively, I don't remember). Which is weird, because none of these keys sound like they represent these inputs, and nowhere on the Internet does anyone say they do.
If I add the condition that these key codes are passed on to DefSubclassProc() instead of being rejected, it fixes the problem. But I'm hesitant to just take this fix and move on, because I can't explain why it works, and I don't know what bugs it might introduce, resulting from what these key codes actually mean.
So, why does overriding WM_CHAR make copy/paste/cut no longer work? And why do these key codes, which seemingly have nothing to do with these inputs, get associated with them? And how might I allow copy/paste/cut in a less hacky way?
Per the Keyboard Input documentation on MSDN:
So, what is happening is that
TranslateMessage()in your app's message loop convertsWM_KEYDOWNmessages for CTRL-C, CTRL-V, and CTRL-X sequences intoWM_CHARmessages carrying the ASCII control characters 0x03 (ASCIIETX, aka^C), 0x16 (ASCIISYN, aka^V), and 0x18 (ASCIICAN, aka^X), respectively.WM_CHARcarries translated character codes, NOT virtual key codes, which is whyVK_CANCEL(0x03),VK_IME_ON(0x16), andVK_FINAL(0x18) are confusing you. Virtual key codes are not used inWM_CHAR. The reason whyVK_RETURNandVK_BACK(but notVK_DELETE) "work" in your filtering is because those keys are translated into ASCII control characters, per the Using Keyboard Input documentation:ENTER is translated into ASCII control character 0x0D (ASCII
CR, aka^M), which is the same numeric value asVK_RETURN.BACKSPACE is translated into ASCII control character 0x08 (ASCII
BS, aka^H), which is the same numeric value asVK_BACK.Note that the DELETE key is not on the list of translated keys, so the standard DELETE key will not generate a
WM_CHARmessage, as there is no ASCII control character for delete (however, the DEL (.) key on a numeric keyboard may generate aWM_CHARmessage carryingVK_DELETE. In this case, bit 24 of thelParamwill be 1).So,
DefWindowProc()would translate these specialWM_CHARmessages for your clipboard operations intoWM_COPY,WM_PASTE, andWM_CUTmessages, respectively. However, you are filtering out those messages so they don't reachDefSubclassProc(), and thus do not reachDefWindowProc().So, as you already discovered, you do need to allow those messages to pass through your filtering, eg: