Баги при работе с клавиатурными хуками

216
09 августа 2021, 13:50

Есть участок кода где перехватываются клавиши и блокируются:

public IntPtr LowLevelKeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
       var objKeyInfo = (KBDLLHOOKSTRUCT)System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
       if (objKeyInfo.key == Keys.RWin || objKeyInfo.key == Keys.LWin)
       {
          return (IntPtr)1; // <WinKey> 
       }
       else if (objKeyInfo.key == Keys.Alt || objKeyInfo.key == Keys.Tab)
       {
          return (IntPtr)1; // <Alt>+<Tab> 
       }
       else if (objKeyInfo.key == Keys.Alt || objKeyInfo.key == Keys.Space)
       {
          return (IntPtr)1; // <Alt>+<Space> 
       }
       else if (objKeyInfo.key == Keys.LControlKey || objKeyInfo.key == Keys.RControlKey || objKeyInfo.key == Keys.LShiftKey || objKeyInfo.key == Keys.RShiftKey || objKeyInfo.key == Keys.Escape)
       {
           return (IntPtr)1; // <Control>+<Shift>+<Escape> 
       }
       else if (objKeyInfo.key == Keys.LControlKey || objKeyInfo.key == Keys.Alt || objKeyInfo.key == Keys.Delete)
       {
          return (IntPtr)1; // <Control>+<Alt>+<Del> 
       }
    }
    return CallNextHookEx(this.m_hHook, nCode, wParam, lParam);
}

Почему при таком подходе не все клавиши блокируются?

В Данном случае не блокируется это сочетание клавиш: ( Не видит клавишу Alt )

else if (objKeyInfo.key == Keys.LControlKey || objKeyInfo.key == Keys.Alt || objKeyInfo.key == Keys.Delete)
{
   MessageBox.Show(objKeyInfo.key.ToString()); // Проверка какая клавиша нажата
   return (IntPtr)1; // <Control>+<Alt>+<Del> 
}

Просто для каждой блокировки клавиш выделять отдельный метод с отдельными клавишами для похукивания не айс.

Вызов хука - в классе SetHook():

public IntPtr m_hHook;
public void SetHook()
{
    m_callback = LowLevelKeyboardHookProc;
    m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, m_callback, GetModuleHandle(IntPtr.Zero), 0);
}

Попробовал так же сделать связку через: vkCode

int vkCode = Marshal.ReadInt32(lParam);
if (Keys.Control == Control.ModifierKeys || Keys.Delete == (Keys)vkCode || Keys.Alt == (Keys)vkCode)
{
   MessageBox.Show(objKeyInfo.key.ToString());
   return (IntPtr)1; // Ctrl + Alt + Delete
}

Просто Alt не хочет читать, подскажите почему такой баг возникает?

И есть ли решение данной проблемы?

[Новая проверка]

Пробую проверять так:

if ((objKeyInfo.flags & LLKHF_ALTDOWN) != 0 && vkCode == (uint)Keys.Control && vkCode == (uint)Keys.Delete)
{
  return (IntPtr)1;
}
// или даже так:
if (Keys.Control == Control.ModifierKeys && (objKeyInfo.flags & LLKHF_ALTDOWN) != 0 && vkCode == (uint)Keys.Delete)
{
   return (IntPtr)1;
}

Но всё ровно не блокируется именно это сочетание клавиш, хочу понять почему такое случается.

P.S: Система Windows 10 x64

Answer 1

Потому что вы не написали правильный код для блокировки этих сочетаний клавиш. Начать с того, что VK для нажатия клавиши ALT в хуке - на самом деле не Keys.Alt, а Keys.LMenu или Keys.RMenu. Но даже если поменять VK на правильный, все равно условие if (objKeyInfo.key == Keys.Alt || objKeyInfo.key == Keys.Tab) никак не может соответствовать "блокировать сочетание клавиш Alt+Tab" - вы блокируете вместо этого любые нажатия клавиш Alt и Tab!

Правильно это делается так:

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Forms;
namespace KeyboardHookSample
{
    class Program
    {
        static KeyHooker k;
        static void Main(string[] args)
        {
            k = new KeyHooker();
            k.Start();
            Console.CancelKeyPress += Console_CancelKeyPress;
            Console.WriteLine("Hook installed...");
            Application.Run();
        }
        private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            k.Stop();
            Application.Exit();
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public class KBDLLHOOKSTRUCT
    {
        public uint vkCode;
        public uint scanCode;
        public uint flags;
        public uint time;
        public UIntPtr dwExtraInfo;
    }    
    sealed class KeyHooker : IDisposable 
    {
        //объявления типов, констант и функций Windows API
        delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
        const int WH_KEYBOARD_LL = 13;
        const int HC_ACTION = 0;
        const int WM_KEYDOWN = 0x0100;
        const int WM_SYSKEYDOWN = 0x0104;        
        const uint LLKHF_ALTDOWN = 0x20;
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hInstance, int threadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string dllToLoad);
        IntPtr _hookID = IntPtr.Zero;
        //начинает отслеживание событий клавиатуры
        public void Start()
        {
            if (disposed) throw new ObjectDisposedException("KeyLogger");
            if (_hookID != IntPtr.Zero) return;
            _hookID = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, IntPtr.Zero, 0);
            if (_hookID == IntPtr.Zero)
            {
                int error = Marshal.GetLastWin32Error();
                throw new Win32Exception(error, "Failed to install hook! Error: " + error.ToString());
            }
        }
        //останавливает отслеживание событий клавиатуры
        public void Stop()
        {
            if (disposed) return;
            if (_hookID == IntPtr.Zero) return;
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
        }
        //вызывается Windows при нажатии клавиши
        IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
            {    
                KBDLLHOOKSTRUCT khs = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                uint vkCode = khs.vkCode;                                
                if ((khs.flags & LLKHF_ALTDOWN) != 0 && vkCode == (uint)Keys.Tab)
                {                    
                    return (IntPtr)1; //Cancel Alt+Tab
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
        bool disposed = false;
        public void Dispose()
        {
            if (disposed) return;
            Stop();
            disposed = true;
            GC.SuppressFinalize(this);
        }
        ~KeyHooker()
        {
            Dispose();
        }
    }
}

Также следует помнить, что блокировку системных сочетаний клавиш нужно применять с осторожностью. Если полноэкранное приложение блокирует сочетания клавиш Alt+Tab и Ctrl+Alt+Delete, то в случае его зависания система становится полностью неработоспособной - ничего не остается, кроме как перезагружать системник кнопкой Reset.

READ ALSO
Считывание большого файла

Считывание большого файла

Появилась проблема со считыванием большого файлаИндекс массива, в который записываются строки(array[i]), останавливается на 174 и всё

113
Получить папки из быстрого доступа проводника

Получить папки из быстрого доступа проводника

Можно как то получить папки из быстрого доступа проводника?

135
Преобразование newtonsoft.json.Linq.JToken в List&lt;string&gt;

Преобразование newtonsoft.json.Linq.JToken в List<string>

Не до конца понимаю, почему появляется ошибка

204
Выполнить команду по завершении потока

Выполнить команду по завершении потока

Вворачиваю в самопальный лаунчер для самопальных программ систему обновленияУ меня есть готовая процедура обновления, которая запускается...

107