Intercepting Window Messages for another Window - c#

I'm using CefGlue to make an application with an embedded webkit browser in it, and I need to listen for mousemovements in the browser window. The winforms control doesn't pass down mouse events to the control so I can't listen to them.
However, I found a bug/feature request with a solution in it, but it's beyond me on how to implement it, I'm not familiar with working directly in the WinAPI. The developer says I need to:
2. OS-specific (windows) - after browser created (CefLifeSpanHandler.OnAfterCreated) get window handle and subclass
them (window subclassing technique). Actually now we have native
window with class CefBrowserWindow (returned by
CefBrowser.GetHost().GetWindowHandle()), then child window
Chrome_WidgetWin_0, and then Chrome_RenderWidgetHostHWND. For
intercepting WM_MOUSEMOVE you are interesting in Chrome_WidgetWin_0
window, which can be easily obtained via CefBrowserWindow. Just play
with Spy++ to see precisely.
https://bitbucket.org/xilium/xilium.cefglue/issue/4/mouse-events-unaccessible
I've done some googling, but I'm not sure how to hook into this. I have this function on my form:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
switch (m.Msg) {
case WM_MOUSEMOVE:
Console.WriteLine("Mouse move!");
break;
default:
Console.WriteLine(m.ToString());
break;
}
}
But I never see a mouse move when I'm over the control. I suspect I need to be listening on the WndProc of CefBrowser.GetHost().GetWindowHandle() But I'm not sure how to do that.

