控制台应用程序中的全局热键

有谁知道如何在控制台应用程序中使用RegisterHotKey / UnregisterHotKey API调用? 我假设设置/删除热键是一样的,但是当按键被按下时,我怎样才能接通电话?

我看到的每个示例都是针对Winforms,并使用protected override void WndProc(ref Message m){...} ,这对我来说是不可用的。


更新:我有什么是下面,但事件是从来没有打。 我以为这可能是因为当你加载ConsoleShell它阻止进一步的执行,但即使我把SetupHotkey到另一个线程没有任何反应。 有什么想法吗?

 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); } } 

你可以做什么是在你的控制台应用程序中创build一个隐藏的窗口,用于处理热键通知和引发事件。

代码在这里certificate了原理。 这里是一篇关于在控制台应用程序中处理消息的文章,使用它你应该能够增强HotKeyManager在控制台应用程序中运行。

HotKeyManager的以下更新将创build一个后台线程,该后台线程运行消息循环并处理窗口消息。

 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 } } 

这是一个从控制台应用程序使用HotKeyManager的例子

 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!"); } } } 

我只是想提供一个替代解决scheme。

我正在回答一个正在使用这个脚本的人的问题,我想这可能会帮助其他人在设置全局密钥钩子时遇到困难。

在这里输入图像描述

 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 } } } 

我想出了一个基于Chris的答案的解决scheme,它使用WPF而不是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 } 

要使用这个,你需要添加对PresentationFramework.dll和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(); } }