Перехват клавиатуры Windows на C#

 

Один из разработчиков обучающей системы с модулем тестирования обратился ко мне с достаточно простой на первый взгляд задачей – как при запуске экзамена не допустить переключения приложения. При всей простоте, задача немного усложнилась тем, что комплекс был разработан на .NET Framework и подключать вставки native кода на C++ не очень хотелось. На самом деле язык C# на платформе Windows позволяет работать с WIN API. Осталось только привести пример того, как можно осуществить перехват нажатий на клавиши на чуть более низком уровне.

Собственно ниже пример перехвата клавиатуры на языке C#. Пример этот будет работать как на 32х так и на 64 битной Windows. Программа выводит в отладочную консоль нажатия на клавиши в любом приложении, а также производит блокировку нажатия Alt+Tab. Как вы уже понимаете, перехват клавиатуры ведется на уровне всех приложений. При желании можно даже перехватить Ctrl Alt Del но думаю что детали, тем кому это необходимо, выяснят самостоятельно.

 

using System;

using System.Diagnostics;

using System.Runtime.InteropServices;

namespace HookDemoApp

{

    internal class HookDemoHelper

    {

        private const int WH_KEYBOARD_LL = 13;

        private LowLevelKeyboardProcDelegate m_callback;

        private IntPtr m_hHook;

        [DllImport("user32.dll", SetLastError = true)]

        private static extern IntPtr SetWindowsHookEx(

            int idHook,

            LowLevelKeyboardProcDelegate lpfn,

            IntPtr hMod, int dwThreadId);

      [DllImport("user32.dll", SetLastError = true)]

        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("Kernel32.dll", SetLastError = true)]

        private static extern IntPtr GetModuleHandle(IntPtr lpModuleName);

        [DllImport("user32.dll", SetLastError = true)]

        private static extern IntPtr CallNextHookEx(

            IntPtr hhk,

            int nCode, IntPtr wParam, IntPtr lParam);

        private IntPtr LowLevelKeyboardHookProc(

            int nCode, IntPtr wParam, IntPtr lParam)

        {

            if (nCode < 0)

            {

                return CallNextHookEx(m_hHook, nCode, wParam, lParam);

            }

            else

            {

                var khs = (KeyboardHookStruct)

    Marshal.PtrToStructure(lParam,

                          typeof (KeyboardHookStruct));

               

                Debug.Print("Hook: Code: {0}, WParam: {1},{2},{3},{4} ",

                            nCode, wParam, lParam,

     khs.VirtualKeyCode,

                            khs.ScanCode, khs.Flags, khs.Time);

               

                Debug.Print(khs.VirtualKeyCode.ToString());

                if (khs.VirtualKeyCode == 9 &&

                    wParam.ToInt32() == 260 &&

                    khs.ScanCode == 15) //alt+tab

                {

                    System.Console.WriteLine("Alt+Tab pressed!");

                    IntPtr val=new IntPtr(1);

                    return val;                    

                }

                else

                {

                    return CallNextHookEx(m_hHook, nCode, wParam, lParam);

                }

           

            }

        }

        [StructLayout(LayoutKind.Sequential)]

        private struct KeyboardHookStruct

        {

            public readonly int VirtualKeyCode;

            public readonly int ScanCode;

            public readonly int Flags;

            public readonly int Time;

            public readonly IntPtr ExtraInfo;

        }

        private delegate IntPtr LowLevelKeyboardProcDelegate(

            int nCode, IntPtr wParam, IntPtr lParam);

        public void SetHook()

        {

            m_callback =LowLevelKeyboardHookProc;

            m_hHook =SetWindowsHookEx(WH_KEYBOARD_LL,

                m_callback,

                GetModuleHandle(IntPtr.Zero),0);

        }

        public void Unhook()

        {

            UnhookWindowsHookEx(m_hHook);

        }

    }

}