A dialog can send key input to any foreground window without itself being activated to function as an on-screen keyboard. Regular windows get activated and focused when they are clicked or something. To avoid this, a window needs to be a non-activating window. You can do this by creating a window with the "
WS_EX_NOACTIVATE" extended style, or by modifying its style using "
ModifyStyleEx". But, with this style, a window behaves somewhat differently than expected when you move the window. To solve this, you need to provide your own implementations for the "
WM_NCLBUTTONDOWN" and "
WM_MOUSEMOVE" messages to behave the same way as the system-provided On-Screen Keyboard does. This code shows how to do this and send your own keyboard inputs to any foreground window using the newly-introduced "
SendInput" system function.
One day, I needed to implement a virtual On-Screen Keyboard that received signals - through a COM (serial) port - from a custom input device and then sent keyboard inputs to the system according to the signals. While doing this, I came to know about the On-Screen Keyboard accessory application provided by Windows, and wanted my application to behave similar to that. But, it was not as trivial a task as I first expected, because of the "focus" and "activation" problems. In this example, you will find that avoiding those problems could be done in a simple way.
Using the code
This is a VC++ 9.0 project.
Firstly, you need to create an MFC dialog-based application. And then, to make the dialog a non-activating topmost window, you need to change its "No Activate" and "Topmost" resource properties to "true".
Then, handle "
WM_NCLBUTTONDOWN" message. This is for making the dialog temporarily behave as a regular window without the "
WS_EX_NOACTIVATE" style so that it can move smoothly.
void COnScreenKeyboardDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
m_hForegroundWnd = ::GetForegroundWindow();
Now, handle the "
WM_MOVE" message. This is for making the dialog behave again as a non-activating window when the mouse pointer moves around in the client area, and for making any previous foreground window active. This is the exact behavior the system-provided On-Screen Keyboard shows.
void COnScreenKeyboardDlg::OnMouseMove(UINT nFlags, CPoint point)
m_hForegroundWnd = NULL;
Now, to send an arbitrary key input to the system, you can use the "
SendInput" function. Here, I send the "x" (virtual code 88) key input as an example when the "Send X" button in the dialog is clicked.
keyInput.type = INPUT_KEYBOARD;
key.wVk = 88;
key.wScan = ::VkKeyScan(88);
key.dwFlags = 0;
keyInput.ki = key;
key.dwFlags = KEYEVENTF_KEYUP;
keyInput.ki = key;
Points of interest
Code samples I have found on the Internet do this in an overly complicated manner using the "
AttachThreadInput" function. As you can see, making a window behave the same way as the Windows On-Screen Keyboard can be done in a simple way using the "
WS_EX_NOACTIVATE" style and some other small tricks.