Pull to refresh

Отключение ctrl-alt-delete, alt-tab, Пуск и прочих сочетаний

Reading time 5 min
Views 71K
Это вольный перевод статьи с сайта tamas.io о программном отключении и включении горячих клавиш Windows средствами C#. Совсем недавно мне самому понадобились подобные функции в моей программе и я с удивлением обнаружил, что в рунете ничего об этом нет, да и вообще в сети об этом сказано совсем немного. Так что, если интересно или также нужно, как было мне — добро пожаловать!

Я написал эту статью в 2007 году и она до сих пор ходит по сети и упоминается в различных обсуждениях, включая StackOverflow и DotNetSpider, так что я решил вновь опубликовать её. Это было актуально уже тогда и не было проверено на Windows 7, в силу того, что её на тот момент просто не существовало физически.
Перед тем, как продолжить чтение, прошу, обратите внимание на то, что эта статья вовсе не призывает вас создавать вредоносные приложения. Вы можете использовать приведённый код в целях обучения или для развлечения.

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

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

Используем возможности редактирования реестра C#, чтобы установить / изменить групповую политику для последовательности клавиш CTRL-ALT-DEL. Давайте посмотрим, что можно сделать не написав ни строчки кода. Откройте «Пуск -> Выполнить» и наберите gpedit.msc. Перейдите к: Конфигурация пользователя> Административные шаблоны> Система>Варианты действий после нажатия CTRL-ALT-DEL (User Configuration > Administrative Templates > System > CTRL+ALT+DELETE Options). Это то место, где стандартным способом можно установить действие, которое будет вызвано нажатием этого комбо. Дважды щёлкните по «Удалить диспетчер задач» (Remove Task Manager). Изменение этого параметра меняет значение в Software\Microsoft\Windows\CurrentVersion\Policies\System и значение DisableTaskMgr получит значение 1.

Теперь задача стала яснее.

Важно, не пропустите эту строку:

using Microsoft.Win32;


Метод, который я написал, выглядит следующим образом:

public void KillCtrlAltDelete()
        {
            RegistryKey regkey;
            string keyValueInt = "1";
            string subKey = @"Software\Microsoft\Windows\CurrentVersion\Policies\System";

            try
            {
                regkey = Registry.CurrentUser.CreateSubKey(subKey);
                regkey.SetValue("DisableTaskMgr", keyValueInt);
                regkey.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }


Таким образом, мы позаботились об изменении значения комбинации CTRL-ALT-DEL. Рассмотрим остальные сочетания. Возможно, вам могло показаться это сложным, но это проще простого. Как отключить ALT + F4? Всего 5 строк кода:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            base.OnClosing(e);
        }


Хорошо. Об остальном пришлось много читать и сейчас будет трудно назвать точно те статьи, которые мне помогли разобраться, но из всех было штук 15, которые содержали хоть какую-то полезную информацию. Я дам вам метод, который называется хук. Фрагмент кода использует LowLevelKeyboardProc, который:

Процедура LowLevelKeyboardProc — это программно определённая или библиотечно определённая процедура вызова функции, используемая совместно с функцией SetWindowsHookEx. Система вызывает эту функцию каждый раз, когда событие ввода с клавиатуры вот-вот будет размещено в очереди потока ввода. Ввод с клавиатуры может пройти через драйвер или через вызовы функции keybdevent. Если вызов произошёл через вызов keybdevent, то он был «введён» (injected). Тем не менее, хук WHKEYBOARDLL не внедряется в другой процесс. Вместо этого, контекст переключается на другой процесс, на который установлен хук и вызывается в первоначальном виде. Затем, контекст переключается обратно на приложение, которое вызвало это событие.


И снова, не забудьте:


using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Diagnostics;


Здесь всё остальное, что может ещё вам пригодиться:


[DllImport("user32", EntryPoint = "SetWindowsHookExA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int SetWindowsHookEx(int idHook, LowLevelKeyboardProcDelegate lpfn, int hMod, int dwThreadId);
        [DllImport("user32", EntryPoint = "UnhookWindowsHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int UnhookWindowsHookEx(int hHook);
        public delegate int LowLevelKeyboardProcDelegate(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        [DllImport("user32", EntryPoint = "CallNextHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int CallNextHookEx(int hHook, int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        public const int WH_KEYBOARD_LL = 13;

        /*code needed to disable start menu*/
        [DllImport("user32.dll")]
        private static extern int FindWindow(string className, string windowText);
        [DllImport("user32.dll")]
        private static extern int ShowWindow(int hwnd, int command);

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;
public struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        public static int intLLKey;

        public int LowLevelKeyboardProc(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam)
        {
            bool blnEat = false;

            switch (wParam)
            {
                case 256:
                case 257:
                case 260:
                case 261:
                    //Alt+Tab, Alt+Esc, Ctrl+Esc, Windows Key,
                    blnEat = ((lParam.vkCode == 9) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 0)) | ((lParam.vkCode == 91) && (lParam.flags == 1)) | ((lParam.vkCode == 92) && (lParam.flags == 1)) | ((lParam.vkCode == 73) && (lParam.flags == 0));
                    break;
            }

            if (blnEat == true)
            {
                return 1;
            }
            else
            {
                return CallNextHookEx(0, nCode, wParam, ref lParam);
            }
        }
public void KillStartMenu()
        {
            int hwnd = FindWindow("Shell_TrayWnd", "");
            ShowWindow(hwnd, SW_HIDE);
        }
private void Form1_Load(object sender, EventArgs e)
        {
            intLLKey = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(), 0);
        }

Совершенно очевидно, что вы можете программно вернуть все значения, вновь включив все сочетания:

public static void ShowStartMenu()
        {
            int hwnd = FindWindow("Shell_TrayWnd", "");
            ShowWindow(hwnd, SW_SHOW);
        }
public static void EnableCTRLALTDEL()
        {
            try
            {
                string subKey = @"Software\Microsoft\Windows\CurrentVersion\Policies\System";
                RegistryKey rk = Registry.CurrentUser;
                RegistryKey sk1 = rk.OpenSubKey(subKey);
                if (sk1 != null)
                    rk.DeleteSubKeyTree(subKey);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnhookWindowsHookEx(intLLKey);
        }


Я надеюсь, вам понравилась статья и вы нашли в ней что-то полезное. Я постарался собрать здесь всю информацию, которую только смог найти по этой теме.

От себя дополню, что в .NET4 код вида:


intLLKey = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(), 0);


Не работает. Нужно писать что-то типа:


var inst = LoadLibrary("user32.dll").ToInt32();
intLLKey = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, inst, 0);


Ну и в начало кода структуры AddHooks, среди прочего импорта сделать:


LoadLibrary:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr LoadLibrary(string lpFileName);


Надеюсь, перевод получился не совсем плох и окажется кому — либо полезен.
UPD: Ссылка на законченную рабочую программу.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+2
Comments 7
Comments Comments 7

Articles