Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 Отследить нажатия кнопок.  [new]
THe_bl
Member

Откуда:
Сообщений: 32
Как мне отследить событе нажатия кнопки на клавиатуре/мышке?
22 янв 09, 15:54    [6722311]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
Iktomy
Member

Откуда:
Сообщений: 325
Если в WinForms, то отслеживаем Events. Key и Mouse соответсвенно
22 янв 09, 16:10    [6722447]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
THe_bl
Member

Откуда:
Сообщений: 32
Это работает только если форма активна, а нужно, чтоб везде
22 янв 09, 16:36    [6722650]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
Iktomy
Member

Откуда:
Сообщений: 325
реализовать это при помощи ловушек (hooks).

Вот, кстати, наткнулся на похожую тему
22 янв 09, 17:11    [6722915]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
student-uni
Member

Откуда: Одесса
Сообщений: 1101
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace hook
{
    class Program
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

        static void Main()
        {
            _hookID = SetHook(_proc);
            Application.Run();
            UnhookWindowsHookEx(_hookID);

        }

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (wParam == (IntPtr)WM_KEYDOWN))
            {
                int vkCode = Marshal.ReadInt32(lParam);
                if (((Keys)vkCode == Keys.LWin) || ((Keys)vkCode == Keys.RWin))
                {
                    Console.WriteLine("{0} blocked!", (Keys)vkCode);
                    return (IntPtr)1;
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

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

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

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

    
    
    }
}





22 янв 09, 18:01    [6723294]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
THe_bl
Member

Откуда:
Сообщений: 32
Спасибо, код работает.
23 янв 09, 15:51    [6728000]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
Nenorma
Member

Откуда: Миасс
Сообщений: 26
возможно ли привести коментарии дл яприведенного выше кода?
3 мар 09, 18:31    [6883280]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
aleks-sam
Member

Откуда: Krasnoyarsk
Сообщений: 1842
Nenorma
возможно ли привести коментарии дл яприведенного выше кода?

Попробуйте хотяб в MSDN почитать, русифицированно все давно уже, если вдруг какие сложности в английском. Опыта наберетесь, если сами ковырять все будете.
3 мар 09, 18:38    [6883313]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
student-uni
Member

Откуда: Одесса
Сообщений: 1101
Хмм... Самому интересно :-)

Итак

Для реализации хука необходимы 3 метода из библиотеки user32.dll:

SetWindowsHookEx -установить хук
UnhookWindowsHookEx - снять хук
CallNextHookEx - передать инфу следующему приложению слушающему клавиатурные события

ключевым моментом является вызов функции SetWindowsHookEx
Ей необходимы 4 параметра

idHook - номер определяющий тип хука, в нашем случае - 13, что для удобства чтения
передано через константу WH_KEYBOARD_LL

private const int WH_KEYBOARD_LL = 13;

полный список можно взять здесь
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp

lpfn - указатель на функцию которая будет обрабатывать события клавиатуры

