I want to capture WM_KEYDOWN event when PRESS TAB, but looks like I cannot do it. Only WM_KEYUP can be caught when press TAB
Actually I found the WM_KEYDOWN event was filter out by the child control as 'TAB' is in the accelerator table.
Related
The default way of handling the lifetime of windows in Windows is listening for a WM_CLOSE message, then deciding whether to close the window or not and optionally processing the WM_DESTROY message (for cleanup etc.) afterwards.
Is there any scenario where WM_DESTROY could be sent without WM_CLOSE beforehand? I mean "by the system", not by manually calling DestroyWindow() or PostMessage(). Or is it safe to rely on WM_CLOSE always being sent before WM_DESTROY?
Yes. If you create a MFC dialog based application and press "Cancel" button (IDCANCEL as the ID), it doesn't send WM_CLOSE event. Also if you press "Esc" key, same thing.
If you click "X" button on the upper-right corner of the dialog box, yes WM_CLOSE will be sent, though.
In the WM_COMMAND section of a Dialogex message loop it's possible to add a case for WM_DESTROY. This case captures an Escape keydown by default, so by adding an accompanying EndDialog statement in the VK_ESCAPE condition, we can then initiate a PostQuitMessage.
Edit: There's a clearer explanation here.
I am using Visual Studio 2005 with MFC and Windows 7. I have an application with many dialog windows.
I use OnSysCommand to check for SC_CLOSE messages and check the lParam to determine if this is initiated from the task bar or the close button on the dialog. But how can I determine whether a close message is a "close all windows" from the task bar or just closing an individual dialog from the task bar?
Thanks
I don't think that you get this solved with a single message.
When you Close the application you have to distinguish also between a mouse action and Alt+F4
If you close the application with Alt+F4 the message looks identical like closing it from the task bar (Look at the lParam value)
You can look at the last message that was retrieved with GetMessage (the last input message). If the message comes from the task bar it is a posted WM_SYSCOMMAND. If the message comes from the inside you receive the WM_SYSCOMMAND as SendMessage.
You can use AfxGetCurrentMessage to determine what was the last input message. If you find WM_SYSCOMMAND here the close comes from the taskbar. If you find a keyboard or mouse message here the message comes form the user input.
Tip: Use Spy++ to examine this behavior.
I think you can differentiate as follows:
Closing a windows using the system menu 'Close' generates a WM_SYSCOMMAND where wParam= SC_CLOSE and lParam!=0.
Closing the window using Alt+F4 or "Close all windows" both generate a WM_SYSCOMMAND with a wParam=SC_CLOSE and lParam=0.
However, Alt+F4 generates a WM_SYSKEYDOWN message with wParam=VK_F4 beforehand.
I wanted to ignore the "Close all windows", whilst using Alt+F4 and the 'Close' menu. I therefore caught Alt+F4 in WM_SYSKEYDOWN and posted a WM_CLOSE message. I then ignored any WM_SYSCOMMAND message with wParam=SC_CLOSE and lParam=0.
I'm having a problem receiving message in WM_KEYDOWN. WM_KEYDOWN works just fine until I click any button in my app. From that point it no longer receives my input from the keyboard. How to fix it?
If you are using Win32 controls such as CreateWindowEx(NULL, L"BUTTON", ... this is expected Each control is actually a child window and is capturing all of the window messages after it has focus.
Once the button is clicked you can capture the WM_COMMAND - BM_CLICK message to then call SetFocus(hwnd) to refocus on your window (as Giswin mentioned).
Probably your window has no focus before you click any button on your app. you can add code somewhere in your app to set focus programmatically:
yourwindow->SetFocus();
or use winapi:
::SetFocus(hWnd);
Just in case anyone is wondering, I (unsurprisingly) noticed the same behavior for handling WM_CHAR responses in my WindowProcedure callback as well. As soon as you click a button, the focus changes from the main window to the button control (which is a child window) and keyboard presses no longer have any effect.
As suggested by #NTSCCobalt, adding a simple SetFocus(main window handler) in your WM_COMMAND cases will solve the problem, e.g.
case DEL__BUTTON:{
<Button specific code>
SetFocus(hwnd);
return 0;
}
I have a Windows application that registers one CALLBACK procedure that handles WM_HELP messages for the dialog, and one CALLBACK procedure that handles WM_LBUTTONUP messages for a custom button.
Now, when the user clicks the "?" button, then clicks the custom button, the help opens up as expected (on mouse down), BUT if the help window is not occluding the button, a WM_LBUTTONUP message is triggered as well for the custom button (on mouse up). This causes the button to trigger when the user was only asking for help.
Is there any way to stop the WM_LBUTTONUP message from being sent if the button press is for help?
EDIT: The custom button is implemented using a STATIC control. I believe this is because it needs to have an image and no border. So, it does not send BN_CLICKED notifications. In fact, it does not seem to trigger WM_COMMAND in the parent at all.
Thanks
This is normal. Be sure to use the button's BN_CLICKED notification to see the difference. Generated when the user clicks the button, not generated when the user uses the help cursor. The button still sees the normal button up/down messages so that's not a good trigger to detect a click. Extra bonus is that the button can now also be clicked with the keyboard (space bar).
A good class library takes care of these nasty little details.
A better way would be to create ? as a custom control with BS_CHECKBOX | BS_PUSHLIKE style and capture the mouse. After that you will get all the WM_LBUTTONDOWN message to this custom control and then you can use WindowFromPoint to get the window where the WM_LBUTTONDOWN happened and can send a custom notification to the parent window. The parent window can then show a the tooltip or open the help doc or discard it.
The advantage is, you create the control only once and you can use it in multiple places.
Okay, I fixed it by creating the custom button (static control) with the SS_NOTIFY style, then handling the STN_CLICKED notification in the WM_COMMAND message. (SS_NOTIFY causes WM_COMMAND to trigger in the parent when it is clicked.) This does not trigger when using the "?" button. Thanks!
I'm trying to get my application to do something when CTRL+S is pressed. I'm just not sure how the W and L params work for WM_KEYDOWN. MSDN has something about bit fields which i'm not sure about. How can I detect CTRL and S?
Thanks
What do I do if another control aside from hWnd has focus?
Well, this is the big list of virtual key codes.
CTRL-S is going to be sent through as 2 WM_KEYDOWN messages - a message when the ctrl key is pressed (VK_LCONTROL or VK_RCONTROL) followed by a 0x53 for the "S" key.
Rather than processing both messages, wait for the key down message for the 'S' press then call GetKeyState using the magic value VK_CONTROL (otheriwse you'd need to test individually for the left AND right control keys) to see if the S was pressed with CTRL held down.
--
Obviously, keyboard messages are sent directly to the window that has focus. To get accelerator combinations to work at the application scope you need to check the messages before dispatching them to the focus window - i.e. in your message pump. See the documentation for TranslateAccelerator.
If you want to handle system wide keypresses, the other answer points to the hot key api.
When the WPARAM is equal to the CTRL VKcode, then set a bool in the state of your object. Then, when S comes up, if Ctrlbool, you've got CTRL-S.