key capture using global hotkey in C# - c#

I have a application that runs in the background like i may keep my app in the system tray.If it remains on system tray my app will do it's job. Whenever a user press F10 or F9 some works will be done. i tried this:
public partial class Form1 : Form
{
public int a = 1;
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
const int MYACTION_HOTKEY_ID = 1;
public Form1()
{
InitializeComponent();
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F9);
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int)Keys.F10);
this.ShowInTaskbar = false;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID && (GetAsyncKeyState(Keys.F9) == -32767))
{
if ((a % 2) != 0)
{
a++;
MessageBox.Show(a.ToString()+"not equal F9");
label1.Text = "not equal F9";
}
if ((a % 2) == 0)
{
a++;
MessageBox.Show(a.ToString()+"equal F9");
label1.Text = " equal F9";
}
}
else if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID && (GetAsyncKeyState(Keys.F10) == -32767))
{
if ((a % 2) != 0)
{
a++;
MessageBox.Show(a.ToString() + "not equal F10");
label1.Text = "not equal F10";
}
if ((a % 2) == 0)
{
a++;
MessageBox.Show(a.ToString() + "equal F10");
label1.Text = " equal F10";
}
}
base.WndProc(ref m);
}
}
As i use set "this.ShowInTaskbar = false" this line it doesn't work.But if i don't set this it works fine.For my app i have to use this line.How can i solve this????

You need to subscribe to certain messages that the operating system sends by means of a native function call like RegisterHotKey(). When you call this function You tell the operating system which window to send the messages to by specifying the Handle of the window, this can be considered an address. When you set ShowInTaskbar = false the handle changes so the operating system will not know where to reach you.
See the first arugment:
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F9);
To resolve your issue you can create a class that derives from NativeWindow which "Provides a low-level encapsulation of a window handle and a window procedure." and from within that class (or at least using that class's handle depending on your implementation), register the hotkeys using a handle that will never change.
public sealed class HotkeyManager : NativeWindow, IDisposable
{
public HotkeyManager()
{
CreateHandle(new CreateParams());
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Constants.WM_HOTKEY)
{
//handle hotkey message
}
base.WndProc(ref m);
}
public void Dispose()
{
DestroyHandle();
}
}

As far as I know, you need to re-register the hotkey whenever you change the "ShowInTaskbar" state.
Someone else had a similar problem; see this thread.

Related

c# message filter keeps "leaking" first 2 keys pressed

I'm working on a small extension for autocad using its .net api.
I'm trying to filter keys using IMessageFilter. And it "sort of" works.
the problem is, its "leaking" first 2 keys I press through... and after that it starts working.
I'm not sure where the bug is occurring (tried posting this at autodesk forum to no avail).
as for the "ps2" , its an instance of autocads "paletteset" object which houses UserControls to fit into acad graphic interface.
I'd appreciate any idea as to why the only first 2 keys get leaked, or methods as to why this is occurring.
here's my code:
[CommandMethod("TestCheck")]
public void MyTab()
{
filter = new MyMessageFilter(ps1, ps2);
filter.OnNumber+=filter_OnNumber;
...
}
private bool keyboardFilterEnabled = false;
public bool KeyboardFilterEnabled
{
get { return keyboardFilterEnabled; }
set
{
keyboardFilterEnabled = value;
if (keyboardFilterEnabled == true)
System.Windows.Forms.Application.AddMessageFilter(filter);
else
System.Windows.Forms.Application.RemoveMessageFilter(filter);
}
}
}
public delegate void NotifyParentDelegate(Keys source);
public class MyMessageFilter : IMessageFilter
{
public MyMessageFilter(PaletteSet p1, PaletteSet p2)
: base()
{
PaletteSet ps1 = p1;
PaletteSet ps2 = p2;
}
public event NotifyParentDelegate OnNumber;
public event NotifyParentDelegate OnLetter;
public event NotifyParentDelegate OnControl;
public const int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
// Check for the Escape keypress
Keys kc = (Keys)(int)m.WParam & Keys.KeyCode;
if (m.Msg == WM_KEYDOWN && kc == Keys.Escape)
{
return false;
}
if (m.Msg == WM_KEYDOWN && char.IsDigit((char)kc) == true)
{
OnNumber(kc);
return false;
}
return true;
}
return false;
}
}
public void filter_OnNumber(Keys source)
{
ps2.Focus();
if (ps2.KeepFocus != true)
{ps2.KeepFocus=true; }
formDimensions.input_dimension.Focus();
//SendKeys.Send(source.ToString());
}

