Как реализовать глобальный KeyPress в C#?

Достаточно долгое время пользовался программой AutoHotKey для автоматизации достаточно большого количества действий, и хоть функционал программы достаточно, но все же не безграничен. В итоге я понял, то эта программа не единственное решение и пора двигаться дальше.
В результате на начал изучать С# и сейчас я пытаюсь перенести один проект на эту платформу, но не знаю как работать с WinApi, а если более конкретно, то как считать нажатие клавиш с клавиатуры вне формы. Не могли бы вы подсказать, как это реализуется?
  • Вопрос задан
  • 1835 просмотров
Решения вопроса 1
@dark_Kelvin
Если нужны горячие клавиши, то используйте RegisterHotKey / UnregisterHotKey
Если надо отлавливать события KeyDown KeyPress KeyUp, то используйте SetWindowsHookEx с параметром WH_KEYBOARD_LL / UnhookWindowsHookEx
private bool hooked = false;
		private IntPtr hHook;
		private HookProc managedDelegate = InternalCallback;
		
		public event KeyEventHandler KeyDown;
        public event KeyEventHandler KeyUp;
		public event KeyPressEventHandler KeyPress;

		public void StartHook()
        {
            IntPtr delegt = Marshal.GetFunctionPointerForDelegate(managedDelegate);
			hHook = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, delegt, getMainModuleHandle(), 0);
			if (hHook == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error());
		}
		
		public virtual void Unhook()
        {
			if (!UnhookWindowsHookEx(hHook)) throw new Win32Exception(Marshal.GetLastWin32Error());
		}
		
		protected int InternalCallback(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code >= 0)
            {
                bool handled = false;
                KeyboardHookStruct keyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
				
				if (KeyDown != null && ((int)wParam == WM_KEYDOWN || (int)wParam == WM_SYSKEYDOWN))
				{
					Keys keyData = (Keys)keyboardHookStruct.VirtualKeyCode;
					KeyEventArgs e = new KeyEventArgs(keyData);
					KeyDown.Invoke(null, e);
					handled = e.Handled;
				}
				
				if (KeyPress != null && (int)wParam == WM_KEYDOWN)
				{
					//Перекодирование сканкодов в Ascii
				}
				
				if (KeyUp != null && ((int)wParam == WM_KEYUP || (int)wParam == WM_SYSKEYUP))
				{
					Keys keyData = (Keys)keyboardHookStruct.VirtualKeyCode;
					KeyEventArgs e = new KeyEventArgs(keyData);
					KeyUp.Invoke(null, e);
					handled = handled || e.Handled;
				}
				
                if (handled) return 1;
            }
            return CallNextHookEx(hHook, code, wParam, lParam);
        }
		
		
		
		private IntPtr getMainModuleHandle()
        {
            IntPtr hMod;
            using (Process process = Process.GetCurrentProcess())
            using (ProcessModule module = process.MainModule)
            {
                hMod = GetModuleHandle(module.ModuleName);
            }
            return hMod;
        }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(HookType hook, IntPtr callback,
            IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", SetLastError = true)]
        internal static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll")]
        internal static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
            IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
		
		 private const int
         WM_KEYDOWN = 0x100,
         WM_KEYUP = 0x101,
         WM_SYSKEYDOWN = 0x104,
         WM_SYSKEYUP = 0x105;
		
        [StructLayout(LayoutKind.Sequential)]
        private struct KeyboardHookStruct
        {
            /// <summary>
            /// Specifies a virtual-key code. The code must be a value in the range 1 to 254. 
            /// </summary>
            public int VirtualKeyCode;
            /// <summary>
            /// Specifies a hardware scan code for the key. 
            /// </summary>
            public int ScanCode;
            /// <summary>
            /// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
            /// </summary>
            public int Flags;
            /// <summary>
            /// Specifies the Time stamp for this message.
            /// </summary>
            public int Time;
            /// <summary>
            /// Specifies extra information associated with the message. 
            /// </summary>
            public int ExtraInfo;
        }

		
		
	internal enum HookType : int
    {
        ///
        WH_JOURNALRECORD = 0,
        ///
        WH_JOURNALPLAYBACK = 1,
        ///
        WH_KEYBOARD = 2,
        ///
        WH_GETMESSAGE = 3,
        ///
        WH_CALLWNDPROC = 4,
        ///
        WH_CBT = 5,
        ///
        WH_SYSMSGFILTER = 6,
        ///
        WH_MOUSE = 7,
        ///
        WH_HARDWARE = 8,
        ///
        WH_DEBUG = 9,
        ///
        WH_SHELL = 10,
        ///
        WH_FOREGROUNDIDLE = 11,
        ///
        WH_CALLWNDPROCRET = 12,
        ///
        WH_KEYBOARD_LL = 13,
        ///
        WH_MOUSE_LL = 14
    }
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@bmforce
Комментировать
@APXAHGEL
Я вот этим проектом пользуюсь
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы