Почему некоторые приложения иногда не принимают некоторые ключи отправки

Это проблема, с которой я сталкивался раньше, но я всегда отказывался от решения проблемы и работал над ее решением. Не сегодня (надеюсь).

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

sendkeys.send("{ESC}")

Не повезло. Но потом случилось нечто странное. Я случайно запустил код, когда уже был в меню... и он закрыл меню (что нормально, если вы нажмете escape в меню). Так что ясно, что Doom II прислушивается к Sendkeys.

С тех пор я пробовал sendinput, postmessage и simulatorinput. Ни один из них не работал (все они ведут себя так же, как описано с помощью sendkeys).

Было бы здорово, если бы кто-нибудь подъехал на белой лошади и дал мне код, чтобы обойти эту проблему, но кроме этого, может ли кто-нибудь просто объяснить мне такое поведение?


person FraserOfSmeg    schedule 01.10.2016    source источник
comment
Ах, Doom II... Одна из старых классиков... Я очень люблю эту игру :). -- Не по теме, вы используете версию Doom II для DOS или версию для Windows 95? Потому что проблема может быть связана с эмулятором DOS, если вы его используете.   -  person Visual Vincent    schedule 01.10.2016
comment
@visualVincent, это действительно классика, просто позор, у меня так плохо получается! На самом деле я использую Zandronum. Я не могу понять, почему побег закроет меню, но не откроет его!   -  person FraserOfSmeg    schedule 01.10.2016
comment
Это хорошо, потому что я тоже использую Zandronum! Если бы вы сказали DOS, мне было бы сложнее помочь, потому что у меня есть только Doom95 и Zan. -- Я попробую и с SendKeys, и с SendInput. Вы работаете в полноэкранном режиме или в оконном режиме?   -  person Visual Vincent    schedule 01.10.2016
comment
@FraserOfSmeg В игре игра может проверять состояние клавиатуры, а не обрабатывать сообщения Windows.   -  person Andrew Morton    schedule 01.10.2016
comment
@AndrewMorton: Я думаю, что да, он работает на OpenGL или на классическом программном рендерере (вы можете выбрать рендерер самостоятельно). В любом случае SendInput должен работать.   -  person Visual Vincent    schedule 01.10.2016
comment
Хм, я понимаю, что вы имеете в виду. Как вы говорите, меню закрывается, но вы не можете его открыть... Странно, на самом деле. Кстати, я использовал WinAPI SendInput.   -  person Visual Vincent    schedule 01.10.2016
comment
@VisualVincent Если игра идет, эй, клавиатура! какие клавиши нажаты прямо сейчас? и не собираюсь, эй, моя очередь оконных сообщений! Есть что-нибудь для меня? Игнорируйте нажатия клавиш, тогда любое сообщение Windows о клавиатуре будет проигнорировано.   -  person Andrew Morton    schedule 01.10.2016
comment
@AndrewMorton: Но SendInput не использует сообщения Window. Согласно MSDN : Функция SendInput последовательно вставляет события из структур INPUT в поток ввода с клавиатуры или мыши. -- Опять же, это работает для закрытия меню, но не для его открытия.   -  person Visual Vincent    schedule 01.10.2016
comment
Хорошо, я, кажется, нашел решение этой проблемы... Позвольте мне написать свой ответ (написание может занять некоторое время, есть некоторая информация для объяснения).   -  person Visual Vincent    schedule 01.10.2016
comment
Извините, только что вернулся к своему компьютеру. Я запускаю в оконном режиме. Спасибо всем за поддержку, ребята! Сейчас я пойду спать, завтра вернусь за ответом @VisualVincent!   -  person FraserOfSmeg    schedule 01.10.2016


Ответы (1)


Похоже, что Zandronum не принимает виртуальные ключи для отправки ему, когда игра запущена (не поставлена ​​на паузу). Я не уверен, но кажется, что виртуальные клавиши могут быть оконными сообщениями, как сказал Эндрю Мортон (или они, по крайней мере, что-то похожее...). Обходной путь состоял в том, чтобы отправить аппаратный код сканирования вместо a код виртуального ключа .

аппаратный скан-код — это код, отправляемый реальной клавиатурой при нажатии клавиши, а виртуальный код клавиши — это клавиша, которую система интерпретирует из скан-кода ( ссылка).