How to create a universal keyboard shortcut?

I have searched online for this, but can't find nothing at all.
What I would like to do is to create a keyboard shortcut that I would be able to use in all applications. A universal keyboard shortcut, so that when I press, say Ctrl+Shift+X in any application, it would execute a piece of code I created in C#. For example, when I'm in Skype, I would select text and press Ctrl+Shift+X (or whatever other key combination), it would change the color of the text from black to blue. That is just an example to try and explain what I want to do. I'm thinking I would have to import a DLL and edit that (maybe user32.dll?) I'm just guessing. I have no clue how to do this, so any help will be greatly appreciated!
Thanks a lot in advance :)
PS: I am using Windows Forms Application, .NET Framework 4.0. Unclear about something I am trying to do/say? Please feel free to comment and I will get back to you right away.
Win32 has a RegisterHotKey function as part of the Win32 API. To use it in managed code (C#), you'd have to pInvoke it. Here is an example:
public class WindowsShell
{
#region fields
public static int MOD_ALT = 0x1;
public static int MOD_CONTROL = 0x2;
public static int MOD_SHIFT = 0x4;
public static int MOD_WIN = 0x8;
public static int WM_HOTKEY = 0x312;
#endregion
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int keyId;
public static void RegisterHotKey(Form f, Keys key)
{
int modifiers = 0;
if ((key & Keys.Alt) == Keys.Alt)
modifiers = modifiers | WindowsShell.MOD_ALT;
if ((key & Keys.Control) == Keys.Control)
modifiers = modifiers | WindowsShell.MOD_CONTROL;
if ((key & Keys.Shift) == Keys.Shift)
modifiers = modifiers | WindowsShell.MOD_SHIFT;
Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;
keyId = f.GetHashCode(); // this should be a key unique ID, modify this if you want more than one hotkey
RegisterHotKey((IntPtr)f.Handle, keyId, (uint)modifiers, (uint)k);
}
private delegate void Func();
public static void UnregisterHotKey(Form f)
{
try
{
UnregisterHotKey(f.Handle, keyId); // modify this if you want more than one hotkey
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
public partial class Form1 : Form, IDisposable
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Keys k = Keys.A | Keys.Control;
WindowsShell.RegisterHotKey(this, k);
}
// CF Note: The WndProc is not present in the Compact Framework (as of vers. 3.5)! please derive from the MessageWindow class in order to handle WM_HOTKEY
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WindowsShell.WM_HOTKEY)
this.Visible = !this.Visible;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
WindowsShell.UnregisterHotKey(this);
}
}
This code came from this article. Read that article for more information and more examples.

Implementing Multi-letter Hotkey [duplicate]

How can I capture Ctrl + Alt + K + P keys on a C# form?
thanks
It is a chord, you cannot detect it without memorizing having seen the first keystroke of the chord. This works:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private bool prefixSeen;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (prefixSeen) {
if (keyData == (Keys.Alt | Keys.Control | Keys.P)) {
MessageBox.Show("Got it!");
}
prefixSeen = false;
return true;
}
if (keyData == (Keys.Alt | Keys.Control | Keys.K)) {
prefixSeen = true;
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
I'm not sure if you can. What you CAN do however, is the way Visual Studio does it.
It has shortcuts like Ctrl + K, C. You first press Ctrl+K, then hold down Ctrl and press C. In your case, you could check for Ctrl+Alt+K,P.
You can first check for only Ctrl+Alt+K as done in the other answers, then set a member variable/flag to indicate Ctrl+Alt+K has been pressed. In the same method where you check for K you can check for P, and if the flag you just set was set to true, do whatever you need to do. Otherwise set the flag back to false.
Rough pseudo-code:
private bool m_bCtrlAltKPressed = false;
public void KeyDown() {
if (Ctrl+Alt+K)
{
m_bCtrlAltKPressed = true;
}
else if (Ctrl+Alt+P && m_bCtrlAltKPressed) {
//do stuff
}
else {
m_bCtrlAltKPressed = false;
}
}
Hope that's clear enough.
MessageFilters can help you in this case.
public class KeystrokMessageFilter : System.Windows.Forms.IMessageFilter
{
public KeystrokMessageFilter() { }
#region Implementation of IMessageFilter
public bool PreFilterMessage(ref Message m)
{
if ((m.Msg == 256 /*0x0100*/))
{
switch (((int)m.WParam) | ((int)Control.ModifierKeys))
{
case (int)(Keys.Control | Keys.Alt | Keys.K):
MessageBox.Show("You pressed ctrl + alt + k");
break;
//This does not work. It seems you can only check single character along with CTRL and ALT.
//case (int)(Keys.Control | Keys.Alt | Keys.K | Keys.P):
// MessageBox.Show("You pressed ctrl + alt + k + p");
// break;
case (int)(Keys.Control | Keys.C): MessageBox.Show("You pressed ctrl+c");
break;
case (int)(Keys.Control | Keys.V): MessageBox.Show("You pressed ctrl+v");
break;
case (int)Keys.Up: MessageBox.Show("You pressed up");
break;
}
}
return false;
}
#endregion
}
Now in your C# WindowsForm, register the MessageFilter for capturing key-strokes and combinations.
public partial class Form1 : Form
{
KeystrokMessageFilter keyStrokeMessageFilter = new KeystrokMessageFilter();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Application.AddMessageFilter(keyStrokeMessageFilter);
}
}
Somehow it only detects Ctrl + Alt + K. Please Read the comment in MessageFilter code.
You can use GetKeyState to get the status of the other keys when one of the keys in the combination has been pressed. Here's an example on a form.
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DetectKeyChord
{
public partial class Form1 : Form
{
private const int WM_KEYDOWN = 0x100;
private const int KEY_PRESSED = 0x80;
public Form1()
{
InitializeComponent();
}
public void ShortcutAction()
{
MessageBox.Show("Ctrl+Alt+K+P has been pressed.");
}
private bool IsKeyDown(Keys key)
{
return (NativeMethods.GetKeyState(key) & KEY_PRESSED) == KEY_PRESSED;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
//If any of the keys in the chord have been pressed, check to see if
//the entire chord is down.
if (new[] { Keys.P, Keys.K, Keys.ControlKey, Keys.Menu }.Contains((Keys)m.WParam))
{
if (IsKeyDown(Keys.ControlKey) && IsKeyDown(Keys.Menu) && IsKeyDown(Keys.K) && IsKeyDown(Keys.P))
{
this.ShortcutAction();
}
}
}
base.WndProc(ref m);
}
}
public static class NativeMethods
{
[DllImport("USER32.dll")]
public static extern short GetKeyState(Keys nVirtKey);
}
}
See this great blog post about setting up hotkeys in c#
Christian Liensberger » Blog »
Installing a global hot key with
C#
There's also a good article that explains all of this here
Simple steps to enable Hotkey and ShortcutInput user control - CodeProject

Mouse wheel event to work with hovered control

In my C# 3.5 Windows Forms application, I have a few SplitContainers. There is a list control inside each (dock fill). When the focus is on one of these controls and I move mouse wheel, the list, which is now focused, is scrolled.
My task is to scroll the list, which is currently hovered by mouse, not the one which is selected. Is it possible in Windows Forms? If not, is it possible with PInvoke?
It looks like you can use the IMessageFilter and PInvoke to handle this. An example in VB can be found at Redirect Mouse Wheel Events to Unfocused Windows Forms Controls. You should be able to easily convert this to C#.
Points of Interest
This class uses the following techniques for the given task:
Listen to the control's MouseEnter and MouseLeave events to determine when the mouse pointer is over the control.
Implement IMessageFilter to catch WM_MOUSEWHEEL messages in the application.
PInvoke the Windows API call SendMessage redirecting the WM_MOUSEWHEEL message to the control's handle.
The IMessageFilter object is implemented as a singleton of the MouseWheelRedirector class and accessed by the shared members Attach, Detach, and Active.
Using a VB.NET to C# converter, this is what you end up with:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MouseWheelRedirector : IMessageFilter
{
private static MouseWheelRedirector instance = null;
private static bool _active = false;
public static bool Active
{
get { return _active; }
set
{
if (_active != value)
{
_active = value;
if (_active)
{
if (instance == null)
{
instance = new MouseWheelRedirector();
}
Application.AddMessageFilter(instance);
}
else
{
if (instance != null)
{
Application.RemoveMessageFilter(instance);
}
}
}
}
}
public static void Attach(Control control)
{
if (!_active)
Active = true;
control.MouseEnter += instance.ControlMouseEnter;
control.MouseLeave += instance.ControlMouseLeaveOrDisposed;
control.Disposed += instance.ControlMouseLeaveOrDisposed;
}
public static void Detach(Control control)
{
if (instance == null)
return;
control.MouseEnter -= instance.ControlMouseEnter;
control.MouseLeave -= instance.ControlMouseLeaveOrDisposed;
control.Disposed -= instance.ControlMouseLeaveOrDisposed;
if (object.ReferenceEquals(instance.currentControl, control))
instance.currentControl = null;
}
private MouseWheelRedirector()
{
}
private Control currentControl;
private void ControlMouseEnter(object sender, System.EventArgs e)
{
var control = (Control)sender;
if (!control.Focused)
{
currentControl = control;
}
else
{
currentControl = null;
}
}
private void ControlMouseLeaveOrDisposed(object sender, System.EventArgs e)
{
if (object.ReferenceEquals(currentControl, sender))
{
currentControl = null;
}
}
private const int WM_MOUSEWHEEL = 0x20a;
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (currentControl != null && m.Msg == WM_MOUSEWHEEL)
{
SendMessage(currentControl.Handle, m.Msg, m.WParam, m.LParam);
return true;
}
else
{
return false;
}
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr SendMessage(
IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}
I had similar question and found this thread... so posting my belated answer for others who might find this thread. In my case, I just want the mouse wheel events to go to whatever control is under the cursor... just like right-click does (it would be bewildering if right-click went to the focus control rather than the control under the cursor... I argue the same is true for the mouse wheel, except we've gotten used to it).
Anyway, the answer is super easy. Just add a PreFilterMessage to your application and have it redirect mouse wheel events to the control under the mouse:
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL: // 0x020A
case WM_MOUSEHWHEEL: // 0x020E
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
return false; // already headed for the right control
else
{
// redirect the message to the control under the mouse
SendMessage(hControlUnderMouse, m.Msg, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}
This is Brian Kennedy's answer completed with Hank Schultz comment:
First you should make a class implements IMessageFilter:
public class MessageFilter : IMessageFilter
{
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_MOUSEHWHEEL = 0x020E;
[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(Point p);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
{
//Do nothing because it's already headed for the right control
return false;
}
else
{
//Send the scroll message to the control under the mouse
uint u = Convert.ToUInt32(m.Msg);
SendMessage(hControlUnderMouse, u, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}
}
Example usage:
public partial class MyForm : Form
{
MessageFilter mf = null;
public MyForm
{
Load += MyForm_Load;
FormClosing += MyForm_FormClosing;
}
private void MyForm_Load(object sender, EventArgs e)
{
mf= new MessageFilter();
Application.AddMessageFilter(mf);
}
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Application.RemoveMessageFilter(mf);
}
}
Use Control.MouseEnter Event to set focus to to the control. E.g. using ActiveControl Property

How can I register a global hot key to say CTRL+SHIFT+(LETTER) using WPF and .NET 3.5?

I'm building an application in C# using WPF. How can I bind to some keys?
Also, how can I bind to the Windows key?
This is a full working solution, hope it helps...
Usage:
_hotKey = new HotKey(Key.F9, KeyModifier.Shift | KeyModifier.Win, OnHotKeyHandler);
...
private void OnHotKeyHandler(HotKey hotKey)
{
SystemHelper.SetScreenSaverRunning();
}
Class:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace UnManaged
{
public class HotKey : IDisposable
{
private static Dictionary<int, HotKey> _dictHotKeyToCalBackProc;
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
public const int WmHotKey = 0x0312;
private bool _disposed = false;
public Key Key { get; private set; }
public KeyModifier KeyModifiers { get; private set; }
public Action<HotKey> Action { get; private set; }
public int Id { get; set; }
// ******************************************************************
public HotKey(Key k, KeyModifier keyModifiers, Action<HotKey> action, bool register = true)
{
Key = k;
KeyModifiers = keyModifiers;
Action = action;
if (register)
{
Register();
}
}
// ******************************************************************
public bool Register()
{
int virtualKeyCode = KeyInterop.VirtualKeyFromKey(Key);
Id = virtualKeyCode + ((int)KeyModifiers * 0x10000);
bool result = RegisterHotKey(IntPtr.Zero, Id, (UInt32)KeyModifiers, (UInt32)virtualKeyCode);
if (_dictHotKeyToCalBackProc == null)
{
_dictHotKeyToCalBackProc = new Dictionary<int, HotKey>();
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
}
_dictHotKeyToCalBackProc.Add(Id, this);
Debug.Print(result.ToString() + ", " + Id + ", " + virtualKeyCode);
return result;
}
// ******************************************************************
public void Unregister()
{
HotKey hotKey;
if (_dictHotKeyToCalBackProc.TryGetValue(Id, out hotKey))
{
UnregisterHotKey(IntPtr.Zero, Id);
}
}
// ******************************************************************
private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
{
if (!handled)
{
if (msg.message == WmHotKey)
{
HotKey hotKey;
if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
{
if (hotKey.Action != null)
{
hotKey.Action.Invoke(hotKey);
}
handled = true;
}
}
}
}
// ******************************************************************
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// ******************************************************************
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be _disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be _disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
Unregister();
}
// Note disposing has been done.
_disposed = true;
}
}
}
// ******************************************************************
[Flags]
public enum KeyModifier
{
None = 0x0000,
Alt = 0x0001,
Ctrl = 0x0002,
NoRepeat = 0x4000,
Shift = 0x0004,
Win = 0x0008
}
// ******************************************************************
}
I'm not sure of what you mean by "global" here, but here it goes (I'm assuming you mean a command at the application level, for example, Save All that can be triggered from anywhere by Ctrl + Shift + S.)
You find the global UIElement of your choice, for example, the top level window which is the parent of all the controls where you need this binding. Due to "bubbling" of WPF events, events at child elements will bubble all the way up to the root of the control tree.
Now, first you need
to bind the Key-Combo with a Command using an InputBinding like this
you can then hookup the command to your handler (e.g. code that gets called by SaveAll) via a CommandBinding.
For the Windows Key, you use the right Key enumerated member, Key.LWin or Key.RWin
public WindowMain()
{
InitializeComponent();
// Bind Key
var ib = new InputBinding(
MyAppCommands.SaveAll,
new KeyGesture(Key.S, ModifierKeys.Shift | ModifierKeys.Control));
this.InputBindings.Add(ib);
// Bind handler
var cb = new CommandBinding( MyAppCommands.SaveAll);
cb.Executed += new ExecutedRoutedEventHandler( HandlerThatSavesEverthing );
this.CommandBindings.Add (cb );
}
private void HandlerThatSavesEverthing (object obSender, ExecutedRoutedEventArgs e)
{
// Do the Save All thing here.
}
Registering OS level shortcuts is hardly ever a good thing: users don't want you to mess with their OS.
That said, there is a much simpler and user friendly way of doing this in WPF, if you're ok with the hotkey working within the application only (i.e as long as your WPF app has the focus):
In App.xaml.cs :
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(Window), Window.PreviewKeyUpEvent, new KeyEventHandler(OnWindowKeyUp));
}
private void OnWindowKeyUp(object source, KeyEventArgs e))
{
//Do whatever you like with e.Key and Keyboard.Modifiers
}
It's that simple
If you're going to mix Win32 and WPF, here's how I did it:
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using System.Threading;
using System.Windows;
using System.Windows.Input;
namespace GlobalKeyboardHook
{
public class KeyboardHandler : IDisposable
{
public const int WM_HOTKEY = 0x0312;
public const int VIRTUALKEYCODE_FOR_CAPS_LOCK = 0x14;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private readonly Window _mainWindow;
WindowInteropHelper _host;
public KeyboardHandler(Window mainWindow)
{
_mainWindow = mainWindow;
_host = new WindowInteropHelper(_mainWindow);
SetupHotKey(_host.Handle);
ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage;
}
void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled)
{
if (msg.message == WM_HOTKEY)
{
//Handle hot key kere
}
}
private void SetupHotKey(IntPtr handle)
{
RegisterHotKey(handle, GetType().GetHashCode(), 0, VIRTUALKEYCODE_FOR_CAPS_LOCK);
}
public void Dispose()
{
UnregisterHotKey(_host.Handle, GetType().GetHashCode());
}
}
}
You can get the virtual-key code for the hotkey you want to register here: http://msdn.microsoft.com/en-us/library/ms927178.aspx
There may be a better way, but this is what I've got so far.
Cheers!
This is similar to the answers already given, but I find it a bit cleaner:
using System;
using System.Windows.Forms;
namespace GlobalHotkeyExampleForm
{
public partial class ExampleForm : Form
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
enum KeyModifier
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
WinKey = 8
}
public ExampleForm()
{
InitializeComponent();
int id = 0; // The id of the hotkey.
RegisterHotKey(this.Handle, id, (int)KeyModifier.Shift, Keys.A.GetHashCode()); // Register Shift + A as global hotkey.
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x0312)
{
/* Note that the three lines below are not needed if you only want to register one hotkey.
* The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); // The key of the hotkey that was pressed.
KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF); // The modifier of the hotkey that was pressed.
int id = m.WParam.ToInt32(); // The id of the hotkey that was pressed.
MessageBox.Show("Hotkey has been pressed!");
// do something
}
}
private void ExampleForm_FormClosing(object sender, FormClosingEventArgs e)
{
UnregisterHotKey(this.Handle, 0); // Unregister hotkey with id 0 before closing the form. You might want to call this more than once with different id values if you are planning to register more than one hotkey.
}
}
}
I've found it on fluxbytes.com.
With the NHotKey package, you can make your hotkey global:
https://github.com/thomaslevesque/NHotkey
https://thomaslevesque.com/2014/02/05/wpf-declare-global-hotkeys-in-xaml-with-nhotkey/ (use web.archive.org if the link is broken)
In short, for XAML, all you need to do is to replace
<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
by
<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
HotkeyManager.RegisterGlobalHotkey="True" />
I'm not sure about WPF, but this may help. I used the solution described in RegisterHotKey (user32) (modified to my needs of course) for a C# Windows Forms application to assign a CTRL-KEY combination within Windows to bring up a C# form, and it worked beautifully (even on Windows Vista). I hope it helps and good luck!
I've found the Global Hotkeys in WPF project on codeproject.com which does the job for me. It's relatively recent, does not need a reference to System.Windows.Forms and works "globally" in terms of reacting to the hotkey being pressed even if "your" application is not the active window.
Baboon's solution works best because you may have multiple windows. I did tweak it so it uses the PreviewKeyDownEvent instead of the PreviewKeyUpEvent in order to handle repetition in keystrokes.
I would advise against OS-level registration unless you are writing something like a snipping tool or an audio recording app as it will let you access functionality when the window is not focused.
Although RegisterHotKey is sometimes precisely what you want, in most cases you probably do not want to use system-wide hotkeys. I ended up using code like the following:
using System.Windows;
using System.Windows.Interop;
namespace WpfApp
{
public partial class MainWindow : Window
{
const int WM_KEYUP = 0x0101;
const int VK_RETURN = 0x0D;
const int VK_LEFT = 0x25;
public MainWindow()
{
this.InitializeComponent();
ComponentDispatcher.ThreadPreprocessMessage +=
ComponentDispatcher_ThreadPreprocessMessage;
}
void ComponentDispatcher_ThreadPreprocessMessage(
ref MSG msg, ref bool handled)
{
if (msg.message == WM_KEYUP)
{
if ((int)msg.wParam == VK_RETURN)
MessageBox.Show("RETURN was pressed");
if ((int)msg.wParam == VK_LEFT)
MessageBox.Show("LEFT was pressed");
}
}
}
}
RegisterHotKey() suggested by John could work - the only catch is that it requires an HWND (using PresentationSource.FromVisual(), and casting the result to an HwndSource).
However, you'll also need to respond to the WM_HOTKEY message - I'm not sure if there is a way to get access to the WndProc of a WPF window or not (which can be done for Windows Forms windows).

Categories