I found a solution. I call this function in the OnAfterCreated event from the WebLifeSpanHandler.
browser.Created += (sender, eventargs) => {
Console.WriteLine("Created.");
BrowserWindowPointer = browser.CefBrowser.GetHost().GetWindowHandle();
Console.WriteLine("BrowserWindowPointer: " + BrowserWindowPointer);
uint BrowserThreadId = GetWindowThreadProcessId(BrowserWindowPointer, IntPtr.Zero);
Console.WriteLine("Browser PID: " + BrowserThreadId);
MouseHookProcedure = new HookProc(this.MouseHookProc);
hHookMouse = SetWindowsHookEx(WH_MOUSE,
MouseHookProcedure,
(IntPtr)0,
BrowserThreadId);
if (hHookMouse == 0) {
Console.WriteLine("MouseHook Failed. Making cursor always visible.");
Cursor.Show();
}
KeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD,
KeyboardHookProcedure,
(IntPtr)0,
BrowserThreadId);
};
The function I was looking for is GetWindowThreadProcessId which I can use the window handle provided by browser.CefBrowser.GetHost().GetWindowHandle()
One thing to note is that the Hook Procedure needs to have some sort of larger scope. C# doesn't see it hooked into the native process and will Garbage Collect it if you let it go out of scope. To fix this, I made them class properties.
Supporting Documents:
http://support.microsoft.com/kb/318804 How to set a Windows hook in Visual C# .NET Provided the basis for hooking into the mouse events. Modified to use the browser thread id
http://msdn.microsoft.com/en-us/library/ms644988(v=vs.85).aspx MouseProc callback function How to handle the mouse windows hook
http://msdn.microsoft.com/en-us/library/ms644984(VS.85).aspx KeyboardProc callback function How to handle the keyboard windows hook. Includes structure of lParam. Important note: wParam contains the Virtual Key Code of the key entered.
http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx Virtual-Key Codes List of keys and their Virtual Key Code, so I could tell what button the user was pressing.
My two hooks (not good code, I don't do anything with the marshaled data):
private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
//Marshall the data from the callback.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (nCode < 0) {
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
} else {
if (wParam.ToInt32() == WM_MOUSEMOVE) {
Screensaver_OnMouseMove(this, null);
}
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
}
}
private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
//Marshall the data from the callback.
KeyboardHookStruct keyInfo = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
int VK_ESCAPE = 0x1B;
if (nCode < 0) {
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
} else {
int keyCode = wParam.ToInt32();
if (keyCode == VK_ESCAPE) {
Application.Exit();
}
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
}
}
This is the externs and the code needed to expose the hooks, this is class level scope:
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
//Declare the hook handle as an int.
static int hHookMouse = 0;
static int hHookKeyboard = 0;
//Declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private const int WM_MOUSEMOVE = 0x0200;
//Declare the wrapper managed POINT class.
[StructLayout(LayoutKind.Sequential)]
public class POINT {
public int x;
public int y;
}
//Declare the wrapper managed MouseHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct {
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}
public struct KeyboardHookStruct {
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
//This is the Import for the SetWindowsHookEx function.
//Use this function to install a thread-specific hook.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint threadId);
//This is the Import for the UnhookWindowsHookEx function.
//Call this function to uninstall the hook.
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//This is the Import for the CallNextHookEx function.
//Use this function to pass the hook information to the next hook procedure in chain.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
public HookProc KeyboardHookProcedure { get; set; }
public HookProc MouseHookProcedure { get; set; }
I'm not sure this is entirely required, especially since the thread being hooked to is going away on close, but just for good measure, don't forget to cleanup your hook after you're done listening. I do this on my form's dispose method:
protected override void Dispose(bool disposing) {
base.Dispose();
if (disposing) {
if (hHookKeyboard != 0) {
UnhookWindowsHookEx(hHookKeyboard);
}
if (hHookMouse != 0) {
UnhookWindowsHookEx(hHookMouse);
}
}
}

Related

How to fix error KeyEventHandler in c# console application?

I have a problem that when executing this code in my winform application, it runs normally but when running on the console it gets an error message, it seems that KeyEventHandler is only used in winform, yes What can be replaced in the console application. Here is my hook function:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace Websevice_Core_Console
{
class Y2KeyboardHook
{
#region Win32 API Functions and Constants
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
KeyboardHookDelegate lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x101;
#endregion
private KeyboardHookDelegate _hookProc;
private IntPtr _hookHandle = IntPtr.Zero;
public delegate IntPtr KeyboardHookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardHookStruct
{
public int VirtualKeyCode;
public int ScanCode;
public int Flags;
public int Time;
public int ExtraInfo;
}
#region Keyboard Events
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
// destructor
~Y2KeyboardHook()
{
Uninstall();
}
public void Install()
{
_hookProc = KeyboardHookProc;
_hookHandle = SetupHook(_hookProc);
if (_hookHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private IntPtr SetupHook(KeyboardHookDelegate hookProc)
{
IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (wParam == (IntPtr)WM_KEYDOWN)
{
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
else if (wParam == (IntPtr)WM_KEYUP)
{
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
}
return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
}
public void Uninstall()
{
UnhookWindowsHookEx(_hookHandle);
}
}
}
Here is how I use it:
_y2KeyboardHook.KeyDown += (sender, e) =>// ghi nhan thong tin tu ban phim
{
if (e.KeyCode.ToString() != "")
{
Console.WriteLine("Hello "+KeyCode);
}
      }
I got an error message on 2 lines
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
Can anyone help me, thanks a lot!
Given the code you posted and the vague "I got an error message" (which is not an actual question nor a useful problem statement…always be specific), it seems likely you are not referencing System.Windows.Forms.dll. On the other hand, if you aren't referencing the Winforms assembly, it's not clear why you don't also get an error where you use KeyEventArgs, since that's defined in the same assembly.
That said, assuming the error you're getting for your use of KeyEventHandler is simply that it's not defined, you can define the same type for your own use:
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
I.e., just copy the delegate type declaration from the documentation.
Now, all that said, if you aren't referencing the Winforms assembly, and you are getting an error message as expected with the KeyEventArgs type as well, as would be expected, you might want to consider not copying the Winforms implementation of this approach, and just declaring your own event args type with your own event handler. The only thing you're returning is the virtual key code, so you might as well define your own types for that:
class KeyboardHookEventArgs : EventArgs
{
public int VirtualKeyCode { get; }
public KeyboardHookEventArgs(int virtualKeyCode)
{
VirtualKeyCode = virtualKeyCode;
}
}
…then the events:
public event EventHandler<KeyboardHookEventArgs> KeyDown;
public event EventHandler<KeyboardHookEventArgs> KeyUp;
Where you create the args object like new KeyboardHookEventArgs(kbStruct.VirtualKeyCode) instead.
Note that your event-raising implementation is not thread-safe. It would be more safe, and more idiomatic in C#, to use the null-conditional operator, e.g.:
if (wParam == (IntPtr)WM_KEYDOWN)
{
KeyDown?.Invoke(this, new KeyboardHookEventArgs(kbStruct.VirtualKeyCode));
}
You should also pass a correct value for sender, i.e. this as I've shown above. If you want for the event to have no sender, then it (and the rest of the class) should just be static.

Global low-level mouse hook doesn't get called when UWP desktop app is out of focus

I'm creating a desktop UWP app and I need to set a global low level mouse hook to detect and change the position of it when it is moved to certain locations of the screen.
It works fine while my app's window is in focus and the values get logged correctly to the output window(meaning that the hook is working correctly).
This UWP app isn't going to be on the store and will only be used on a Windows 10 desktop (1903+).
I've tried calling SetWindowsHookEx on a different thread (which didn't do anything).
Tried also passing a thread ID when calling SetWindowsHookEx to no avail.
Also tried using the following restricted capabilities to prevent the app to suspend when not on focus: extendedExecutionUnconstrained and extendedBackgroundTaskTime along with the PreventFromSuspending method shown here.
Another thing I tried was to set uiAccess to true on the app manifest, which also didn't work.
The global hook is supposed to work even when the app isn't in the foreground, but instead it just works when it has the active window focus.
#region Structures
[StructLayout(LayoutKind.Sequential)]
/* MSLLHOOKSTRUCT */
public struct NativeMouseLowLevelHook
{
public override string ToString()
{
return $"{nameof(Point)}: {Point}, {nameof(MouseData)}: {MouseData}, {nameof(Flags)}: {Flags}";
}
public NativePoint Point;
public int MouseData;
public int Flags;
public int Time;
public UIntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public class NativePoint
{
public override string ToString()
{
return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}";
}
public int X;
public int Y;
}
#endregion
#region Hook
public class ManagedMouseHook : SafeHandleZeroOrMinusOneIsInvalid
{
public static List<ManagedMouseHook> KnownHooks { get; set; } = new List<ManagedMouseHook>();
public HookProc HookImpl { get; set; }
public ManagedMouseHook() : base(true)
{
Hook();
}
private void Hook()
{
HookImpl = NativeHookCallback;
KnownHooks.Add(this);
using (var curProcess = Process.GetCurrentProcess())
using (var curModule = curProcess.MainModule)
{
DoHook(curModule);
}
}
private void DoHook(ProcessModule curModule)
{
SetHandle(SetWindowsHookEx(14 /*WH_MOUSE_LL*/, HookImpl, GetModuleHandle(curModule.ModuleName), 0));
}
private bool UnHook()
{
var result = UnhookWindowsHookEx(DangerousGetHandle());
KnownHooks.Remove(this);
HookImpl = null;
return result;
}
/* LowLevelMouseProc */
private IntPtr NativeHookCallback(int code, IntPtr wparam, IntPtr lparam)
{
if (code >= 0)
{
var info = (NativeMouseLowLevelHook) Marshal.PtrToStructure(lparam,
typeof(NativeMouseLowLevelHook));
Debug.WriteLine(info); //Output example: Point: X: 408, Y: 535, MouseData: 0, Flags: 0
return new IntPtr(-1);
}
return CallNextHookEx(IntPtr.Zero, code, wparam, lparam);
}
protected override bool ReleaseHandle()
{
return UnHook();
}
}
#endregion
#region Interop
public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, ulong dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
IntPtr lParam);
#endregion
Then, to start using it:
public ManagedMouseHook MouseHook { get; set; }
MouseHook/*property*/ = new ManagedMouseHook();
And to unhook:
MouseHook/*property*/.Close();
UWP apps are running in the sandbox, so that doesn't sound strange. If you think a bit it is a security problem if the app can receive such an input and as 'Security' is listed as No.1 characteristic of the UWP this behavior is expected.
I've decided with the help of the comments that the best way to do this is to use my UWP app as a front-end and using a win32 app for the hooking functionality itself.

SetWindowsHookEx does not work in C# .net core?

I'm using the following code trying to get OS wide keyboard inputs with no luck:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
while(true)
continue;
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine(vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc 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);
}
HookCallback is simply not being called. I have a suspicion it's trying to listen only to a form which doesn't exist rather than running system wide.
Low-level Windows hooks internally use Windows messaging. The thread that calls SetWindowsHookEx must have the message loop in the end, which allows to call HookCallback function. In C++ message loop looks like this:
MSG msg;
BOOL result;
for (;;)
{
result = GetMessage(&msg, nullptr, 0, 0);
if (result <= 0)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Find all required PInvoke definitions for GetMessage, TranslateMessage, DispatchMessage and MSG, translate this code to C# and place it instead of your endless loop while(true). You can find all this stuff at PInvoke.Net, see also this Microsoft forum discussion:
Console keyboard hook not getting called
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ed5be22c-cef8-4615-a625-d05caf113afc/console-keyboard-hook-not-getting-called?forum=csharpgeneral
I'm obviously very late but just hoping I could help (if the OP haven't have yet the help he/she needed) so I'm posting my answer.
It says in the MSDN documentation that when you want to set a system-wide hook, you must give the hMod parameter a
handle to the DLL containing the hook procedure pointed to by the lpfn
parameter
and
If the dwThreadId parameter is zero or specifies the identifier of a
thread created by a different process, the lpfn parameter must point
to a hook procedure in a DLL
but, look at this:
SetWindowsHookEx(2, kbdHookProc, GetModuleHandle("user32"), 0)
kbdHookProc is a function in my C# winforms application but the value I gave in the hMod parameter is the hinstance obtained by loading user32.dll via GetModuleHandle. I am using the keyboard hook (WH_KEYBOARD) to monitor locking of capslock, numlock and scroll lock keys. Don't ask me why I did that and would it work or why it works because I don't know but, yes, IT WORKS!
For a full answer to this;
As Alex says, you'll need a message loop to process windows messages and call your hooks,
public class MessageLoop
{
[DllImport("user32.dll")]
private static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
uint wMsgFilterMax);
[DllImport("user32.dll")]
private static extern bool TranslateMessage([In] ref MSG lpMsg);
[DllImport("user32.dll")]
private static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
IntPtr hwnd;
uint message;
UIntPtr wParam;
IntPtr lParam;
int time;
POINT pt;
int lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
public static implicit operator System.Drawing.Point(POINT p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator POINT(System.Drawing.Point p)
{
return new POINT(p.X, p.Y);
}
public override string ToString()
{
return $"X: {X}, Y: {Y}";
}
}
private Action InitialAction { get; }
private Thread? Thread { get; set; }
public bool IsRunning { get; private set; }
public MessageLoop(Action initialAction)
{
InitialAction = initialAction;
}
public void Start()
{
IsRunning = true;
Thread = new Thread(() =>
{
InitialAction.Invoke();
while (IsRunning)
{
var result = GetMessage(out var message, IntPtr.Zero, 0, 0);
if (result <= 0)
{
Stop();
continue;
}
TranslateMessage(ref message);
DispatchMessage(ref message);
}
});
Thread.Start();
}
public void Stop()
{
IsRunning = false;
}
}
I use a separate thread here to avoid blocking the main thread.
The InterceptKeys class as shown in the question needs no modification;
class InterceptKeys
{
// ...
public static void Main()
{
var loop = new MessageLoop(() => {
_hookID = SetHook(_proc);
});
while (Console.ReadKey(true) != ConsoleKey.X) // For exemplary purposes
{
continue;
}
loop.Stop();
}
// ...
}

Sending scroll commands with SendInput on Windows10 UWP applications

I have code very similar to this question running in a windows tray application, even with this exact code from the question I get the same behavior. It all works well on classic windows applications, such as Firefox, Chrome, Windows Explorer etc. However when the mouse focus gets to a UWP app such as Edge or Calendar or Mail the scroll becomes jittery and after a few dozen scrolls performed my application hangs and can't even be terminated from task manager (permission denied), this behavior is very reproducible.
I'll paste the code from the question here:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace EnableMacScrolling
{
class InterceptMouse
{
const int INPUT_MOUSE = 0;
const int MOUSEEVENTF_WHEEL = 0x0800;
const int WH_MOUSE_LL = 14;
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
if (_hookID == null)
{
MessageBox.Show("SetWindowsHookEx Failed");
return;
}
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_MOUSEWHEEL == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.mouseData);
if (hookStruct.flags != -1) //prevents recursive call to self
{
INPUT input;
input = new INPUT();
input.type = INPUT_MOUSE;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
input.mi.time = 0;
input.mi.dwExtraInfo = 0;
input.mi.mouseData = -(hookStruct.mouseData >> 16);
try
{
SendInput(1, ref input, Marshal.SizeOf(input));
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
return (IntPtr)1;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private enum MouseMessages
{
WM_MOUSEWHEEL = 0x020A
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public int mouseData;
public int flags;
public int time;
public IntPtr dwExtraInfo;
}
public struct INPUT
{
public int type;
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public uint dwFlags;
public int time;
public int 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);
[DllImport("User32.dll", SetLastError = true)]
public static extern int SendInput(int nInputs, ref INPUT pInputs, int cbSize);
} }
Is it possible that I'm dealing with a bug in windows here? Any pointers to how I can find out what's going on?
Update:
I have created a test Win32 application in C++ to more easily reproduce/demonstrate the problem. The issue is with the SendCommand which when executed while any classic application is in focus works fine. However when executed while a UWP application is in focus, or even the windows launch/start menu causes the calling application (my application) to hang and be stuck until windows is restarted.
An effective workaround/solution for this problem is to perform the SendCommand call on another thread from the thread handling the hook callback. Immediately launching the thread that executes SendCommand and returning from the hook callback produces the desired behavior and doesn't cause any problems.
if (hookStruct.flags != -1) //prevents recursive call to self
That's rather important, yes. But how the statement could possibly do its job is very unclear. The expected value of this field is 0, 1 or 3. Never -1. You perhaps got mislead by another mouse hook that is active on your machine.
Now it does matter whether a WinRT app is in the foreground. Because then the message broker is involved and the field value changes, the LLMHF_LOWER_IL_INJECTED bit is turned on. WinRT apps run in a sandbox that run at a lower integrity level. So the field will not be -1 and your SendInput() call triggers the mouse hook again. On and on it goes, show is over when it runs out of stack.
So first possible fix is to use the field as intended, change the statement to:
if ((hookStruct.flags & 1) == 0)
Won't work if that presumed wonky mouse hook is corrupting the field, then consider using a static bool field in your class to break the recursion. Set it to true before the SendInput() call, false afterwards.
But I think I know why you are doing this, I too have been a victim of a trackpad that got it backwards. There is a much easier way to do it, just modify the FlipFlopWheel setting.

WinForms equivalent of WPF WindowInteropHelper, HwndSource, HwndSourceHook

I have a block of code like:
IntPtr hWnd = new WindowInteropHelper(this).Handle;
HwndSource source = HwndSource.FromHwnd(hWnd);
source.AddHook(new HwndSourceHook(WndProc));
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_CALL, IntPtr.Zero, IntPtr.Zero);
This was originally in a WPF application. However, I need to replicate the functionality in a WinForms application. Also, NativeMethods.PostMessage just maps to user32.dll PostMessage:
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
Are there a 1 to 1 equivalents of WindowInteropHelper/HwndSource/HwndSourceHook that I can use in my WinForms applications?
The basic point is: you don't need anything except AddHook from your source. Each WinForm has a method GetHandle() which will give you the handle of the Window/Form (and you found PostMessage already by yourself).
Too translate AddHook you either write your own class implementing IMessageFilter (1) or you override WndProc() (2).
(1) will receive messages application-wide, regardless to which form you send them while (2) only receives messages for the specific form overriding the method.
I could'nt find anything regarding WM_CALL, as you have to specify the window message as an integer (usually in hex), so this is up to you.
(1):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
//private const int WM_xxx = 0x0;
//you have to know for which event you wanna register
public Form1()
{
InitializeComponent();
IntPtr hWnd = this.Handle;
Application.AddMessageFilter(new MyMessageFilter());
PostMessage(hWnd, WM_xxx, IntPtr.Zero, IntPtr.Zero);
}
}
class MyMessageFilter : IMessageFilter
{
//private const int WM_xxx = 0x0;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_xxx)
{
//code to handle the message
}
return false;
}
}
(2):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form 1 {
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
//private const int WM_xxx = 0x0;
//you have to know for which event you wanna register
public Form1()
{
InitializeComponent();
IntPtr hWnd = this.Handle;
PostMessage(hWnd, WM_xxx, IntPtr.Zero, IntPtr.Zero);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WMK_xxx)
{
//code to handle the message
}
}
}
Am not more of WPF background. but for me it sounds like you're looking for NativeWindow.

Categories

Resources