2010-09-06 16 views
29

¿Alguien sabe cómo utilizar las llamadas de la API RegisterHotKey/UnregisterHotKey en una aplicación de consola? Supongo que la configuración/eliminación de la tecla de acceso directo es la misma, pero ¿cómo obtengo la devolución de llamada cuando se presiona la tecla?tecla de acceso rápido global en la aplicación de consola

Cada ejemplo que veo es para Winforms, y usa protected override void WndProc(ref Message m){...}, que no está disponible para mí.


actualización: lo que tengo está debajo, pero el evento nunca se golpea. Pensé que podría ser porque al cargar ConsoleShell bloquea la ejecución posterior, pero incluso si puse SetupHotkey en un hilo diferente, no ocurre nada. ¿Alguna idea?

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

Respuesta

50

Lo que puede hacer es Crear una ventana oculta en la aplicación de la Consola que se utiliza para manejar la notificación de teclas de acceso rápido y provocar un evento.

El código HERE muestra el principal. HERE es un artículo sobre el manejo de mensajes en una aplicación de la Consola, usando esto usted debería poder mejorar el HotKeyManager para ejecutar en una Aplicación de Consola.

La siguiente actualización de HotKeyManager crea un hilo de fondo que ejecuta el bucle de mensajes y maneja los mensajes de Windows.

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

Aquí es un ejemplo del uso HotKeyManager desde una aplicación de consola

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

he intentado muchas veces, y el evento nunca parece ser despedido. ¿Hay algo más que me estoy perdiendo? – Joe

+0

@joe, he agregado una actualización que incluye el HotKeyManager rediseñado para admitir aplicaciones de consola. –

+0

¡agradable! Funciona de maravilla. :) – Joe

5

Sólo quería ofrecer una solución alternativa.

Estaba respondiendo una pregunta para alguien que estaba usando este script y pensé que esto podría ayudar a alguien más que tiene problemas para configurar un enganche de llave global.

enter image description here

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

me ocurrió con una solución basada en la respuesta de Chris WPF que utiliza en lugar de Windows Forms:

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 
} 

Para utilizarlo, es necesario agregar referencias a PresentationFramework.dll y 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(); 
    } 
} 
0

Un cambio en la clase HotKeyManager

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

HotKeyEventArgs Clase:

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

y clase: HotKeyEventArgs

public partial class HotKeyEventArgs 
    { 
     [Flags] 
     public enum KeyModifiers 
     { 
      Alt = 1, 
      Control = 2, 
      Shift = 4, 
      Windows = 8, 
      NoRepeat = 0x4000 
     } 
    } 
Cuestiones relacionadas