I've implemented the user32.dll register and unregister hot key methods, but after registering a hotkey, I never get the WndProc message 0x0312 when pressing the hotkey. Can someone review my code and help me understand why I never get the 0x0312 message.
The combination of hotkeys I've tried so far:
Ctrl + Shift + F12
F12
F9
My implementation is just the most common implementation:
[DllImport("c:\\windows\\system32\\user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("c:\\windows\\system32\\user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
protected override void WndProc(ref Message m) {
if(m.Msg == 0x0312) {
int id = m.WParam.ToInt32();
switch(id) {
case 0:
MessageBox.Show("Ctrl + Shift + F12 HotKey Pressed ! Do something here ... ");
break;
}
}
}
I created a singleton class to handle the registration and unregistration of hotkeys:
public class HotKeyHandler {
//Hotkey register and unregister.
[DllImport("c:\\windows\\system32\\user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("c:\\windows\\system32\\user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
public const int MOD_ALT = 0x0001;
public const int MOD_CONTROL = 0x0002;
public const int MOD_SHIFT = 0x0004;
public const int MOD_WIN = 0x0008;
byte ID = 0;
/// <summary>
/// Keep the constructor private due to singleton implementation
/// </summary>
private HotKeyHandler() { }
public static HotKeyHandler Instance = new HotKeyHandler();
public bool RegisterHotKey(IntPtr handle, int modifier, Key key) {
bool returnVal = RegisterHotKey(handle, ID, modifier, (int) key);
ID++;
return returnVal;
}
public void UnregisterAllHotKeys(IntPtr handle) {
for(short s = 0; s <= ID; s++) {
UnregisterHotKey(handle, s);
}
}
}
Finally I register the HotKey like this:
HotKeyHandler.Instance.RegisterHotKey(this.Handle, HotKeyHandler.MOD_CONTROL | HotKeyHandler.MOD_SHIFT, Key.F12);
I'd like to take the time to answer my own question just in case others might find themselves in the same.. rather irritating situation..
So after some digging and prodding I finally found out what was the problem, I was looking at the value for the Key ID that was passed to the RegisterHotKey method and noticed that the value I got did not match with the actual ID for the Key.
Turns out there exists two types of Key enums, there is the System.Windows.Input.Keyand the System.Windows.Forms.Keys. I was not aware of this and was using Input.Key which has different values from Forms.Keys
TL:DR
Use Forms.Keys for RegisterHotKey() and not Input.Key
In my form, I register different Hotkeys. Later during execution, I would like to know which of the Hotkeys was actually pressed. Where can I get that information from?
Registering during Initialization:
public Form1()
{
this.KeyPreview = true;
ghk = new KeyHandler(Keys.F1, this);
ghk.Register();
ghk = new KeyHandler(Keys.F2, this);
ghk.Register();
InitializeComponent();
}
Using this KeyHandler Class:
public class KeyHandler
{
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private int key;
private IntPtr hWnd;
private int id;
public KeyHandler(Keys key, Form form)
{
this.key = (int)key;
this.hWnd = form.Handle;
id = this.GetHashCode();
}
public override int GetHashCode()
{
return key ^ hWnd.ToInt32();
}
public bool Register()
{
return RegisterHotKey(hWnd, id, 0, key);
}
public bool Unregister()
{
return UnregisterHotKey(hWnd, id);
}
}
The method that is triggered:
protected override void WndProc(ref Message m)
{
if (m.Msg == Constants.WmHotkeyMsgId)
HandleHotkey(m);
base.WndProc(ref m);
}
Here I want to distinguish between two Hotkeys:
private void HandleHotkey(Message m)
{
if(key == F1)
DoSomething
if(key == F2)
DoSomethingElse
}
You should be able to know the actual key by using the id. When you register the hotkey, you use an id, a key and a modifier. When the hotkey is pressed, Windows gives you the id of the hotkey in the callback, not the key and modifiers.
RegisterHotKey(Handle, id: 1, ModifierKeys.Control, Keys.A);
RegisterHotKey(Handle, id: 2, ModifierKeys.Control | ModifierKeys.Alt, Keys.B);
const int WmHotKey = 786;
if (msg.message != WmHotKey)
return;
var id = (int)msg.wParam;
if (id == 1) // Ctrl + A
{
}
else if (id == 2) // Ctrl + Alt + B
{
}
Here's a blog post I've written with the code to register a hotkey for a WPF application:
https://www.meziantou.net/2012/06/28/hotkey-global-shortcuts
I have a C# application running on Windows CE 6.0 with CF 3.5, I think. The application starts minimized in the system tray with an icon. It's working so far but now I want to add context menu to this icon and I really don't get behind the secret with this context menu. The Icon is created with this code:
public event EventHandler Click;
private MyMessageWindow messageWindow;
private int uID = 5000;
private System.Drawing.Icon m_Icon;
public NotifyIcon()
{
messageWindow = new MyMessageWindow(this);
messageWindow.uID = uID;
}
public System.Drawing.Icon Icon
{
set { m_Icon = value; }
}
~NotifyIcon()
{
Remove();
}
public void Add(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, hIcon);
}
public void Add(string IconRes)
{
IntPtr hIcon = LoadIcon(GetModuleHandle(null), IconRes);
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, hIcon);
}
public void Add(System.Drawing.Icon icon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, icon.Handle);
}
public void Add()
{
if (m_Icon != null)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, m_Icon.Handle);
}
}
public void Remove()
{
NotifyMessage(messageWindow.Hwnd, NIM_DELETE, (uint) uID, IntPtr.Zero);
}
public void Modify(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_MODIFY, (uint) uID, hIcon);
}
private void NotifyMessage(IntPtr hwnd, int dwMessage, uint uID, IntPtr hIcon)
{
NOTIFYICONDATA notdata = new NOTIFYICONDATA();
notdata.cbSize = 152;
notdata.hIcon = hIcon;
notdata.hWnd = hwnd;
notdata.uCallbackMessage = WM_NOTIFY_TRAY;
notdata.uFlags = NIF_MESSAGE | NIF_ICON;
notdata.uID = uID;
int ret = Shell_NotifyIcon(dwMessage, ref notdata);
}
//Definition of the message.
private const int NIF_MESSAGE = 0x00000001;
private const int NIF_ICON = 0x00000002;
internal const int WM_LBUTTONDOWN = 0x0201;
internal const int NIM_ADD = 0x00000000;
internal const int NIM_MODIFY = 0x00000001;
internal const int NIM_DELETE = 0x00000002;
//Custom message
internal const int WM_NOTIFY_TRAY = 0x0400 + 2001;
internal struct NOTIFYICONDATA
{
internal int cbSize;
internal IntPtr hWnd;
internal uint uID;
internal uint uFlags;
internal uint uCallbackMessage;
internal IntPtr hIcon;
}
[DllImport("coredll.dll")]
internal static extern int Shell_NotifyIcon(
int dwMessage, ref NOTIFYICONDATA pnid);
[DllImport("coredll.dll")]
internal static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("coredll.dll")]
internal static extern int ShowWindow(
IntPtr hWnd,
int nCmdShow);
[DllImport("coredll.dll")]
internal static extern IntPtr GetFocus();
[DllImport("coredll.dll")]
internal static extern IntPtr LoadIcon(IntPtr hInst, string IconName);
[DllImport("coredll.dll")]
internal static extern IntPtr GetModuleHandle(String lpModuleName);
#endregion
#region MessageWindow
internal class MyMessageWindow : Microsoft.WindowsCE.Forms.MessageWindow
{
private int _uID = 0;
private NotifyIcon notifyIcon;
public MyMessageWindow(NotifyIcon notIcon)
{
notifyIcon = notIcon;
}
public int uID
{
set { _uID = value; }
}
protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message msg)
{
if (msg.Msg == WM_NOTIFY_TRAY)
{
if ((int) msg.LParam == WM_LBUTTONDOWN)
{
if ((int) msg.WParam == _uID)
{
if (notifyIcon.Click != null)
notifyIcon.Click(notifyIcon, null);
}
}
}
}
}
I know windows CE is not state of the art but I need to do this because of old hardware.
Does anyone know how to use the RegisterHotKey/UnregisterHotKey API calls in a console application? I assume that setting up/removing the hotkey is the same, but how do I get the call back when the key was pressed?
Every example I see is for Winforms, and uses protected override void WndProc(ref Message m){...}, which isn't available to me.
update: what I have is below, but the event is never hit. I thought it could be because when you load ConsoleShell it does block further execution, but even if I put SetupHotkey into a different thread nothing happens. Any thoughts?
class Program
{
static void Main(string[] args)
{
new Hud().Init(args);
}
}
class Hud
{
int keyHookId;
public void Init(string[] args)
{
SetupHotkey();
InitPowershell(args);
Cleanup();
}
private void Cleanup()
{
HotKeyManager.UnregisterHotKey(keyHookId);
}
private void SetupHotkey()
{
keyHookId = HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Control);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
}
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
//never executed
System.IO.File.WriteAllText("c:\\keyPressed.txt", "Hotkey pressed");
}
private static void InitPowershell(string[] args)
{
var config = RunspaceConfiguration.Create();
ConsoleShell.Start(config, "", "", args);
}
}
What you can do is Create a hidden window in your Console application which is used to handle the hotkey notification and raise an event.
The code HERE demonstrates the principal. HERE is an article on handling messages in a Console application, using this you should be able to enhance HotKeyManager to run in a Console Application.
The following update to the HotKeyManager creates a background thread which runs the message loop and handles the windows messages.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleHotKey
{
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)modifiers, (uint)key);
return id;
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hwnd, id, modifiers, key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
if (HotKeyManager.HotKeyPressed != null)
{
HotKeyManager.HotKeyPressed(null, e);
}
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
Thread messageLoop = new Thread(delegate()
{
Application.Run(new MessageWindow());
});
messageLoop.Name = "MessageLoopThread";
messageLoop.IsBackground = true;
messageLoop.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = this.Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
HotKeyManager.OnHotKeyPressed(e);
}
base.WndProc(ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError=true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
public class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
uint param = (uint)hotKeyParam.ToInt64();
Key = (Keys)((param & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)(param & 0x0000ffff);
}
}
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Here is an example of using HotKeyManager from a Console application
using System;
using System.Windows.Forms;
namespace ConsoleHotKey
{
class Program
{
static void Main(string[] args)
{
HotKeyManager.RegisterHotKey(Keys.A, KeyModifiers.Alt);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
Console.ReadLine();
}
static void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
Console.WriteLine("Hit me!");
}
}
}
I just wanted to offer an alternative solution.
I was answering a question for someone who was using this script and I figured this might help someone else who has trouble setting up a global key hook.
Edit: Don't forget to add a reference to System.Windows.Forms
You can do this by selecting Projectš¢Add Reference and checking System.Windows.Forms
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleKeyhook
{
class Hooky
{
///////////////////////////////////////////////////////////
//A bunch of DLL Imports to set a low level keyboard hook
///////////////////////////////////////////////////////////
[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);
////////////////////////////////////////////////////////////////
//Some constants to make handling our hook code easier to read
////////////////////////////////////////////////////////////////
private const int WH_KEYBOARD_LL = 13; //Type of Hook - Low Level Keyboard
private const int WM_KEYDOWN = 0x0100; //Value passed on KeyDown
private const int WM_KEYUP = 0x0101; //Value passed on KeyUp
private static LowLevelKeyboardProc _proc = HookCallback; //The function called when a key is pressed
private static IntPtr _hookID = IntPtr.Zero;
private static bool CONTROL_DOWN = false; //Bool to use as a flag for control key
public static void Main()
{
_hookID = SetHook(_proc); //Set our hook
Application.Run(); //Start a standard application method loop
}
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) //A Key was pressed down
{
int vkCode = Marshal.ReadInt32(lParam); //Get the keycode
string theKey = ((Keys)vkCode).ToString(); //Name of the key
Console.Write(theKey); //Display the name of the key
if (theKey.Contains("ControlKey")) //If they pressed control
{
CONTROL_DOWN = true; //Flag control as down
}
else if (CONTROL_DOWN && theKey == "B") //If they held CTRL and pressed B
{
Console.WriteLine("\n***HOTKEY PRESSED***"); //Our hotkey was pressed
}
else if (theKey == "Escape") //If they press escape
{
UnhookWindowsHookEx(_hookID); //Release our hook
Environment.Exit(0); //Exit our program
}
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
{
int vkCode = Marshal.ReadInt32(lParam); //Get Keycode
string theKey = ((Keys)vkCode).ToString(); //Get Key name
if (theKey.Contains("ControlKey")) //If they let go of control
{
CONTROL_DOWN = false; //Unflag control
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam); //Call the next hook
}
}
}
I came up with a solution based on Chris' answer that uses WPF instead of WinForms:
public sealed class GlobalHotkeyRegister : IGlobalHotkeyRegister, IDisposable
{
private const int WmHotkey = 0x0312;
private Application _app;
private readonly Dictionary<Hotkey, Action> _hotkeyActions;
public GlobalHotkeyRegister()
{
_hotkeyActions = new Dictionary<Hotkey, Action>();
var startupTcs = new TaskCompletionSource<object>();
Task.Run(() =>
{
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
_app = new Application();
_app.Startup += (s, e) => startupTcs.SetResult(null);
_app.Run();
});
startupTcs.Task.Wait();
}
public void Add(Hotkey hotkey, Action action)
{
_hotkeyActions.Add(hotkey, action);
var keyModifier = (int) hotkey.KeyModifier;
var key = KeyInterop.VirtualKeyFromKey(hotkey.Key);
_app.Dispatcher.Invoke(() =>
{
if (!RegisterHotKey(IntPtr.Zero, hotkey.GetHashCode(), keyModifier, key))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
public void Remove(Hotkey hotkey)
{
_hotkeyActions.Remove(hotkey);
_app.Dispatcher.Invoke(() =>
{
if (!UnregisterHotKey(IntPtr.Zero, hotkey.GetHashCode()))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
{
if (msg.message != WmHotkey)
return;
var key = KeyInterop.KeyFromVirtualKey(((int) msg.lParam >> 16) & 0xFFFF);
var keyModifier = (KeyModifier) ((int) msg.lParam & 0xFFFF);
var hotKey = new Hotkey(keyModifier, key);
_hotkeyActions[hotKey]();
}
public void Dispose()
{
_app.Dispatcher.InvokeShutdown();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}
public class Hotkey
{
public Hotkey(KeyModifier keyModifier, Key key)
{
KeyModifier = keyModifier;
Key = key;
}
public KeyModifier KeyModifier { get; }
public Key Key { get; }
#region ToString(), Equals() and GetHashcode() overrides
}
[Flags]
public enum KeyModifier
{
None = 0x0000,
Alt = 0x0001,
Ctrl = 0x0002,
Shift = 0x0004,
Win = 0x0008,
NoRepeat = 0x4000
}
To use this, you need to add references to PresentationFramework.dll and WindowsBase.dll.
public static void Main()
{
using (var hotkeyManager = new GlobalHotkeyManager())
{
var hotkey = new Hotkey(KeyModifier.Ctrl | KeyModifier.Alt, Key.S);
hotkeyManager.Add(hotkey, () => System.Console.WriteLine(hotkey));
System.Console.ReadKey();
}
}
Changed the HotKeyManager class
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, HotKeyEventArgs.KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, Interlocked.Increment(ref _id), (uint)modifiers, (uint)key);
return Interlocked.Increment(ref _id);
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
private delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
private delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hWnd: hwnd, id: id, fsModifiers: modifiers, vk: key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
HotKeyPressed?.Invoke(null, e);
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
new Thread(delegate ()
{
Application.Run(new MessageWindow());
})
{
Name = "MessageLoopThread",
IsBackground = true
}.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
var e = new HotKeyEventArgs(hotKeyParam: m.LParam);
OnHotKeyPressed(e);
}
base.WndProc(m: ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
Class HotKeyEventArgs:
public partial class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
Key = key;
Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
Key = (Keys)(((uint)hotKeyParam.ToInt64() & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)((uint)hotKeyParam.ToInt64() & 0x0000ffff);
}
}
And class: HotKeyEventArgs
public partial class HotKeyEventArgs
{
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Hey, I'm trying to write a program in C# that will track the pressing of certain keys (using a keyboard hook), and send different ones instead.
For instance, when I press the A key it will instead send the Q key.
I used http://www.codeproject.com/KB/cs/CSLLKeyboardHook.aspx this for my hooks and tried to use the SendKeys function, but I get an exception about the garbage collector destroying some object inside the hook class.
First you need to hook up the keys.
With this class you can register a global shortcut, I'm skipping the explanation, but you can read it here.
public class KeyboardHook
{
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
public enum Modifiers
{
None = 0x0000,
Alt = 0x0001,
Control = 0x0002,
Shift = 0x0004,
Win = 0x0008
}
int modifier;
int key;
IntPtr hWnd;
int id;
public KeyboardHook(int modifiers, Keys key, Form f)
{
this.modifier = modifiers;
this.key = (int)key;
this.hWnd = f.Handle;
id = this.GetHashCode();
}
public override int GetHashCode()
{
return modifier ^ key ^ hWnd.ToInt32();
}
public bool Register()
{
return RegisterHotKey(hWnd, id, modifier, key);
}
public bool Unregister()
{
return UnregisterHotKey(hWnd, id);
}
}
Then on your form you have to register the shortcut
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
KeyboardHook hook = new KeyboardHook((int)KeyboardHook.Modifiers.None, Keys.A, this);
hook.Register(); // registering globally that A will call a method
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312)
HandleHotkey(); // A, which was registered before, was pressed
base.WndProc(ref m);
}
private void HandleHotkey()
{
// instead of A send Q
KeyboardManager.PressKey(Keys.Q);
}
}
And here the class to manage Keyboard press and release events.
public class KeyboardManager
{
public const int INPUT_KEYBOARD = 1;
public const int KEYEVENTF_KEYUP = 0x0002;
public struct KEYDBINPUT
{
public Int16 wVk;
public Int16 wScan;
public Int32 dwFlags;
public Int32 time;
public Int32 dwExtraInfo;
public Int32 __filler1;
public Int32 __filler2;
}
public struct INPUT
{
public Int32 type;
public KEYDBINPUT ki;
}
[DllImport("user32")]
public static extern int SendInput(int cInputs, ref INPUT pInputs, int cbSize);
public static void HoldKey(Keys vk)
{
INPUT input = new INPUT();
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = 0;
input.ki.wVk = (Int16)vk;
SendInput(1, ref input, Marshal.SizeOf(input));
}
public static void ReleaseKey(Keys vk)
{
INPUT input = new INPUT();
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = KEYEVENTF_KEYUP;
input.ki.wVk = (Int16)vk;
SendInput(1, ref input, Marshal.SizeOf(input));
}
public static void PressKey(Keys vk)
{
HoldKey(vk);
ReleaseKey(vk);
}
}
I've tested it on this textarea which I'm writing to, when I pressed A it was sending Q.
I'm not sure what will be the behavior on Warcraft III, maybe they have blocked to prevent some kind of bot or something...
And when you look at your hook class what is the source of the problem? It sounds like a resource not being managed properly.
Realize that if you are planning on doing this as some sort of practical joke these never go over well because generally of the inability to turn these off. Also recognize that this type of seemingly unethical topic will not likely get much support.