Таковой является в нашем случае HookCallback

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (wParam == (IntPtr)WM_KEYDOWN))
            {
                int vkCode = Marshal.ReadInt32(lParam);
                if (((Keys)vkCode == Keys.LWin) || ((Keys)vkCode == Keys.RWin))
                {
                    Console.WriteLine("{0} blocked!", (Keys)vkCode);
                    return (IntPtr)1;
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
Получить на нее указатель в C# можно через делегат с такой же сигнатурой.

private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

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

        private static LowLevelKeyboardProc _proc = HookCallback;

hMod - определят экземпляр приложения установившего хук, принимается как IntPtr.Zero, а далее заменяется на результат функции GetModuleHandle (), что однозначно определяет экземпляр приложения

            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }

dwThreadId - Ай Ди текущего потока, устанавливается в 0 для получения большего приоритета

И так еще раз:

1. Вызываем функции АПИ

        static void Main()
        {
            _hookID = SetHook(_proc); // установить хук
            Application.Run();
            UnhookWindowsHookEx(_hookID); // снять хук

        }
2. для установки хука необходим указатель на функцию обработчик, его достаем через делегат, не забываем вставить между статическую переменную для защиты от ГК

По сути дела после вызова
SetWindowsHookEx(WH_KEYBOARD_LL, proc ...
передача параметра proc говорит вызывать каждый раз при клавиатурном событии
функцию HookCallBack с уже заполненными входными параметрами

Осталось разобраться что делает HookCallBack с этими параметрами
(см. также http://msdn.microsoft.com/en-us/library/ms644985.aspx)

Он принимает 3 параметра

nCode если его значение < 0 , то сообщение не обрабатываем а пропускаем, иначе - обрабатываем

wParam - здесь определяется была ли клавиша нажата/отжата и была ли это системная клавиша

lParam - это указатель на структуру, содержащую информацию такую как напр. код нажатой клавиши

Структура имеет вид

typedef struct {
    DWORD vkCode;
    DWORD scanCode;
    DWORD flags;
    DWORD time;
    ULONG_PTR dwExtraInfo;
} KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
где vkCode и есть код клавиши.

Но нам система даст лишь указатель на эту структуру (адрес в памяти по которому она лежит)
Все что нам надо - считать одно (первое) "слово" DWORD по этому адресу.

С этим справится функция Marshal.ReadInt32
из пространства имен System.Runtime.InteropServices;

 int vkCode = Marshal.ReadInt32(lParam);


Вуаля, код клавиши у нас.

Проверяем не та ли эта клавиша которую хотим запретить
                if (((Keys)vkCode == Keys.LWin) || ((Keys)vkCode == Keys.RWin))
                {
                    Console.WriteLine("{0} blocked!", (Keys)vkCode);
                    return (IntPtr)1;
                }
и если "да" - выдаем сообщение.
4 мар 09, 11:10    [6884932]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
aleks-sam
Member

Откуда: Krasnoyarsk
Сообщений: 1842
student-uni
Хмм... Самому интересно :-)

Понесли сандали...
4 мар 09, 11:45    [6885131]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
Wehr
Member

Откуда:
Сообщений: 28
Скажите, как блокировать с помощью этого хука всю клавиатуру + мышь + горячие клавиши? Спасибо.
25 ноя 09, 23:59    [7979670]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
MafiaMedved
Member

Откуда:
Сообщений: 2
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)

непойму для чего эти строчки в программе?
В первой мы получаем дескриптор нашего процесса, во втором получаем модуль, а для чего это надо?
28 янв 10, 04:37    [8257270]     Ответить | Цитировать Сообщить модератору
 Re: Отследить нажатия кнопок.  [new]
MafiaMedved
Member

Откуда:
Сообщений: 2
MafiaMedved,

Все я понил зачем надо ))
28 янв 10, 04:46    [8257272]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: Отследить нажатия кнопок.  [new]
-=Loopback=-
Member

Откуда:
Сообщений: 51
Перенес вышеприведенный код в оконное приложение. Все работает, но во время работы HookCallback приложение подвисает на пару секунд. Причина не понятна. Может кто прояснит?
Вот сам код.
using System;
//using System.Collections.Generic;
using System.Diagnostics;
//using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ScreenShoter
{
    static class Program
    {

        #region  Установка хука на F12
        //====================================================================================
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        //private const int WM_KEYUP = 0x0101;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (wParam == (IntPtr)WM_KEYDOWN))
            {
                int vkCode = Marshal.ReadInt32(lParam);
                if (((Keys)vkCode == Keys.F12))
                {
                    //Shoter.GetScreenShot();
                    return (IntPtr)0;
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

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

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

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

        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //Application.EnableVisualStyles();
            //Application.SetCompatibleTextRenderingDefault(false);
            _hookID = SetHook(_proc);
            Application.Run(new MainForm());
            UnhookWindowsHookEx(_hookID);
            //UnhookWindowsHookEx(hook);
        }
    }
}
11 июл 11, 10:55    [10954286]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить