Any way to access mouse Raw Input from WPF? - wpf

I'm currently using a global mouse hook to make an app appear if it the mouse cursor reaches the corner of the screen. I just read about the existence of Raw Input and from what I understand, this is a more robust method as a slowdown in my hook will not impact the overall system.
Problem is I can't find any examples anywhere about using Raw Input in WPF.
Closest I got was SlimDX with the following code:
Device.RegisterDevice(UsagePage.Generic, UsageId.Mouse,
DeviceFlags.None);
Device.MouseInput += new EventHandler<MouseInputEventArgs>(mouse_MouseInput);
But that does not seem to work in WPF, only winforms.

Those DeviceFlags.None need to be InputSink to capture input in the background. SharpDX flags there are actually a wrapper for the RAWINPUTDEVICEFLAGS (InputSink = 0x00000100).
In WPF, you want to 1) override OnSourceInitialized and 2) hook WndProc there. While the window pointer is available you need to define the RAWINPUTDEVICE's you want to watch, flag InputSink.
It will look something like
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
var win = source.Handle;
RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[2];
rid[0].UsagePage = (HIDUsagePage)0x01;
rid[0].Usage = (HIDUsage)0x05; // adds game pad
rid[0].Flags = RawInputDeviceFlags.InputSink; // accept input in background
rid[0].WindowHandle = win;
rid[1].UsagePage = (HIDUsagePage)0x01;
rid[1].Usage = (HIDUsage)0x04; // adds joystick
rid[1].Flags = RawInputDeviceFlags.InputSink; // accept input in background
rid[1].WindowHandle = win;
if (RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])) == false)
{
var err = Marshal.GetLastWin32Error();
if (err > 0)
{
throw new Win32Exception($"GetLastWin32Error: {err}");
}
else
{
throw new Win32Exception("RegisterRawInputDevices failed with error code 0. Parameter count mis-match?");
}
}
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_INPUT:
{
System.Diagnostics.Debug.WriteLine("Received WndProc.WM_INPUT");
DoTheThing(lParam);
}
break;
}
return hwnd;
}

Related

WPF Ctrl+C Hotkey Registration Overrides/Stops Typical Copy Functionality

I have tried looking into this for a while now and have not been able to come up with an answer that has worked. I am trying to register my WPF C# application to listen for Ctrl+C from anywhere while the application is running. The code below is part of my application and does trigger when the user presses Ctrl+C, however, the contents of the Clipboard are stale (the contents are from a previous copy before the application was running). The AnimateWindow function is being called so I know my hotkey registration is working.
So, my issue is that it appears registering for the hotkey below is overriding the copy functionality. I would like to keep this functionality intact as it is part of how my application works. I need to take what was copied and use that data for a database query and to fill a TextBox.
This is just a snippet of the entire project, please let me know if additional example code is needed to help answer. Thank you!
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
// CTRL + C Hotkey
private const int COPY_ID = 9001;
private const uint MOD_CONTROL = 0x0002; // Modifier: Control
private const uint VK_C = 0x43; // 'C' key
/* Registering the hotkeys to be monitored */
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_windowHandle = new WindowInteropHelper(this).Handle;
_source = HwndSource.FromHwnd(_windowHandle);
_source.AddHook(HwndHook);
RegisterHotKey(_windowHandle, COPY_ID, MOD_CONTROL, VK_C); // 'C' key
}
/* The hook for pressing a hotkey */
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
switch (msg)
{
case WM_HOTKEY:
switch (wParam.ToInt32())
{
case COPY_ID:
AnimateWindow();
break;
}
}
}

C# WPF Auto Resize Window When Taskbar Height Changed

I have a WPF window which has these properties:
-ResizeMode=NoResize
-WindowStyle=None
I made every functionality of a normal window but i can't figure out how can i make window auto resize itself(when it's maximized) when taskbar's height changes. (Like Microsoft Visual Studio 2017 Window).
I can manually maximize my window but if I hide taskbar there is an empty space between my window and bottom of screen.
Is there any event fired when working area changes?
For your problem you can use SystemParameters.WorkArea.
Initially set the MaxHeight of your MainWindow.
MaxHeight="{Binding Height, Source={x:Static SystemParameters.WorkArea}}"
Register to the SystemParameters.StaticPropertyChanged in the codebehind of the MainWindow to receive changes and update your window size.
SystemParameters.StaticPropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(SystemParameters.WorkArea))
{
this.Dispatcher.Invoke(() =>
{
MaxHeight = SystemParameters.WorkArea.Height;
Height = SystemParameters.WorkArea.Height;
WindowState = WindowState.Normal; // Updates the windows new sizes
WindowState = WindowState.Maximized;
});
}
};
Handle the WM_GETMINMAXINFO message for your window, and do whatever re-sizing you need to do
public MainWindow()
{
InitializeComponent();
SourceInitialized += new EventHandler(win_SourceInitialized);
}
private void win_SourceInitialized(object sender, EventArgs e)
{
System.IntPtr handle = (new WinInterop.WindowInteropHelper(this)).Handle;
WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
}
private const int WM_GETMINMAXINFO = 0x0024;
private static System.IntPtr WindowProc(
System.IntPtr hwnd,
int msg,
System.IntPtr wParam,
System.IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case WM_GETMINMAXINFO: //https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-getminmaxinfo
WmGetMinMaxInfo(hwnd, lParam); // <------ Do what you need to here ---------->
handled = true;
break;
}
return (System.IntPtr)0;
}
Note that if you are a borderless, not-resizable window, you may also need to get the monitor info (via the Win32 GetMonitorInfo) and restrict your application to the work area of the monitor it is on. On our systems, windows does not correctly size the window for 1900x1200 monitors (it makes it too tall, so we have to set the MaxHeight based on the Monitor Info, and pay attention for that to change if the taskbar is resized by continuing to watch the WM_GETMINMAXINFO messages).
This blog can probably help with that if you have those issues as well:
https://blogs.msdn.microsoft.com/llobo/2006/08/01/maximizing-window-with-windowstylenone-considering-taskbar/

How to stop FilterMessage to proceed further?

I am looking forward to find a way to stop my message to proceed further or in other words to make it handled.
Say, cursor location is at textbox and I am scanning a barcode "#Save#". I recognized using WindowProc that this is barcode text and I handled Save text (by saving my form) but now I don't want to write this text("#Save#") in textbox.
So in short sentence I am looking for property or method to say a window this text (input) is handled, don't do any further action.
HwndSource source = (HwndSource)PresentationSource.FromDependencyObject(this);
source.AddHook(WindowProc);
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if(device == barcode)
{
......
.....
//Did some action
//tried to stop msg to follow further in loop, but not working
handled ==true;
}
return IntPtr.Zero;
}
Is that a typo, extra equals sign? Your code reads:
handled ==true;
that's not an assignment, although, I don't think that should even compile...
did you mean:
handled = true;

Subscribing to mouse events of all controls in form

How can i easily catch the "mouse down" events of all the controls in a form, without manually subscribing to each and every event? (C#)
Something like the "KeyPreview" feature, but for the mouse events.
I found this to be the best solution for my purposes.
Create a new class derived from IMessageFilter:
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// do something
((YourMainForm)Form.ActiveForm).YourMainForm_Click(null, null);
}
return false;
}
}
Then in your main form add this to register the message filter:
GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
And add this function to do whatever you have to, in your form:
public void YourMainForm_Click(object sender, EventArgs e)
{
// do anything here...
}
Solution 1
Subscribing to each and every event on every control within a form is certainly the most simplest approach to take, since you just use the code given by Ramesh.
However, another technique involves overriding the default windows message processing method ("WndProc") on the parent control - in this case, the form that contains all the controls.
This has a side effect that you won't be able to detect when the mouse cursor moves over controls contained inside another parent control.
For example, you won't be able to detect when the mouse cursor is over a TextBox that is contained inside a TabControl. This is because the TabControl will continue to process all mouse events.
Solution 2
The following solution will overcome all issues in attempting to detect which control the mouse cursor is over using a technique known as windows hooks.
Hooks essentially allow us to trap mouse and keyboard events even before they are dispatched to the window with focus.
Here's a sample:
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStruct
{
public POINT pt;
public int hwnd;
public int hitTestCode;
public int dwExtraInfo;
}
[DllImport("user32.dll", SetLastError = true)]
static extern int SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hInstance, uint dwThreadId);
[DllImport("user32.dll", SetLastError= true)]
static extern int CallNextHookEx(int hook, int code, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern int GetLastError();
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static int hHook;
public Form1()
{
InitializeComponent();
hHook = SetWindowsHookEx(HookType.WH_MOUSE, MouseHookProc, IntPtr.Zero, (uint)GetCurrentThreadId());
if (hHook == 0)
MessageBox.Show("GetLastError: " + GetLastError());
}
private int MouseHookProc(int code, IntPtr wParam, IntPtr lParam)
{
//Marshall the data from the callback.
MouseHookStruct mouseInfo = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (code < 0)
{
return CallNextHookEx(hHook, code, wParam, lParam);
}
else
{
//Create a string variable that shows the current mouse coordinates.
String strCaption = "x = " + mouseInfo.pt.X.ToString("d") +
" y = " + mouseInfo.pt.Y.ToString("d");
//You must get the active form because it is a static function.
Form tempForm = Form.ActiveForm;
Control c = Control.FromHandle((IntPtr)mouseInfo.hwnd);
if (c != null)
label1.Text = c.Name;
else
label1.Text = "Control not found";
//Set the caption of the form.
tempForm.Text = strCaption;
return CallNextHookEx(hHook, code, wParam, lParam);
}
}
Other conttrols in the form cannot listen to the Mouse event handlers of the form. Because each control got its own mouse event listners.
But You can subscribe each controls mouse events to the forms mouse events
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.label1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.ListBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this way you can have single handler for all the controls mouse events.

WPF Window Drag/Move Boundary

just curious if you know of any way to setup a drag boundary for a window?
It would be nice to have these properties:
Me.MinLeft = 10
Me.MinTop = 10
Me.MaxLeft = 150
Me.MaxTop = 150
Those are made up properties, btw, which would be nice to have.
I know I could probably setup a timer to fire ever 10th of a second and check the left and top and then move it back if it's over. But it would be more elegant to have the window act like it hit a wall and can't go any farther, like moving to the edge of the screen or something similar.
Edit: There seems to be some confusion somewhere, the point I'm trying to make is in the paragraph above, dragging, not re-sizing.
Here is teh "magic" you need to create this functionality, all you have to do is set the Window_SourceInitialized method to the window's SourceInitialized event and insert you logic where the big comment is.
I combined this code from several sources, so there could be some syntax errors in it.
internal enum WM
{
WINDOWPOSCHANGING = 0x0046,
}
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
private void Window_SourceInitialized(object sender, EventArgs ea)
{
HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
hwndSource.AddHook(DragHook);
}
private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
switch ((WM)msg)
{
case WM.WINDOWPOSCHANGING:
{
WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
if ((pos.flags & (int)SWP.NOMOVE) != 0)
{
return IntPtr.Zero;
}
Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
if (wnd == null)
{
return IntPtr.Zero;
}
bool changedPos = false;
// ***********************
// Here you check the values inside the pos structure
// if you want to override tehm just change the pos
// structure and set changedPos to true
// ***********************
if (!changedPos)
{
return IntPtr.Zero;
}
Marshal.StructureToPtr(pos, lParam, true);
handeled = true;
}
break;
}
return IntPtr.Zero;
}
As I have no doubt that Nir's answer will work spending a little time implementing it, I was able to do what I wanted a little bit more elegant with this code:
Private Sub myWindow_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
Dim primaryBounds As System.Drawing.Rectangle = Windows.Forms.Screen.PrimaryScreen.Bounds
Dim windowBounds As System.Drawing.Rectangle = New System.Drawing.Rectangle(CInt(Me.Left), CInt(Me.Top), CInt(Me.Width), CInt(Me.Height))
If (windowBounds.Left < 0) Then
windowBounds = New System.Drawing.Rectangle(0, windowBounds.Top, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Right > primaryBounds.Right) Then
windowBounds = New System.Drawing.Rectangle(primaryBounds.Right - windowBounds.Width, windowBounds.Top, windowBounds.Width, windowBounds.Height)
End If
If (windowBounds.Top < 0) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, 0, windowBounds.Width, windowBounds.Height)
ElseIf (windowBounds.Bottom > primaryBounds.Bottom) Then
windowBounds = New System.Drawing.Rectangle(windowBounds.Left, primaryBounds.Bottom - windowBounds.Height, windowBounds.Width, windowBounds.Height)
End If
Me.Left = windowBounds.Left
Me.Top = windowBounds.Top
End Sub
This made the window being dragged stay within the primary screen (whole window), but you could easily change the bounds to whatever values you needed.
There are dependency properties for WPF's Window for this purpose.
Here they are:
Window.MaxWidth
Window.MaxHeight
These properties will constrain the size of the Window, just like the WinForm's Form.
Maybe you could handle PreviewMouseMove (either the event or override the corresponding protected method) and set e.Handled = true whenever the mouse movement would cause the window to move outside the region you want to constrain it to.
This seems like the most logical, WPF-like way of doing this.

Resources