Eating keys in WM_SYSKEYDOWN - winapi

I need to handle the WM_SYSKEYDOWN Message sent to my window when alt or F10 is pressed and so I did this in pretranslatemessage for my window
I checked for MSG parameter's message to WM_SYSKEYDOWN and returned true, it is some thing similar to this
BOOL Class::PreTranslateMessage(MSG* pMsg)
{
MSG *pMessage = (MSG *)pMsg;
HWND hwnd = ::GetActiveWindow();
if(::IsWindowEnabled(hwnd)==NULL){
if(pMsg->message = WM_SYSKEYDOWN)
{
return true;
}
}
return parentclass::PreTranslateMessage(pMsg);
}
But this function is not triggered when the user presses the alt key.
I verified in spy++ that when user clicked alt key a WM_SYSKEYDOWN message is sent to the window for which I wrote pretranslatemessage.
I am able to resolve the issue by writing a message handler
void Class::OnSysKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
HWND hwnd = ::GetActiveWindow();
if(::IsWindowEnabled(hwnd)==NULL){
return;
}
parentclass::OnSysKeyDown(nChar,nRepCnt,nFlags);
}
and including ON_WM_SYSKEYDOWN in Message Map

I was a bit curious after I saw your post, so I did a quick test, and as expected WM_SYSKEYDOWN certainly does get sent on Alt-F10 combination (since that message is specifically intended for Alt-Key combinations). In fact, you will get 4 messages which result in the following:
Message Key Char Repeat Scan Ext Alt Prev Tran
-----------------------------------------------------------------------------
WM_SYSKEYDOWN 18 Alt 1 56 No Yes Up Down
WM_SYSKEYDOWN 121 F10 1 68 No Yes Up Down
WM_SYSKEYUP 121 F10 1 68 No Yes Down Up
WM_SYSKEYUP 18 Alt 1 56 No No Down Up
So double check your code, and I am sure you will determine the issue. I hope this helps!

Related

How to redirect mouse wheel message to other windows?

I'm developing a Word addin for MS Word on Windows, and this addin has as 'advanced task pane' showing and docking on the left side of the Word document window (it's treeview(outline) showing a list of Word documents for fast editing multiple documents in a project).
My question is, the Word document window responds to mouse wheel message only when it's focused, but I want to be able to scroll the document with mouse wheel whenever the mouse cursor is hovering on it even the Word document window doesn't have a input focus.
Any hints in this particular case? Thank you!
Not quite sure it will work, but I'd try the following:
Implement a global low-level mouse hook using the SetWindowsHookEx function.
In the hook procedure, which should be called on mouse wheel scroll events, check if the window under mouse cursor is the Word document window. If so, set a shared flag indicating the needed scroll action.
Don't send WM_VSCROLL directly from the hook procedure! This procedure has to be really fast and simple.
Now, in your add-in's message loop check the flag and if it is set, send WM_VSCROLL to the Word document window.
Perhaps you could make use of the SetCapture(hWnd) Windows API function. This will cause all mouse events to go to your hWnd instead of whatever hWnd might normally expect to receive them. If you capture when the mouse enters the Word document window, and ReleaseCapture() when the mouse leaves or Word gains focus, it should work alright.
Disclaimer: I've used mouse capturing in C# before, but I've never done it in C++. I don't know if it behaves exactly the same way.
Try the following , this might help you.
1) Handle WM_MOUSEHOVER message.
2) In the handler , use SendMessage, with WM_VSCROLL as the message parameter .
Using Spy++ I saw that the window that gets the messages is of the class _Wwg (At least 2003 is) and it is responding to the WM_MOUSEWHEEL message. So you would send that window a WM_MOUSEWHELL message when you want it to scroll.
I've got the C++ code snipped below from a comment in https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
And I used it (and variations on it) successfully.
The user who wrote it claims it was inspired by a ms recommendation on a Windows Vista best practices guide, to forward the mouse wheel event to whatever window is hovered by the mouse cursor. The virtue of his/her implementation is that it's completely unintrusive, you just drop it and it set the hooks, referencing your main thread. It avoids forwarding the event to windows belonging to other processes, but perhaps that could actually be a good thing.
namespace {
LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam) {
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
if (nCode >= 0) {
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
if (msgInfo.message == WM_MOUSEWHEEL ||
msgInfo.message == WM_MOUSEHWHEEL) {
POINT pt = {};
pt.x = ((int)(short)LOWORD(msgInfo.lParam)); //yes, there's also msgInfo.pt, but let's not take chances
pt.y = ((int)(short)HIWORD(msgInfo.lParam)); //
//visible child window directly under cursor; attention: not necessarily from our process!
//http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (HWND hWin = ::WindowFromPoint(pt))
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr) {
DWORD winProcessId = 0;
::GetWindowThreadProcessId(//no-fail!
hWin, //_In_ HWND hWnd,
&winProcessId); //_Out_opt_ LPDWORD lpdwProcessId
if (winProcessId == ::GetCurrentProcessId()) //no-fail!
msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
}
}
}
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
struct Dummy {
Dummy() {
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, //__in int idHook,
mouseInputHook, //__in HOOKPROC lpfn,
nullptr, //__in HINSTANCE hMod,
::GetCurrentThreadId()); //__in DWORD dwThreadId
assert(hHook);
}
~Dummy() {
if (hHook)
::UnhookWindowsHookEx(hHook);
}
private:
HHOOK hHook;
} dummy;
}

