I am writing a .NET wrapper around Win32 hooks which buffers WM_CHAR messages and allows events such as key presses, key releases, and accelerator keystrokes to be subscribed to. Everything is in working order except apparently my call to TranslateAccelerator. It returns true when I expect it to (when it finds an accelerator in the given table which I constructed earlier) but the WM_COMMAND messages don't seem to be showing up ever. Here is some relevant code.
[StructLayout(LayoutKind.Sequential)]
struct MSG {
IntPtr hWnd;
WindowsMessages message;
IntPtr wParam;
IntPtr lParam;
UInt32 time;
POINT pt;
}
delegate IntPtr HOOKPROC(HookCodes nCode, IntPtr wParam, ref MSG lParam);
[DllImport("user32.dll")]
extern IntPtr CallNextHookEx(IntPtr hhk, HookCodes nCode, IntPtr wParam, ref MSG lParam);
IntPtr HookProcedure(HookCodes nCode, IntPtr wParam, ref MSG lParam) {
IntPtr result = IntPtr.Zero;
if(nCode < HookCodes.ACTION) {
result = CallNextHookEx(hHook, nCode, wParam, ref lParam);
} else if(nCode == HookCodes.ACTION && (PeekMessageOptions)wParam == PeekMessageOptions.REMOVE) {
/*
* Under these conditions, each message will only be passed onto the switch below once.
*/
switch(lParam.message) {
case WindowsMessages.KEYDOWN:
//fire keydown events and call TranslateAccelerator/TranslateMessage
break;
case WindowsMessages.KEYUP:
//fire keyup events
break;
case WindowsMessages.COMMAND:
//fire accelerator events Ex: Ctrl+F, ALT+M, SHIFT+L
break;
case WindowsMessages.CHAR:
//place char in buffer
break;
default:
break;
}
}
return result; //Will be zero if action was taken on the message by this procedure.
}
}
In KeyPressed(MSG), messages are translated like so:
[DllImport("user32.dll")]
extern bool TranslateAccelerator(IntPtr hWnd, IntPtr hAccTable, ref MSG lpMsg);
[DllImport("user32.dll")]
extern bool TranslateMessage(ref MSG lpMsg);
if(!TranslateAccelerator(hWnd, hAccel, ref msg)){
TranslateMessage(ref msg);
}
The hook and accelerator table are created like this:
[StructLayout(LayoutKind.Sequential)]
struct ACCEL {
byte fVirt;
ushort key;
ushort cmd;
}
[DllImport("user32.dll")]
extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
extern IntPtr SetWindowsHookEx(WindowsHooks hook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
extern IntPtr CreateAcceleratorTable(ACCEL[] lpaccl, int cEntries);
HOOKPROC proc = HookProcedure;
uint pid = GetWindowThreadProcessId(/*IntPtr*/hWnd, IntPtr.Zero);
IntPtr hHook = SetWindowsHookEx(WindowsHooks.GETMESSAGE, proc, IntPtr.Zero, pid);
IntPtr hAccel = CreateAcceleratorTable(/*ACCEL[]*/accelerators, accelerators.Length);
Everything works fine (such as attaching events to key-downs and buffering WM_CHAR messages) except I can't find WM_COMMAND or WM_SYSCOMMAND messages anywhere in the queue. They just don't seem to be visible to my hook procedure, even though TranslateAccelerator returns true when I expect it to. Note that I am really pretty clueless when it comes to Win32 so I'm probably missing something fairly obvious to the trained eye. But I am at my wits end. I've tried attaching the hook to an XNA window and a Windows Form without success.
You won't catch those WM_COMMAND messages with a GETMESSAGE hook.
TranslateAccelerator "sends the WM_COMMAND or WM_SYSCOMMAND message directly to the specified window procedure" (see the documentation) and hence bypasses the message queue.
You'll need a CALLWNDPROC hook instead.
Related
so, I've been developing a class to handle Kwyboard input in a VSTO add-in, so far I've been using Windows hooks to do so with relative success.
Having this code:
//.....
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private enum WM : uint {
KEYDOWN = 0x0100,
KEYFIRST = 0x0100,
KEYLAST = 0x0108,
KEYUP = 0x0101,
MOUSELEFTDBLCLICK = 0x0203,
MOUSELEFTBTNDOWN = 0x0201,
MOUSELEFTBTNUP = 0x0202,
MOUSEMIDDBLCLICK = 0x0209,
MOUSEMIDBTNDOWN = 0x0207,
MOUSEMIDBTNUP = 0x0208,
MOUSERIGHTDBLCLK = 0x0206,
MOUSERIGHTBTNDOWN = 0x0204,
MOUSERIGHTBTNUP = 0x0205
}
private hookProcedure proc;
private static IntPtr hookID = IntPtr.Zero;
//Enganches
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int hookId, hookProcedure proc, IntPtr hInstance, uint thread);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool unHookWindowsHookEx(int hookId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hookId, int ncode, IntPtr wparam, IntPtr lparam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
public CPInputListener() {
proc = keyBoardCallback;
hookID = setHook(proc);
}
private IntPtr setHook(hookProcedure procedure){
ProcessModule module = Process.GetCurrentProcess().MainModule;
uint threadId = (uint)GetCurrentThreadId();
return SetWindowsHookEx(WH_KEYBOARD, procedure, IntPtr.Zero, threadId);
}
public void stopListeningAll() {
unHookWindowsHookEx(WH_KEYBOARD);//For now
}
private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam) {
if (ncode >= 0) {
//LPARAM pretty useless
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);
onKeyDown(args);//for now
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
//....
I do successfully receive keyboard input, but here is the big mistery; each time a key is pressed, no matter how fast it was, the event (onKeyDown) is called 10 times exactly, no more no less.
If the key is long pressed, the event keep being called but 10 by 10 times instead of calling just once.
So far I've tried
Using wParam to call the required event on Key Up: Doesn't seem to work, in all codes I've seen dealing with Key down and up events, IntPtr wParam is used, but from that variable I can only retrieve the keycode which doesn't help.
Using lParam or nCode: These vars are giving unconsistent values between those 10 calls, ncode tends to retrieve 0's and 3's and lParam some values which seem to be unmanaged memory adresses...
What do I expect
I do expect for onKeyDown to be called just once when the key is pressed or in the other hand being able to call the method by on key up which i do expect to be called just once per key releasing.
How to bypass this
If I can't find a reasonable answer, I was thinking on using a custom made timer to discard all those callings and use only the last one, would you recommend this if everything else fails?
Thanks a lot! Be happy and be kind! :D
First you have to filter for the correct ncode to get only the keystrokes you are supposed to process. (For example, you are not supposed to process HC_NOREMOVE.)
Then you have to check if it was a KeyDown or KeyUp event using a flag in lParam.
If the key was long-pressed, multiple KeyDown events are already combined to one call by Win32, so you don't have to do anything special here. But if you want to get only the last KeyUp event then you have to check another flag in lParam as well.
So, here's the code you need to change:
private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam)
{
// Feel free to move the const to a private field.
const int HC_ACTION = 0;
if (ncode == HC_ACTION)
{
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);
bool isKeyDown = ((ulong)lParam & 0x40000000) == 0;
if (isKeyDown)
onKeyDown(args);
else
{
bool isLastKeyUp = ((ulong)lParam & 0x80000000) == 0x80000000;
if (isLastKeyUp)
onKeyUp(args);
}
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
Edit as requested in the comment:
Unfortunately the documentation of these parameters is pretty sparse.
One "hint" not to process anything other then HC_ACTION can be found here, stating:
if (nCode < 0) // do not process message
return ...;
// ...
switch (nCode)
{
case HC_ACTION:
// ... do something ...
break;
default:
break;
}
// ...
return CallNextHookEx(...);
Another supporting statement is made here:
Why does my keyboard hook receive the same key-up and key-down events multiple times?
The content of the lParam is defined as follows:
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
}
(Just as a reminder: DWORD here is 4 bytes in size on x86 as well as on x64 platforms.)
The documentation of the lParam flags can be found here and here.
In this links it's described that
bit 30 (=0x40000000) is the previous key state
(1 if the key was down and 0 if the key was up before the new key state that caused this call)
bit 31 (=0x80000000) is the transition state
(0 on key press and 1 on key release now)
The term "previous key state" is rather confusing but effectively it's just the opposite of the current state (because there's only up or down and no third state).
The transition state is especially relevant when the "keyboard's automatic repeat feature" is activated, i.e. when the key is pressed down long enough.
Another sample (using VC7) can be found here:
if (HIWORD (lParam) & 0xC000)
// Key up without autorepeat
else
// Key down
Where 0xC000 just is 0x4000 || 0x8000 and defines that the key was released and has created a key up event.
All in all pretty confusing but nonetheless true.
Maybe there are other links out there that can describe this situation better, but I guess in times like these where new app development "should be done" in tiny sandboxes (like UWP) and VSTO is on its sure way to die to make way for newer Office add-ins that are written in HTML and JavaScript, nobody cares all that much about low-level hooks anymore.
Well, first off, what I'm trying to do is click a specific point inside a flash object, inside of a webbrowser control. I'm not sure why it isn't working, but I cannot seem to click any window, be it notepad, or the actual program.
Here is my code that I'm using.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public IntPtr find()
{
return this.Handle;//FindWindow("", "Form1");
}
public enum WMessages : int
{
WM_LBUTTONDOWN = 0x201, //Left mousebutton down
WM_LBUTTONUP = 0x202, //Left mousebutton up
WM_LBUTTONDBLCLK = 0x203, //Left mousebutton doubleclick
WM_RBUTTONDOWN = 0x204, //Right mousebutton down
WM_RBUTTONUP = 0x205, //Right mousebutton up
WM_RBUTTONDBLCLK = 0x206, //Right mousebutton do
}
private int MAKELPARAM(int p, int p_2)
{
return ((p_2 << 16) | (p & 0xFFFF));
}
/** This is the non-working code **/
public void DoMouseLeftClick(IntPtr handle, Point x)
{
SendMessage(handle, (int)WMessages.WM_LBUTTONDOWN, 0, MAKELPARAM(x.X, x.Y));
SendMessage(handle, (int)WMessages.WM_LBUTTONUP, 0, MAKELPARAM(x.X, x.Y));
return;
//I have tried PostMessage, and SendMessage, and both of them at the same time, and neither works.
PostMessage(handle, (uint)WMessages.WM_LBUTTONDOWN, 0, MAKELPARAM(x.X, x.Y));
PostMessage(handle, (uint)WMessages.WM_LBUTTONUP, 0, MAKELPARAM(x.X, x.Y));
}
private void timer2_Tick(object sender, EventArgs e)
{
//I try hovering my mouse over a button I added to the form, and nothing happens.
DoMouseLeftClick(find(), Cursor.Position);
}
So, I have tried using PostMessage, and SendMessage, and neither of those seem to work.
All I need it to do is to click on a specific point.
Also, I need to mention that I can't use mouse_event, because from what I know, the window needs to be active, plus the cursor needs to be over the point you are clicking. I am making a bot that automatically does a process in a flash object, so that's why I can't use mouse_event.
Thanks for your help guys.
I had the same problem.
I tried to draw something in the MS Paint. It turned out that I was clicking on the main window but it turned out that MS Paint (and most of the applications) consist of many child windows and you actually want to click on the child. So I had to change my code from:
IntPtr handle = FindWindow(null, "Untitled - Paint");
PostMessage(handle, (uint)MOUSE_BUTTONS.LEFT_DOWN, 0, lparam);
to
IntPtr handle = FindWindow(null, "Untitled - Paint");
handle = FindWindowEx(handle, IntPtr.Zero, "MSPaintView", null);
canvasHandle = FindWindowEx(handle, IntPtr.Zero, "Afx:00007FF676DD0000:8", null);
PostMessage(canvasHandle, (uint)MOUSE_BUTTONS.LEFT_DOWN, 0, lparam);
You need to use tools like Spy++ for Windows that comes with C++ Visual Studio package if you want to debug these kind of things (even if you program in C#)
Hope it helps someone.
For the WM_LBUTTONDOWN, you may need to specify which button. Take reference for: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645607(v=vs.85).aspx
I used:
SendMessage(hWnd, (int)WMessages.WM_RBUTTONDOWN, (int)KeyDownMessage.MK_LBUTTON, MAKELPARAM(x, y));
When I try to paste text in textbox another program, the text is inserted, but the program does not recognize it.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const uint WM_SETTEXT = 0x000C;
IntPtr text = Marshal.StringToCoTaskMemUni("100");
IntPtr thisWindow = FindWindow(null, "AnotherWindow");
IntPtr handle = FindWindowEx(thisWindow, IntPtr.Zero, "AnotherTextBox", null);
SendMessage(handle, WM_SETTEXT, IntPtr.Zero, text);
Marshal.FreeCoTaskMem(text);
Maybe I should send to the parent a message that the textbox is updated?
Like this:
//Wrong code, because I do not know how correctly send a message
SendMessage(handle, WM_COMMAND, EM_SETMODIFY, handle);
And again...help came from another site
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
//...
IntPtr boo = new IntPtr(1);
SendMessage(handle, EM_SETMODIFY, boo, IntPtr.Zero);
You'd be looking to do something like:
PostMessage(GetParent(handle), WM_COMMAND, MAKEWPARAM(GetWindowLong(handle, GWL_ID), EN_CHANGE), (LPARAM)handle);
some Textboxes have been set so that you can not set your text by WM_SETTEXT immediately, especially those which accept digits and perform calculation according these digits. I had similar problem and solved it by bellow code. I applied WM_PASTE, EM_REPLACESEL to conquer that.
SendMessage(child, WM_SETFOCUS,0 , IntPtr.Zero); // go to text box
System.Windows.Forms.Clipboard.SetText("1"); // set something in clipboard. it does not matter what it is.
SendMessage(child, WM_PASTE, 0, IntPtr.Zero); // paste to get control of text box
SendMessage(child, WM_SETTEXT, IntPtr.Zero, string.Empty); // clear textbox to insert your desired text.
SendMessage(child, EM_REPLACESEL, IntPtr.Zero, "your text"); // insert your desired text. i inserted digits as text.
you need to Import user32.dll file at first:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
private static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
private static extern int SendMessage(IntPtr hWnd, uint msg,int wParam, IntPtr lParam);
I'm trying basically SendKey's to IE9 to change tabs. I have 3 tabs so I'd need to Send keys Ctrl+1, Ctrl+2, Ctrl+3 and also Ctrl+T to open a new tab.
I start by adding the import dlls and constants
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg,
IntPtr wParam, IntPtr lParam);
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
I get the instance of Internet Explorer by opening a new process.
Process p = Process.Start("iexplorer.exe");
Use the process handle to PostMessage to IE9 instance
IntPtr handle = p.MainWindowHandle; //p.Handle (doesn't work either)
//Change to Tab2 using PostMessage
PostMessage(handle, WM_KEYDOWN, ((IntPtr)Keys.LControlKey), (IntPtr)0);
PostMessage(handle, WM_KEYDOWN, ((IntPtr)Keys.D2), (IntPtr)0);
PostMessage(handle, WM_KEYUP, ((IntPtr)Keys.D2), (IntPtr)0);
PostMessage(handle, WM_KEYUP, ((IntPtr)Keys.LControlKey), (IntPtr)0);
No response. I've also tried using SendMessage to no avail as well.
Am I doing anything obviously wrong?
How about SendKeys("^1");
as seen here
I have a laptop with a very sensitive touch pad, and wanted to code a small program that could block the mouse input when I was typing a paper or something.
I didn't think it would be hard to do, considering everything I've seen on low-level hooks, but I was wrong (astounding, right?).
I looked at a few examples, but the examples I've seen either block both keyboard and mouse, or just hide the mouse.
Any help with this would be great.
As you mentioned, you can do this using a low-level mouse hook (WH_MOUSE_LL), albeit somewhat incorrectly. What happens when you set a hook is that you'll receive notifications on each mouse input event (WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_XBUTTONDOWN, WM_NCXBUTTONDOWN, the equivalent up events for each of those, WM_MOUSEWHEEL, and WM_MOUSEHWHEEL). Once you've finished processing each event, you're supposed to call the CallNextHookEx function, which passes the event information on to the next application in the hook chain. However, if you want to prevent any other program from getting mouse input information, you can just skip calling that function at the end of your hook procedure. The "Remarks" section of the above-linked documentation explains it thusly:
Calling CallNextHookEx is optional,
but it is highly recommended;
otherwise, other applications that
have installed hooks will not receive
hook notifications and may behave
incorrectly as a result. You should
call CallNextHookEx unless you
absolutely need to prevent the
notification from being seen by other
applications.
And as it turns out, low-level mouse hooks aren't actually that difficult in C#. I just coded one up myself, actually. But rather than posting that monstrosity of a library, I'll refer you to the simpler code snippet posted on Stephen Toub's blog, which I've reprinted here with syntax highlighting for convenience:
class InterceptMouse
{
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
As I explained above, you'll want to modify his HookCallback method not to call CallNextHookEx once you've finished processing the mouse event, but return something like new IntPtr(1) instead.
EDIT: And yeah, as others mentioned, there are probably other easier, cleaner solutions to this problem. Your trackpad drivers are a great place to look for an option like "Ignore accidental mouse input while typing". If you don't have this option, you're probably using the standard Windows mouse drivers. Try to download the drivers from your trackpad's manufacturer from the laptop manufacturer's website (for what it's worth, most of the non-Apple trackpads I've seen are Synaptics).
A lot of touchpad drivers have this as an option. I.e. When you are typing, it ignores touchpad input. You could also turn off the tap-click, relying on the actual touchpad buttons to click.
First try the driver's configuration utility, before you try to write your own.