Итак, мне удалось отправить нажатия клавиш в Zandronum (как в полноэкранном, так и в оконном режиме), используя несколько функций WinAPI:

  • SendInput(), который используется для отправки фактического ввода с клавиатуры.
  • MapVirtualKeyEx(), который используется для преобразования кодов клавиш в коды сканирования или наоборот.
  • GetKeyboardLayout(), который используется для получения текущей раскладки клавиатуры пользователя (например, у меня шведская клавиатура).

Используя приведенный ниже вспомогательный класс (или, правильнее сказать, оболочку), который я создал, вы теперь можете отправлять нажатия клавиш (аппаратные или нет) простым способом с большим набором клавиш, чем то, что включает SendKeys.Send(). Вы можете использовать любой ключ в System.Windows.Forms.Keys перечисление.

Это было протестировано с Zandronum и полностью работает:

InputHelper.Keyboard.PressKey(Keys.Escape, True) 'True = Send key as hardware scan code.

ИЗМЕНИТЬ (20 сентября 2019 г.)

InputHelper уже давно перемещен в собственную библиотеку. Ответ был обновлен, чтобы отразить это изменение.

Загрузите InputHelper с GitHub:
https://github.com/Visual-Vincent/InputHelper/releases

Ради интереса мне также удалось найти список кодов сканирования в MSDN: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx


Поскольку я сам фанат Doom и знаком с тем, как это работает, возможно, вам следует (согласно вашему старому вопросу) также убедиться, что вы выбрали New Game в меню, прежде чем нажать Enter?

Zandronum знает названия пунктов меню, поэтому вам просто нужно дать ему первую букву, и он перейдет к пункту, начинающемуся с нее:

InputHelper.Keyboard.PressKey(Keys.Escape, True) 'Open the menu.
System.Threading.Thread.Sleep(100)      'Small delay to let the menu open.
InputHelper.Keyboard.PressKey(Keys.N, True)      'Jump to the "New Game" menu item.
InputHelper.Keyboard.PressKey(Keys.Enter, True)  'Go into the "New Game" menu.
InputHelper.Keyboard.PressKey(Keys.Enter, True)  'Start a new game.

Я протестировал приведенный выше код в игре в полноэкранном режиме. Работает как шарм.

person Visual Vincent    schedule 01.10.2016
comment
Это не только то, что я искал, но и такая красивая и аккуратная реализация! Спасибо! - person FraserOfSmeg; 02.10.2016
comment
это то, что я искал! Идеально! Спасибо! - person FraserOfSmeg; 02.10.2016
comment
Я сказал: это не только... Я имел в виду, что вы дали мне код, который делает именно то, что я хочу, и это была отличная реализация! Кстати, знаете ли вы какой-нибудь способ получить координаты игрока в doom2? - person FraserOfSmeg; 02.10.2016
comment
@FraserOfSmeg: Ооооо... Я совершенно упустил это буквально каждый раз, когда читал комментарий. Ну тогда хорошо :). (Я удалил два своих предыдущих комментария, чтобы это было аккуратно) -- я не знаю, как получить координаты игрока из другого процесса, нет. Вам нужно прочитать память процесса Zandronum, чтобы получить это (что может быть довольно сложно, поскольку вы также должны найти где в памяти находятся координаты). Вот небольшое введение: codeproject.com/Articles /716227/ - person Visual Vincent; 02.10.2016
comment
Благодарю. На самом деле у меня есть собственный код для редактирования памяти от предыдущего игрового бота. Я просто надеялся, что может быть способ получить координаты в консоли (чтобы мне было легче найти адрес базовой памяти). Спасибо в любом случае! - person FraserOfSmeg; 02.10.2016
comment
@FraserOfSmeg: О, это действительно возможно. Введите idmypos 1 в консоли или просто idmypos на клавиатуре (без консоли). - person Visual Vincent; 02.10.2016
comment
@FraserOfSmeg: Кстати, см. этот другой мой ответ для примера того, как вы можете удерживать клавиши. Для Zandronum вам придется использовать SetHardwareKeyState() вместо SetKeyState(). - person Visual Vincent; 02.10.2016
comment
Я не мог понять, почему это не работает в KinghtOnline - person Murat Can OĞUZHAN; 17.08.2020