Функция SendInput обрабатывается в блокноте / блокноте, как если бы модификатор CTRL не работал

ИСТОРИЯ:

Я пишу 32-битное приложение WPF (C #), которое работает как экранная клавиатура. Он публикует выбранные нажатия клавиш в фокусированном окне, как если бы были нажаты физические клавиши, точно так же, как работает экранная клавиатура Microsoft, OSK.exe.

ПРОБЛЕМА:

Я успешно использовал библиотеку InputSimulator (здесь код: Класс InputSimulator, который создает INPUT array), но обнаружил, что определенные нажатия клавиш не распознаются Блокнотом, как ожидалось, например клавиши со стрелками вели себя так, как будто удерживалась клавиша CTRL. Точно так же клавиша WIN не работала должным образом, что также можно объяснить, если Windows обрабатывала ввод как Ctrl + Win.

ПОВТОРНОЕ РЕШЕНИЕ:

Я перенес источник InputSimulator в свой проект и внес некоторые изменения в то, как нажатия клавиш отправляются в SendInput, на основе вызовов SendInput, которые отправляет OSK.exe (захваченных с помощью API Monitor). Ключевые отличия, которые я наблюдал (и воспроизвел в моем коде) для KeyDown / KeyUp, таковы:

  • InputSimulator передает Keycode и флаг ExtendedKey (если ключ расширен), а также флаг KeyUp при отпускании клавиши.
  • OSK добавляет Scancode ключа и флаг ScanCode для большинства ключей.
  • OSK имеет и другие отличия; отдельные ключи, где KeyCode не передается вообще, где ScanCode не передается вообще, и различия в том, какие ключи требуют флаг ExtendedKey.

Результатом моего изменения кода для репликации того, как OSK вызывает SendInput, стало то, что еще больше клавиш теперь ведут себя так, как если бы мое целевое / сфокусированное приложение (обычно Блокнот или Wordpad) обнаруживало CTRL. Однако из прямого сравнения моего приложения и OSK в API Monitor я считаю, что мои вызовы SendInput идентичны вызовам OSK.

N.B. OSK безупречно работает на моем ноутбуке с Windows 8.1 (64-разрядная версия).

ИЗОЛИРОВАННОЕ ПРОБЛЕМНОЕ ПРОСТРАНСТВО:

Чтобы свести к минимуму проблемное пространство, я смоделировал одну комбинацию клавиши «вниз / вверх» для клавиши «S» из моего приложения на только что перезагруженном ПК (чтобы я мог быть уверен, что состояния нажатия клавиш не были загрязнены предыдущими запусками или физическими нажатиями клавиш. ). Целью был Блокнот, а затем WordPad - оба отреагировали, открыв диалоговое окно «Сохранить как», предполагая, что они интерпретировали мои нажатия клавиш как CTRL + S. API Monitor обнаружил только 2 вызова SendInput (KeyDown, а затем KeyUp), и эти вызовы соответствовали тому же эксперименту с использованием OSK. Вот журнал;

2014-12-10 21:29:54,650 Calling native method SendInput with params:
nInputs:1
pInputs[0]:
    Type:1(Keyboard)
    Data:
        MOUSEINPUT:
                X:2031699
                Y:8
                MouseData:0
                Flags:0 ()
                Time:0
        KEYBDINPUT:
                KeyCode:83(VK_S)
                Scan:31
                Flags:8 (KEYEVENTF_SCANCODE)
                Time:0
                ExtraInfo:0
        HARDWAREINPUT:
                Msg:2031699
                ParamL:8
                ParamH:0

cbSize:28

2014-12-10 21:29:54,651 Calling native method SendInput with params:
nInputs:1
pInputs[0]:
    Type:1(Keyboard)
    Data:
        MOUSEINPUT:
                X:2031699
                Y:10
                MouseData:0
                Flags:0 ()
                Time:0
        KEYBDINPUT:
                KeyCode:83(VK_S)
                Scan:31
                Flags:10 (KEYEVENTF_KEYUP | KEYEVENTF_SCANCODE)
                Time:0
                ExtraInfo:0
        HARDWAREINPUT:
                Msg:2031699
                ParamL:10
                ParamH:0

cbSize:28

Единственное заметное отличие состоит в том, что OSK передает параметр cbSize как 40, что я не могу подделать (вызов завершается неудачно, если я вручную передаю 40). Мой размер 28, что я получаю, пройдя ниже. Я понятия не имею, почему размеры различаются, поскольку определения моей структуры соответствуют документации MSDN (я не изменял их из исходного кода InputSimulator).

var cbSize = Marshal.SizeOf(typeof (INPUT));

ДРУГИЕ ПОПЫТКАЕМЫЕ ИСПРАВЛЕНИЯ:

  1. Я попытался оставить параметр VirtualCode пустым (0), когда указан ScanCode (и флаг ScanCode), но это не меняет результата. Идея отсюда: SO вопрос

  2. Я пробовал добавить завершающий вызов keybd_event, но это не меняет результата. Идея отсюда: ветка MSDN

    (keybd_event (0x41, 0, 0, 0);)

  3. Я попытался добавить засыпание потока между каждым вызовом SendInput, то есть задержку между вызовами KeyDown и KeyUp.

  4. Я изменил свои структуры и определения функций winapi, чтобы они соответствовали PINVOKE.net

  5. Я перекомпилировал свое приложение как 64-битное (на моей 64-битной машине) - это исправило cbSize до 40, но не изменило поведение.

Любая помощь будет очень признательна. Также есть предложения относительно других инструментов отладки, которые я мог бы использовать? Я попытался отладить все функции клавиатуры, которые может вызывать OSK (например, для обнаружения других вызовов keybd_event), но ни одна из них не регистрируется, кроме вызовов SendInput.


person Julius    schedule 11.12.2014    source источник
comment
Я не могу проверить это до сегодняшнего вечера, но выделяются 2 балла; Я использую 64-битную систему, и мой cbSize равен 28 (а не 40 OSK). Это привело меня к заголовку (stackoverflow.com/questions/15394487/) Вопрос SO, который предполагает наличие проблемы с размерами указателей, которые должны отличаться на 32- битовые и 64-битные машины. Это напрямую ссылается на этот (stackoverflow.com/questions/6830651/sendinput-and-64bits) вопрос SO , который включает потенциальное решение. Обновлю после тестирования.   -  person Julius    schedule 11.12.2014
comment
Изменение моих структурных определений и т. Д., Чтобы они соответствовали определениям PInvoke.net (pinvoke.net/default .aspx / Structures / INPUT.html) не изменил поведения. Компиляция моего приложения как 64-битного приложения (в моей 64-битной ОС) не изменила поведения, но изменила cbSize на 40.   -  person Julius    schedule 12.12.2014
comment
Используя API Monitor (64-битный) в моем 64-битном приложении, я вижу, что есть тонны вызовов GetKeyState, ToUnicodeEx, GetKeyboardLayout из clr.dll (которые я явно не делаю и, похоже, связаны с моими двумя вызовами для SendInput). После всего этого есть еще вызовы состояния GetKeyState и GetKeyboard из IMM32.dll и MSCTF.dll - что интересно, один из них проверяет состояние ключа VK_CONTROL, которое возвращается как -127. Я считаю, что это 1111111110000001 в двоичном формате, поэтому бит старшего разряда включен, что означает, что ключ не работает. ЗАЧЕМ????!!!   -  person Julius    schedule 12.12.2014


Ответы (1)


Правильно, значит, проблема была в PEBCAK.

Я пропустил самое очевидное, что только можно вообразить - сигнал запуска, который я использовал во время тестирования (то есть сигнал, говорящий, что я хочу нажать текущую фокусированную экранную клавишу), - это левая клавиша CTRL. Я нажимал клавишу CTRL, а затем удивлялся, почему Windows считает, что клавиша CTRL нажата. Стреляй в меня сейчас.

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

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

Некоторые этапы отладки, описанные выше, могут быть кому-то полезны; моя измененная версия кода InputSimulator работает нормально, как и исходный неизмененный код.

person Julius    schedule 11.12.2014