Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

C#: Как эмулировать нажатие клавиш?

15K
04 мая 2007 года
Lonely Wolf
5 / / 23.02.2006
Здравствуйте, товарищи программисты!

Я начинающий программист, начал осваивать C# несколько месяцев назад (до этого программированием не занимался). Пишу для себя программу и столкнулся с проблемой. Суть следующая: программа должна работать в фоновом режиме, параллельно с игрой. Ее задача отлавливать клавиатурный ввод и соответствующим образом реагировать. Хук написанный на C#, отлавливающий клавиатурный ввод я нашел (ниже я его выложу), правда, я в нем мало что понял, но нашел фильтрующюю функцию. Реагировать на нажатия определенных клавишь на клавитуре, я тоже разобрался как. Но мне еще нужно модифицировать и послать обратно в систему это сообщение. Т.е. я например нажимаю на клаве одну клавишу, программа должна "нажать" ее несколько раз, или нажать какую-то последовательность клавишь. Функция SendKeys.Send() не помогает, игра не реагирует на нее.

У меня к вам две прозьбы:
1) Напишите мне пояснения к коду который я приложу ниже. Я прочитал пару статей про хуки, но толком ниче не понял. Вы мне просто напишите нормальным языком, какая строка что делает. Но это конечно по желанию)))

2) А главное, скажите что дописать к этому коду, чтоб эмулировать клавиатурный ввод? И тоже если можно с коментариями.

Заранее благодарю за внимание! Да, и это, не отсылайте меня на англоязычные ресурсы и к MSDN, я англ. не понимаю, а переводчик не особо помогает...

Вот код хука:
Код:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace myHook
{
    static 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;

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            _hookID = SetHook(_proc);
            Application.Run(new Form1());
            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)
            {
                //Обработчик хука
            }
            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)]
        [return: MarshalAs(UnmanagedType.Bool)]
        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);
    }
}
5
04 мая 2007 года
hardcase
4.5K / / 09.08.2005
Для эмуляции ввода в XP существует апишка под именем SendInput, но чтобы её использовать нужно повозиться с конвертированием стурктуры INPUT.
Потому предлагаю воспользоваться
 
Код:
VOID keybd_event(
    BYTE bVk,   // виртуальный код клавиши
    BYTE bScan,  
    DWORD dwFlags,
    PTR dwExtraInfo
);


Прототип на C# будет выглядеть примерно так:
 
Код:
[DllImport("user32.dll")]
public static extern void keybd_event(Keys bVk, byte bScan, UInt32 dwFlags, IntPtr dwExtraInfo);

public const UInt32 KEYEVENTF_EXTENDEDKEY = 1;
public const UInt32 KEYEVENTF_KEYUP = 2;


Пример на С иллюстрирует использование (переводить было лениво ;) для симуляции нажатия НумЛока.
Код:
void SetNumLock( BOOL bState )
   {
      BYTE keyState[256];

      GetKeyboardState((LPBYTE)&keyState);
      if( (bState && !(keyState[VK_NUMLOCK] & 1)) ||
          (!bState && (keyState[VK_NUMLOCK] & 1)) )
      {
      // Simulate a key press
         keybd_event( VK_NUMLOCK,
                      0x45,
                      KEYEVENTF_EXTENDEDKEY | 0,
                      0 );

      // Simulate a key release
         keybd_event( VK_NUMLOCK,
                      0x45,
                      KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
                      0);
      }
   }

KEYEVENTF_EXTENDEDKEY нужно указывать только если указываешь вторым параметром скан-код клавиши.
0 - это "нажатие" на клавишу
KEYEVENTF_KEYUP - это "отпускание" клавиши
15K
07 мая 2007 года
Lonely Wolf
5 / / 23.02.2006
Добрый день!

hardcase, огромное спасибо. Сработало, правда пример на С пришлось чуть модернизировать, но там я сразу понял что надо переписать. И че я раньше не обратился за помощью, 3 недели потерял...
27K
15 мая 2007 года
F.7
1 / / 23.03.2007
Lonely Wolf
А не мог бы ты опубликовать тут исходники с тем, что у тебя получилось?
15K
16 мая 2007 года
Lonely Wolf
5 / / 23.02.2006
Мог бы...
Вот код модуля (класса) программы, класс с формой выкладывать не стану. Пример демонстрирует нажатие с отпусканием клавиши "Caps Lock" при нажатии "Enter".
Код:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Primer_keybdEvent
{
    static 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;
        public const UInt32 KEYEVENTF_EXTENDEDKEY = 1;
        public const UInt32 KEYEVENTF_KEYUP = 2;

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            _hookID = SetHook(_proc);
            Application.Run(new Form1());
            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)
        {
            int vcCode = Marshal.ReadInt32(lParam);
            if ((nCode >= 0) && (wParam == (IntPtr)WM_KEYDOWN)
                && ((Keys)vcCode == Keys.Return))
            {
                keybd_event(Keys.CapsLock, 0x45, KEYEVENTF_EXTENDEDKEY | 0, (IntPtr)0);
                keybd_event(Keys.CapsLock, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (IntPtr)0);
                return (IntPtr)1;
            }
            return (IntPtr)0;
        }

        [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)]
        [return: MarshalAs(UnmanagedType.Bool)]
        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);

        [DllImport("user32.dll", EntryPoint = "keybd_event", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern void keybd_event(Keys bVk, byte bScan, UInt32 dwFlags, IntPtr dwExtraInfo);
    }
}


Кстати, функция keybd_event во многих играх не катит, т.к. ввод в играх осуществляется через InputDirect...так что прийдется ковыряться дальше.
15K
24 мая 2007 года
Lonely Wolf
5 / / 23.02.2006
Добрый день, это снова я и все с той же проблемой.

Как я уже писал, я пытаюсь написать программку под определенную игру. Но это игра взаимодействует с устройствами ввода через DirectInput. Ловить клавиатурный ввод через DirectInput я разобрался как, но проблема в том, что я опять не могу понять, как эмулировать нажатие клавиши через DirectInput. Перелопатил все книги по DirectX, которые нашел, но такой инфы нет. Такое впечатление, что это не возможно...

Может кто знает, как это сделать на C#?
18K
24 мая 2007 года
un_named
60 / / 24.04.2007
А чем тебя keybd_event не устраивает? hardcase уже это сказал (в самом начале второго поста).
http://www.firststeps.ru/mfc/winapi/keyb/r.php?36
15K
25 мая 2007 года
Lonely Wolf
5 / / 23.02.2006
Меня-то устраивает, но игра игнорирует сгенерированные нажатия через keybd_event. Я проверял на 2-х разных играх, одна реагирует адекватно, другая (именно та под которую я и пишу прогу) не реагирует вообще.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог