[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, Keys vk);
Declare Function RegisterHotKey Lib "user32.dll" (TODO) As TODO
None.
Do you know one? Please contribute it!
None.
Please add some!
[Flags]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
// Either WINDOWS key was held down. These keys are labeled with the Windows logo.
// Keyboard shortcuts that involve the WINDOWS key are reserved for use by the
// operating system.
Windows = 8
}
public class HotKeyRegister : IMessageFilter, IDisposable
{
/// <summary>
/// Define a system-wide hot key.
/// </summary>
/// <param name="hWnd">
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key. If this parameter is NULL, WM_HOTKEY messages are posted to the
/// message queue of the calling thread and must be processed in the message loop.
/// </param>
/// <param name="id">
/// The identifier of the hot key. If the hWnd parameter is NULL, then the hot
/// key is associated with the current thread rather than with a particular
/// window.
/// </param>
/// <param name="fsModifiers">
/// The keys that must be pressed in combination with the key specified by the
/// uVirtKey parameter in order to generate the WM_HOTKEY message. The fsModifiers
/// parameter can be a combination of the following values.
/// MOD_ALT 0x0001
/// MOD_CONTROL 0x0002
/// MOD_SHIFT 0x0004
/// MOD_WIN 0x0008
/// </param>
/// <param name="vk">The virtual-key code of the hot key.</param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id,
KeyModifiers fsModifiers, Keys vk);
/// <summary>
/// Frees a hot key previously registered by the calling thread.
/// </summary>
/// <param name="hWnd">
/// A handle to the window associated with the hot key to be freed. This parameter
/// should be NULL if the hot key is not associated with a window.
/// </param>
/// <param name="id">
/// The identifier of the hot key to be freed.
/// </param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
/// <summary>
/// Get the modifiers and key from the KeyData property of KeyEventArgs.
/// </summary>
/// <param name="keydata">
/// The KeyData property of KeyEventArgs. The KeyData is a key in combination
/// with modifiers.
/// </param>
/// <param name="key">The pressed key.</param>
public static KeyModifiers GetModifiers(Keys keydata, out Keys key)
{
key = keydata;
KeyModifiers modifers = KeyModifiers.None;
// Check whether the keydata contains the CTRL modifier key.
// The value of Keys.Control is 131072.
if ((keydata & Keys.Control) == Keys.Control)
{
modifers |= KeyModifiers.Control;
key = keydata ^ Keys.Control;
}
// Check whether the keydata contains the SHIFT modifier key.
// The value of Keys.Control is 65536.
if ((keydata & Keys.Shift) == Keys.Shift)
{
modifers |= KeyModifiers.Shift;
key = key ^ Keys.Shift;
}
// Check whether the keydata contains the ALT modifier key.
// The value of Keys.Control is 262144.
if ((keydata & Keys.Alt) == Keys.Alt)
{
modifers |= KeyModifiers.Alt;
key = key ^ Keys.Alt;
}
// Check whether a key other than SHIFT, CTRL or ALT (Menu) is pressed.
if (key == Keys.ShiftKey || key == Keys.ControlKey || key == Keys.Menu)
{
key = Keys.None;
}
return modifers;
}
/// <summary>
/// Specify whether this object is disposed.
/// </summary>
bool disposed = false;
/// <summary>
/// This constant could be found in WinUser.h if you installed Windows SDK.
/// Each windows message has an identifier, 0x0312 means that the mesage is
/// a WM_HOTKEY message.
/// </summary>
const int WM_HOTKEY = 0x0312;
/// <summary>
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key.
/// </summary>
public IntPtr Handle { get; private set; }
/// <summary>
/// A normal application can use any value between 0x0000 and 0xBFFF as the ID
/// but if you are writing a DLL, then you must use GlobalAddAtom to get a
/// unique identifier for your hot key.
/// </summary>
public int ID { get; private set; }
public KeyModifiers Modifiers { get; private set; }
public Keys Key { get; private set; }
/// <summary>
/// Raise an event when the hotkey is pressed.
/// </summary>
public event EventHandler HotKeyPressed;
public HotKeyRegister(IntPtr handle, int id, KeyModifiers modifiers, Keys key)
{
if (key == Keys.None || modifiers == KeyModifiers.None)
{
throw new ArgumentException("The key or modifiers could not be None.");
}
this.Handle = handle;
this.ID = id;
this.Modifiers = modifiers;
this.Key = key;
RegisterHotKey();
// Adds a message filter to monitor Windows messages as they are routed to
// their destinations.
Application.AddMessageFilter(this);
}
/// <summary>
/// Register the hotkey.
/// </summary>
private void RegisterHotKey()
{
bool isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation failed, try to unregister the hotkey if the thread
// has registered it before.
if (!isKeyRegisterd)
{
// IntPtr.Zero means the hotkey registered by the thread.
UnregisterHotKey(IntPtr.Zero, ID);
// Try to register the hotkey again.
isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation still failed, it means that the hotkey was already
// used in another thread or process.
if (!isKeyRegisterd)
{
throw new ApplicationException("The hotkey is in use");
}
}
}
/// <summary>
/// Filters out a message before it is dispatched.
/// </summary>
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public bool PreFilterMessage(ref Message m)
{
// The property WParam of Message is typically used to store small pieces
// of information. In this scenario, it stores the ID.
if (m.Msg == WM_HOTKEY
&& m.HWnd == this.Handle
&& m.WParam == (IntPtr)this.ID
&& HotKeyPressed != null)
{
// Raise the HotKeyPressed event if it is an WM_HOTKEY message.
HotKeyPressed(this, EventArgs.Empty);
// True to filter the message and stop it from being dispatched.
return true;
}
// Return false to allow the message to continue to the next filter or
// control.
return false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Unregister the hotkey.
/// </summary>
protected virtual void Dispose(bool disposing)
{
// Protect from being called multiple times.
if (disposed)
{
return;
}
if (disposing)
{
// Removes a message filter from the message pump of the application.
Application.RemoveMessageFilter(this);
UnregisterHotKey(Handle, ID);
}
disposed = true;
}
}
Here is a helper class for easily adding a hotkey to WPF Windows.
Usage:
/// <summary>
/// Id's to disambiguate multiple hotkey registrations
/// </summary>
uint hotKey1, hotKey2;
// --------------------------------------------------------------------------
/// <summary>
/// Once we have a window handle, register for hot keys
/// </summary>
// --------------------------------------------------------------------------
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_hotKeys = new HotKeyHelper(this, HandleHotKey);
hotKey1 = _hotKeys.ListenForHotKey(System.Windows.Forms.Keys.F8, HotKeyModifiers.Control);
hotKey2 = _hotKeys.ListenForHotKey(System.Windows.Forms.Keys.F9, HotKeyModifiers.WindowsKey | HotKeyModifiers.Shift);
}
// --------------------------------------------------------------------------
/// <summary>
/// Hotkey handler. The keyId is the return value from ListenForHotKey()
/// </summary>
// --------------------------------------------------------------------------
void HandleHotKey(int keyId)
{
if (keyId == hotKey1)
{
// first hotkey was pressed
}
else if (keyId == hotKey2)
{
// second hotkey was pressed
}
}
Helper class:
/// <summary>
/// Simpler way to expose key modifiers
/// </summary>
[Flags]
public enum HotKeyModifiers
{
Alt = 1, // MOD_ALT
Control = 2, // MOD_CONTROL
Shift = 4, // MOD_SHIFT
WindowsKey = 8, // MOD_WIN
}
// --------------------------------------------------------------------------
/// <summary>
/// A nice generic class to register multiple hotkeys for your app
/// </summary>
// --------------------------------------------------------------------------
public class HotKeyHelper : IDisposable
{
// Required interop declarations for working with hotkeys
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterHotKey(IntPtr hwnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
public static extern int UnregisterHotKey(IntPtr hwnd, int id);
[DllImport("kernel32", SetLastError = true)]
public static extern short GlobalAddAtom(string lpString);
[DllImport("kernel32", SetLastError = true)]
public static extern short GlobalDeleteAtom(short nAtom);
public const int WM_HOTKEY = 0x312;
/// <summary>
/// The unique ID to receive hotkey messages
/// </summary>
public short HotkeyID { get; private set; }
/// <summary>
/// Handle to the window listening to hotkeys
/// </summary>
private IntPtr _windowHandle;
/// <summary>
/// Callback for hot keys
/// </summary>
Action<int> _onHotKeyPressed;
// --------------------------------------------------------------------------
/// <summary>
/// ctor
/// </summary>
// --------------------------------------------------------------------------
public HotKeyHelper(Window handlerWindow, Action<int> hotKeyHandler)
{
_onHotKeyPressed = hotKeyHandler;
// Create a unique Id for this class in this instance
string atomName = Thread.CurrentThread.ManagedThreadId.ToString("X8") + this.GetType().FullName;
HotkeyID = GlobalAddAtom(atomName);
// Set up the hook to listen for hot keys
_windowHandle = new WindowInteropHelper(handlerWindow).Handle;
if(_windowHandle == null)
{
throw new ApplicationException("Cannot find window handle. Try calling this on or after OnSourceInitialized()");
}
var source = HwndSource.FromHwnd(_windowHandle);
source.AddHook(HwndHook);
}
// --------------------------------------------------------------------------
/// <summary>
/// Intermediate processing of hotkeys
/// </summary>
// --------------------------------------------------------------------------
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_HOTKEY && wParam.ToInt32() == HotkeyID)
{
_onHotKeyPressed?.Invoke(lParam.ToInt32());
handled = true;
}
return IntPtr.Zero;
}
// --------------------------------------------------------------------------
/// <summary>
/// Tell what key you want to listen for. Returns an id representing
/// this particular key combination. Use this in your handler to
/// disambiguate what key was pressed.
/// </summary>
// --------------------------------------------------------------------------
public uint ListenForHotKey(Keys key, HotKeyModifiers modifiers)
{
RegisterHotKey(_windowHandle, HotkeyID, (uint)modifiers, (uint)key);
return (uint)modifiers | (((uint)key) << 16);
}
// --------------------------------------------------------------------------
/// <summary>
/// Stop listening for hotkeys
/// </summary>
// --------------------------------------------------------------------------
private void StopListening()
{
if (this.HotkeyID != 0)
{
UnregisterHotKey(_windowHandle, HotkeyID);
// clean up the atom list
GlobalDeleteAtom(HotkeyID);
HotkeyID = 0;
}
}
// --------------------------------------------------------------------------
/// <summary>
/// Dispose
/// </summary>
// --------------------------------------------------------------------------
public void Dispose()
{
StopListening();
}
}