Issue with generating left click in win32 api?

Below is my code to generate left click using win32 api. The problem is that it gets stuck and does not return to main. When i press Ctrl+c, then it returns to main. BUT when I call it twice, to simulate double click then it is fine. Is there anything wrong with this code?
Thank you.
void LeftClick(void)
{
INPUT Input={0};
// left down
Input.type = INPUT_MOUSE; /*The event is a mouse event. Use the mi structure of the union.*/
Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1,&Input,sizeof(INPUT));
// left up
ZeroMemory(&Input,sizeof(INPUT));
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1,&Input,sizeof(INPUT));
}
If you're not committed to using SendInput, I've had success in the past using SendMessage on the desired hWnd with WM_LBUTTONDOWN then again with WM_LBUTTONUP.
Most buttons also simulate a click with keyboard entry. You can use SendMessage to your desired hWnd with WM_KEYDOWN and wParam VK_SPACE, then WM_KEYUP with VK_SPACE to complete the space bar keypress simulation.

How to emulate WM_KEYDOWN, WM_KEY* from a WM_INPUT handler registered with RIDEV_NOLEGACY?

I have a system with two HID keyboards (actually, one's a barcode scanner.)
I registered for raw input with RIDEV_NOLEGACY to block the system from creating WM_KEY* messages for the barcode scanner, which tediously also blocks the messages from the other keyboard.
My goal is to keep the WM_* messages for any keybaord device that isn't the barcode scanner.
Basically, I need to either:
Create the WM_* messages myself, and post them to my hwnd from the wndproc that received the wm_input
or
Predict the WM_* messgaes the system will generate, and ignore them if they came from the barcode scanner.
I created a working implementation of 2, which works great on XP but fails to block anything on Windows 7. (In fact, on win7 it seems like i'm only reciving WM_INPUTs even without the RIDEV_NOLEGACY flag)
I'm now trying method 1, which is arguably 'more correct', but I can't seems to find a way to do this completely correctly.
My environment is Python 2.6 using PyQt. I'm sending messages directly to a window created by PyQt, and i've hooked into it's wndproc with a win32 event filter.
class wm_lparam(Structure):
_fields_ = [("repeat_count", c_short),
("scancode", c_byte),
("extended_key", c_int, 1),
("reserved", c_int, 4),
("context_code", c_int, 1),
("prev_state", c_int, 1),
("transition_state", c_int, 1),
]
assert sizeof(wm_lparam) == 8, sizeof(wm_lparam)
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
ALL_WM_KEYDOWN = (WM_KEYDOWN, WM_SYSKEYDOWN)
ALL_WM_KEYUP = (WM_KEYUP, WM_SYSKEYUP)
VK_SHIFT = 0x10
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
#These values are filled in by my WM_INPUT handler and the RAWINPUT struct
#staticmethod
def _synthesize_wm_legacy(hwnd, wm, vk, scancode, modifider_keys=None):
kbState_old = (c_byte*255)()
kbState = (c_byte*255)()
def keydown(vk):
return bool(user32.GetAsyncKeyState(vk) & 0x8000)
kbState[VK_SHIFT] = 0x80 if keydown(VK_SHIFT) else 0
kbState[VK_LSHIFT] = 0x80 if keydown(VK_SHIFT) else 0
user32.GetKeyboardState(kbState_old)
user32.SetKeyboardState(kbState)
lParam = wm_lparam()
lp = c_uint.from_address(ctypes.addressof(lParam))
lParam.repeat_count = 0
lParam.scancode = scancode
lParam.extended_key = 0
if wm in ALL_WM_KEYDOWN:
lParam.context_code = 0
lParam.prev_state = 0
lParam.transition_state = 0
if wm in ALL_WM_KEYUP:
lParam.repeat_count = 0
lParam.context_code = 0
lParam.prev_state = 1
lParam.transition_state = 1
lp = lp.value
if wm in ALL_WM_KEYUP: #Seems ctypes doesn't like my struct definition.
lp |= 1 << 30
lp |= 1 << 31
log.debug("Posting %s %s %s %08x\n%s"%(hwnd, wm_str(wm), vk, lp, lParam.dump_s()))
user32.SendMessageA(hwnd, wm, vk, lp)
user32.SetKeyboardState(kbState_old)
This code works, but certain things (like holding the shift key, etc) fail. Also very strange is that when using SendMessage, the letters I type are in uppercase, but switchign to PostMessage makes them lowercase. I can probably solve this via Get/SetKeyState, but I was hoping somebody could give me some answers.
In addition, I'm posting these messages back onto PyQt's queue, but the application fails to process them until a real event is sytem generated. That is, If I type a sentence into a text box, nothing shows up until I then move my mouse over the window. The messages seem queued until a real event happens. Any suggestions?
Clarification:
This is a window in my own process, created by PyQt. I have gotten it's hwnd, and hooked the raw input notification up to it. In the window procedure for WM_INPUT on this hwnd, I want to sendmessage to my own hwnd to duplicate the 'legacy' WM_KEY* messages that I previously disabled to filter them. Again, this all happens in my own process, in my own thread.
Update:
Shift state detection simply doesn't work. No matter what, I am getting all capital keys. Any advice?
I was unable to solve this in pure Win32, and I've gotten only a half solution since i'm using PyQt. In case anyone is interested though, here's the code i'm using for that portion:
class BarcodeQtEventFiler(QtCore.QObject):
def __init__(self, parent, *args):
self.log = logging.getLogger(__name__ + '.keyevent')
self.app = parent
self.input_to_surpress = list()
super(BarcodeQtEventFiler, self).__init__(parent, *args)
def ignoreKey(self, which):
"""On WM_INPUT from the device, call this with the reported VKey"""
self.input_to_surpress.append(which)
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.KeyPress:
if self.input_to_surpress:
if event.nativeVirtualKey() in self.input_to_surpress:
z = None
#This loop eats the suppression buffer until the VK pressed is found. Fixes Dupes for WM key up/down, etc.
while z != event.nativeVirtualKey():
z = self.input_to_surpress.pop(0)
self.log.debug("Ate key press %s (%s)", event.key(), event.text())
return True
else:
self.log.debug("Future surpressed input: %s", self.input_to_surpress)
self.log.debug("Allowing key press %s (%s)", event.key(), event.text())
return False
This is not fixable as-is, you cannot control the keyboard state. The receiving app will use GetKeyState() to check if the Shift, Ctrl or Alt key is down. SetKeyState() doesn't work, it only changes the keyboard state of your process, not the process that gets the messages.
Use SendInput() instead. A window in the target process must have the focus.

SendMessage WM_LBUTTONDOWN/UP works on buttons but not window

I am trying to send some simple mouse down/up messages to Windows Calculator using SendMessage. I have been able to press the buttons by sending the messages to the buttons directly. However, I have not been able to successfully send the same messages to the main calculator window handle. Given that hWnd is the window handle to calculator, this is my code.
IntPtr fiveKey = FindWindowEx(hWnd, IntPtr.Zero, "Button", "5");
int x = 5; // X coordinate of the click
int y = 5; // Y coordinate of the click
IntPtr lParam = (IntPtr)((y << 16) | x); // The coordinates
IntPtr wParam = IntPtr.Zero; // Additional parameters for the click (e.g. Ctrl)
const uint downCode = 0x201; // Left click down code
const uint upCode = 0x202; // Left click up code
SendMessage(fiveKey, downCode, wParam, lParam); // Mouse button down
SendMessage(fiveKey, upCode, wParam, lParam); // Mouse button up
Can anyone explain to me why sending the messages to hWnd instead of fiveKey with the x/y offsets changed to the position of the "5" key does not work? I would like to eventually use this code to simulate mouse clicks on a different application that doesn't have buttons like calculator.
I'm not sure I follow you. Are you trying to send WM_LBUTTONDOWN to the main window with the coordinates of where the 5 button is, with the hopes that the 5 button will get "clicked"? If so, that's just not going to work. WM_LBUTTONDOWN is only ever sent to the window under the mouse cursor. In theory the main window could handle WM_LBUTTONDOWN and see if any of its child windows are at that location, but nobody does that because that's not how WM_LBUTTONDOWN is designed to work.

Can I suppress selected input before the application's main loop?

As part of my Visual Studio utilities add-in SamTools, I have a mouse input routine that catches Ctrl+MouseWheel and sends a pageup/pagedown command to the active text window. Visual Studio 2010 added a new "feature" that uses that gesture for zoom in/out (barf). Currently, my add-in does send the scrolling command, but Visual Studio still changes the font size because I'm not eating the input.
I set my hook with a call to SetWindowsHookEx. Here's the callback code. My question is: is the best way to prevent Visual Studio from handling the Ctrl+MouseWheel input as a zoom command to simply not call CallNextHookEx when I get a mouse wheel event with the Ctrl key down?
(Please bear in mind this is some old code of mine.) :)
private IntPtr MouseCallback(int code, UIntPtr wParam, ref MOUSEHOOKSTRUCTEX lParam)
{
try
{
// the callback runs twice for each action - this is the latch
if (enterHook)
{
enterHook = false;
if (code >= 0)
{
int x = lParam.mstruct.pt.X;
int y = lParam.mstruct.pt.Y;
uint action = wParam.ToUInt32();
switch (action)
{
case WM_MOUSEWHEEL:
OnMouseWheel(new MouseEventArgs(MouseButtons.None, 0, x, y, ((short)HIWORD(lParam.mouseData)) / (int)WHEEL_DELTA));
break;
default:
// don't do anything special
break;
}
}
}
else
{
enterHook = true;
}
}
catch
{
// can't let an exception get through or VS will crash
}
return CallNextHookEx(mouseHandle, code, wParam, ref lParam);
}
And here's the code that executes in response to the MouseWheel event:
void mouse_enhancer_MouseWheel( object sender, System.Windows.Forms.MouseEventArgs e )
{
try
{
if ( Keyboard.GetKeyState( System.Windows.Forms.Keys.ControlKey ).IsDown && Connect.ApplicationObject.ActiveWindow.Type == vsWindowType.vsWindowTypeDocument )
{
int clicks = e.Delta;
if (e.Delta < 0)
{
Connect.ApplicationObject.ExecuteCommand( "Edit.ScrollPageDown", "" );
}
else
{
Connect.ApplicationObject.ExecuteCommand( "Edit.ScrollPageUp", "" );
}
}
}
catch ( System.Runtime.InteropServices.COMException )
{
// this occurs if ctrl+wheel is activated on a drop-down list. just ignore it.
}
}
PS: SamTools is open source (GPL) - you can download it from the link and the source is in the installer.
PSS: Ctrl+[+] and Ctrl+[-] are better for zooming. Let Ctrl+MouseWheel scroll (the vastly more commonly used command).
According to MSDN, it's possible to toss mouse messages that you process. Here's the recommendation:
If nCode is less than zero, the hook
procedure must return the value
returned by CallNextHookEx.
If nCode is greater than or equal to
zero, and the hook procedure did not
process the message, it is highly
recommended that you call
CallNextHookEx and return the value it
returns; otherwise, other applications
that have installed WH_MOUSE hooks
will not receive hook notifications
and may behave incorrectly as a
result. If the hook procedure
processed the message, it may return a
nonzero value to prevent the system
from passing the message to the target
window procedure.
In other words, if your mouse callback ends up using the mouse message, you don't have to call the next CallNextHookEx -- just return a nonzero value and (in theory, at least) the mouse movement should get swallowed. If that doesn't work the way you want, comment and we can iterate.
BTW, another possible alternative: it's possible that VS's mapping to the mouse wheel shows up in the Tools...Customize... UI, just like key mappings do. In that case, you could simply remap your add-in's commands instead of working at the hook level. But it's also posible (likely?) that this gesture is hard-coded.

Resources