This is another approach to intercepting a key from inside an edit control in Windows. This redirects a message using a field inside the MSG structure in the WinMain message loop instead of subclassing. It is a simple solution for a simple use case.
Introduction
When a dialog box is not used, the purpose of the code is not complex and Microsoft's recommendations are not appropriate, this solution offers an alternative to customising Windows control messages.
Background
I have a Windows single line edit control that the user could enter a filename into. The default processing of this edit control did not include the return/Enter key input because I do not use dialogs. My main window is a straightforward window. The default edit control processes the Enter key by returning to the expected dialog parent. This is no use to me and thus I must code for the use of the Enter key to submit or accept input at the edit control.
Initially, while testing that the input filename was correctly retrieved and processed in my program, my workaround was to focus on the start window (the parent of the edit control) after input and press the Enter key. Just as if there was a Submit or Accept button. My code to retrieve and process the input was then initiated from the start window procedure. Once everything was working well with the input, I needed to find a way to allow the user to enter and return input with the Enter key while inside the edit control. A much more natural method of input.
My research found a lot of solutions to the question of how an application can customise the processing of keys entered into an edit control. But these solutions used subclassing. In subclassing, processing of a control is diverted to an application customised subprocedure. Then the default edit control procedure is called to continue with the rest. This is the recommendation in the Microsoft Learn documentation.
But there is no need to subclass an edit control. In my particular case, I need to process only the Enter key - all other editing can be left to the default edit procedure. So subclassing seems to be overkill in this case. All the text retrieval and processing is already set up from my main/start window procedure. I just need to intercept the edit control Enter key message.
Note that the default edit control process sends EN_CHANGE
and EN_UPDATE
notifications in the WPARAM
parameter through the WM_COMMAND
message to the start window procedure every time a character is input. It is an interesting exercise to print the wParam
at this point as a hex value. The (HIWORD)wParam
part shows whether it is change or update (4 or 3) and the (LOWORD)wParam
gives the hex value of the edit control ID
specified in the create window function. Change notifies keyboard input and update notifies write of the input to display. The notification values are found in the
winuser.h header file.
But I cannot see a way to determine what key is actually pressed at that point. So the message must be rerouted from the edit control to the main window in the message loop of the WinMain
entry function. All messages go through this loop.
Using the Code
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (GetWindowLongPtr(msg.hwnd,GWLP_ID) == ID_inpfn && msg.wParam == 0x0d)
SendMessage(hsw,WM_KEYDOWN,(WPARAM)VK_RETURN,0);
}
Whether this condition is true
or false
, the dispatch of this message here will have no result because the edit control has no dialog box to send to. This is the original problem that is solved when the loop gets the newly sent message from the queue.
msg.hwnd
is the window handle that the message was got from - the input edit control in this case.
hsw
is the handle of the start window (main window) so that the nessage is processed in the main window WNDPROC
function. To make sure that this message is from the edit control and that it is not a return key from the main window for some other purpose, this condition is placed before retrieval and processing of the input:
case WM_KEYDOWN:
switch(wParam) {
case VK_RETURN: {
HWND hfcs = GetFocus();
if (GetWindowLongPtr(hfcs,GWLP_ID) == ID_inpfn) {
}
break;
}
Since the return comes from inside the edit control, the focus must still be with the control.
ID_inpfn
is the designated HMENU
field in the create window function for the edit control. It is defined in the preprocessor as:
#define ID_inpfn 100
HWND hi = CreateWindowEx(0,
"EDIT", 0,
WS_CHILD|WS_VISIBLE|ES_LEFT,
20,20,100,20,
hsw, (HMENU)ID_inpfn,
(HINSTANCE)GetWindowLongPtr(hsw, GWLP_HINSTANCE), 0);
msg.wParam == 0x0d
is the hex representation of VK_RETURN
, which could be put in there instead for readability. Personally, I like to be reminded that Windows predominately uses hex values.
Points of Interest
It is a useful exercise to take a look at the MSG structure
from winuser.h:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG,*PMSG,*NPMSG,*LPMSG;
From this, we can see different ways to access a message using this available information. This is a simple solution for a simple problem.
History
- 26th January, 2024: Initial version