Простой хук клавиатуры

694
30 августа 2017, 13:26

Находила только сложные решения, может кто подскажет самое простое:

есть приложение, оно проигрывает видео, надо что бы приложение сворачивалось при любом нажатии клавиши клавиатуры, либо движении мыши, либо нажатии кнопки мыши. Как это отследить?

Answer 1

Если нужно отслеживать нажатия клавиш, движение и клики мышью именно глобально, то "просто" не получится, потому что данной функциональности в .NET из коробки попросту нету. Можно использовать WinAPI и глобальные системные хуки, и это, на самом деле, не так уж сложно, просто кода получается довольно таки не мало.

Мне приходилось недавно решать похожую задачу и получился примерно вот такой достаточно универсальный класс (всё лишнее я постаралась убрать, но кода всё равно прилично):

public sealed class GlobalHook : IDisposable
{
    // Импортируем необходимые функции WinAPI, объявляем нужные для них структуры и константы
    private static class WinAPI
    {
        public static class Kernel32
        {
            [DllImport("kernel32")]
            public static extern IntPtr LoadLibrary(string lpFileName);
        }
        public static class User32
        {
            // KBDLLHOOKSTRUCT
            // https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms644967(v=vs.85).aspx
            public struct KeyboardHookStruct
            {
                public uint VKCode;
                public uint ScanCode;
                public uint Flags;
                public uint Time;
                public IntPtr dwExtraInfo;
            }
            // MSLLHOOKSTRUCT
            // https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms644970(v=vs.85).aspx
            public struct MouseHookStruct
            {
                public int X;
                public int Y;
                public uint MouseData;
                public uint Flags;
                public uint Time;
                public IntPtr dwExtraInfo;
            }
            // Константы WH_*
            public enum WindowsHook : int
            {
                KeyboardLowLevel = 13,
                MouseLowLevel = 14,
            }
            // Константы WM_*
            public enum WindowsMessage : int
            {
                KeyDown = 0x100,
                KeyUp = 0x101,
                SysKeyDown = 0x104,
                SysKeyUp = 0x105,
                MouseMove = 0x200,
                LeftButtonDown = 0x201,
                LeftButtonUp = 0x202,
                RightButtonDown = 0x204,
                RightButtonUp = 0x205,
                MiddleButtonDown = 0x207,
                MiddleButtonUp = 0x208,
            }
            public delegate int KeyboardHookProc(int code,
                WindowsMessage wParam, ref KeyboardHookStruct lParam);
            public delegate int MouseHookProc(int code,
                WindowsMessage wParam, ref MouseHookStruct lParam);
            [DllImport("user32")]
            public static extern int CallNextHookEx(IntPtr hHk, int nCode,
                WindowsMessage wParam, ref KeyboardHookStruct lParam);
            [DllImport("user32")]
            public static extern int CallNextHookEx(IntPtr hHk, int nCode,
                WindowsMessage wParam, ref MouseHookStruct lParam);
            [DllImport("user32")]
            public static extern IntPtr SetWindowsHookEx(WindowsHook idHook,
                KeyboardHookProc lpfn, IntPtr hMod, uint dwThreadId);
            [DllImport("user32")]
            public static extern IntPtr SetWindowsHookEx(WindowsHook idHook,
                MouseHookProc lpfn, IntPtr hMod, uint dwThreadId);
            [DllImport("user32")]
            public static extern bool UnhookWindowsHookEx(IntPtr hHk);
        }
    }
    // Дескрипторы хуков
    private readonly IntPtr _keyboardHookHandle;
    private readonly IntPtr _mouseHookHandle;
    // Хуки
    private readonly WinAPI.User32.KeyboardHookProc _keyboardCallback;
    private readonly WinAPI.User32.MouseHookProc _mouseCallback;
    // События
    public event KeyEventHandler KeyDown = (s, e) => { };
    public event KeyEventHandler KeyUp = (s, e) => { };
    public event MouseEventHandler MouseButtonDown = (s, e) => { };
    public event MouseEventHandler MouseButtonUp = (s, e) => { };
    public event MouseEventHandler MouseMove = (s, e) => { };
    public GlobalHook()
    {
        // Создадим колбэки и сохраним их в полях класса, чтобы их не собрал GC
        _keyboardCallback = new WinAPI.User32.KeyboardHookProc((int code,
            WinAPI.User32.WindowsMessage wParam, ref WinAPI.User32.KeyboardHookStruct lParam) =>
        {
            // Если code < 0, мы не должны обрабатывать это сообщение системы
            if (code >= 0)
            {
                var key = (Keys)lParam.VKCode;
                var eventArgs = new KeyEventArgs(key);
                // В зависимости от типа пришедшего сообщения вызовем то или иное событие
                switch (wParam)
                {
                    case WinAPI.User32.WindowsMessage.KeyDown:
                    case WinAPI.User32.WindowsMessage.SysKeyDown:
                        KeyDown(this, eventArgs);
                        break;
                    case WinAPI.User32.WindowsMessage.KeyUp:
                    case WinAPI.User32.WindowsMessage.SysKeyUp:
                        KeyUp(this, eventArgs);
                        break;
                }
                // Если событие помечено приложением как обработанное,
                // прервём дальнейшее распространение сообщения
                if (eventArgs.Handled)
                    return 1;
            }
            // Вызовем следующий обработчик
            return WinAPI.User32.CallNextHookEx(_keyboardHookHandle, code, wParam, ref lParam);
        });
        _mouseCallback = new WinAPI.User32.MouseHookProc((int code,
            WinAPI.User32.WindowsMessage wParam, ref WinAPI.User32.MouseHookStruct lParam) =>
        {
            // Если code < 0, мы не должны обрабатывать это сообщение системы
            if (code >= 0)
            {
                // В зависимости от типа пришедшего сообщения вызовем то или иное событие
                switch (wParam)
                {
                    case WinAPI.User32.WindowsMessage.MouseMove:
                        MouseMove(this,
                            new MouseEventArgs(MouseButtons.None, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.LeftButtonDown:
                        MouseButtonDown(this,
                            new MouseEventArgs(MouseButtons.Left, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.RightButtonDown:
                        MouseButtonDown(this,
                            new MouseEventArgs(MouseButtons.Right, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.MiddleButtonDown:
                        MouseButtonDown(this,
                            new MouseEventArgs(MouseButtons.Middle, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.LeftButtonUp:
                        MouseButtonUp(this,
                            new MouseEventArgs(MouseButtons.Left, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.RightButtonUp:
                        MouseButtonUp(this,
                            new MouseEventArgs(MouseButtons.Right, 0, lParam.X, lParam.Y, 0));
                        break;
                    case WinAPI.User32.WindowsMessage.MiddleButtonUp:
                        MouseButtonUp(this,
                            new MouseEventArgs(MouseButtons.Middle, 0, lParam.X, lParam.Y, 0));
                        break;
                }
            }
            // Вызовем следующий обработчик
            return WinAPI.User32.CallNextHookEx(_mouseHookHandle, code, wParam, ref lParam);
        });
        // В SetWindowsHookEx следует передать дескриптор библиотеки user32.dll
        // Библиотека user32 всё равно всегда загружена в приложениях .NET,
        // хранить и освобождать дескриптор или что-либо ещё с ним делать нет необходимости
        IntPtr user32Handle = WinAPI.Kernel32.LoadLibrary("user32");
        // Установим хуки
        _keyboardHookHandle = WinAPI.User32.SetWindowsHookEx(
            WinAPI.User32.WindowsHook.KeyboardLowLevel, _keyboardCallback, user32Handle, 0);
        _mouseHookHandle = WinAPI.User32.SetWindowsHookEx(
            WinAPI.User32.WindowsHook.MouseLowLevel, _mouseCallback, user32Handle, 0);
    }
    #region IDisposable implementation
    private bool _isDisposed = false;
    public bool IsDisposed { get { return _isDisposed; } }
    public void Dispose()
    {
        if (_isDisposed)
            return;
        _isDisposed = true;
        // Удалим хуки
        WinAPI.User32.UnhookWindowsHookEx(_keyboardHookHandle);
        WinAPI.User32.UnhookWindowsHookEx(_mouseHookHandle);
    }
    ~GlobalHook()
    {
        Dispose();
    }
    #endregion
}

А использовать этот класс можно очень просто, например в форме:

public partial class MainForm : Form
{
    protected readonly GlobalHook hook = new GlobalHook();
    ...
    private void MainForm_Load(object sender, EventArgs e)
    {
        hook.KeyDown += (s, ev) => {
            // Так или иначе свернём приложение и т.д.,
            // при необходимости можно проверить, какая именно кнопка была нажата
            ...
        };
        // На hook.KeyUp, hook.MouseMove, hook.MouseButtonDown, hook.MouseButtonUp
        // можно повесить обработчики абсолютно точно так же
        // hook.MouseMove следует использовать осторожно,
        // оно вызывается достаточно большое число раз во время движения мыши
    }
}
READ ALSO
Real-time Debug Android Unity

Real-time Debug Android Unity

ADb настроил, как сделать, что бы при запуске игры в редакторе, она сразу же открывала ее в телефоне, тоесть нажал Play а юнити, и игра запустилась...

377
Свойство Filter и запись расширений видео файлов в него C#

Свойство Filter и запись расширений видео файлов в него C#

У openFileDialog есть свойство Filter, как прописать туда расширения только видео файлов?

359
Вопрос об upcast в c#

Вопрос об upcast в c#

Есть код:

369