Использование User32.dll SendMessage для отправки ключей с модификатором ALT

Возможный дубликат:
C# и SendMessage (ключи) не работает

Я пишу приложение, которое отправляет нажатия клавиш другому приложению, используя функцию SendMessage, определенную в user32.dll. Я понял, как отправить одно нажатие клавиши, но я в тупике, пытаясь отправить нажатие клавиши вместе с клавишей ALT.

Для целей моего вопроса я сосредоточусь на отправке F1 и ALT + F1.

Как указано выше, я могу отправить клавишу F1 без проблем. Вот фрагмент моего кода, который отправляет клавишу F1:

// DLL Imports

//Set the active window
[DllImport("user32.dll")]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);

//sends a windows message to the specified window
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);

// ...

// Some constants
#define WM_SYSKEYDOWN 260
#define WM_SYSKEYUP 261
#define WM_CHAR 258
#define WM_KEYDOWN 256
#define WM_KEYUP 257

// ...

// activate the window and send F1
SetActiveWindow(hWnd);
ushort action = (ushort)WM_SYSKEYDOWN;
ushort key = (ushort)System.Windows.Forms.Keys.F1;
SendMessage(hWnd, action, key, 0);

Одно интересное замечание заключается в том, что хотя приведенный выше код работает при отправке клавиши F1 целевому приложению, он отличается от того, что я вижу при использовании Spy++. Вот вывод журнала Spy++ всякий раз, когда я нажимаю клавишу F1 во время мониторинга целевого приложения:

<00001> 00050412 P WM_KEYDOWN nVirtKey:VK_F1 cRepeat:1 ScanCode:3B fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00002> 00050412 P WM_KEYUP nVirtKey:VK_F1 cRepeat:1 ScanCode:3B fExtended:0 fAltDown:0 fRepeat:1 fUp:1

Обратите внимание, что отправлено два сообщения: WM_KEYDOWN и WM_KEYUP.

Мой первый вопрос: почему я успешно отправляю F1 с помощью WM_SYSKEYDOWN, когда Spy++ говорит мне, что WM_KEYDOWN + WM_KEYUP — это правильная последовательность сообщений?

Переходя к моей следующей задаче по отправке ALT + F1.

Я использовал Spy++ для мониторинга сообщений, передаваемых при нажатии ALT + F1 на клавиатуре, и вот что я вижу:

<00001> 00050412 P WM_SYSKEYDOWN nVirtKey:VK_MENU cRepeat:1 ScanCode:38 fExtended:1 fAltDown:1 fRepeat:0 fUp:0
<00002> 00050412 P WM_SYSKEYDOWN nVirtKey:VK_F1 cRepeat:1 ScanCode:3B fExtended:0 fAltDown:1 fRepeat:0 fUp:0
<00003> 00050412 P WM_SYSKEYUP nVirtKey:VK_F1 cRepeat:1 ScanCode:3B fExtended:0 fAltDown:1 fRepeat:1 fUp:1
<00004> 00050412 P WM_KEYUP nVirtKey:VK_MENU cRepeat:1 ScanCode:38 fExtended:1 fAltDown:0 fRepeat:1 fUp:1

Учитывая приведенный выше захват сообщения Spy++, я попытался отправить точную последовательность сообщений, используя следующий код (упрощенный):

SetActiveWindow(hWnd);    
SendMessage(hWnd, (ushort)WM_SYSKEYDOWN, (ushort)System.Windows.Forms.Keys.Menu, 0);
SendMessage(hWnd, (ushort)WM_SYSKEYDOWN, (ushort)System.Windows.Forms.Keys.F1, 0);
SendMessage(hWnd, (ushort)WM_SYSKEYUP, (ushort)System.Windows.Forms.Keys.F1, 0);
SendMessage(hWnd, (ushort)WM_KEYUP, (ushort)System.Windows.Forms.Keys.Menu, 0);

Это не сработало.

И это приводит к моему следующему вопросу. Есть ли что-нибудь еще, что я могу попробовать, или я что-то делаю не так?

Всякий раз, когда я перехватывал вывод своей программы с помощью Spy++, вот что было записано:

<00001> 00050412 S WM_SYSKEYDOWN nVirtKey:VK_MENU cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00002> 00050412 R WM_SYSKEYDOWN
<00003> 00050412 S WM_SYSKEYDOWN nVirtKey:VK_F1 cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00004> 00050412 R WM_SYSKEYDOWN
<00005> 00050412 S WM_SYSKEYUP nVirtKey:VK_F1 cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00006> 00050412 R WM_SYSKEYUP
<00007> 00050412 S WM_KEYUP nVirtKey:VK_MENU cRepeat:0 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00008> 00050412 R WM_KEYUP

Обратите внимание, что в строках 2, 4, 6 и 8 отправляются лишние сообщения. Может ли это быть причиной того, что что-то не работает?

У меня есть последний вопрос относительно разницы между сообщениями, полученными при фактическом вводе с клавиатуры, и сообщениями, полученными с помощью моего приложения. Обратите внимание на cRepeat, ScanCode, fExtended и т. д. аргументы. Они отличны от нуля в сообщениях, которые были захвачены с помощью моей клавиатуры в качестве ввода, и все они равны нулю в сообщениях, отправленных моим приложением. Может ли это быть причиной того, что мой код не работает? Если да, то как мне изменить эти значения? (я предполагаю, что они поступают из 4-го аргумента функции SendMessage , который я установил равным нулю во всех случаях.)

Спасибо,

Ян


person Jan Tacci    schedule 02.12.2012    source источник
comment
Дубликат, наверное. Но мой вопрос гораздо более сложный, чем тот, на который вы ссылаетесь, и я считаю, что это оправдывает его неудаление.   -  person Jan Tacci    schedule 02.12.2012
comment
SendMessage не является надежным для этой цели. Возможно, вы сможете заставить это работать в контролируемых условиях. (К сожалению, я не могу предоставить более подробную информацию — я работал над этим довольно давно.)   -  person jdigital    schedule 02.12.2012


Ответы (2)


Другое решение. Похоже, вы ничего не передаете для lparam WM_SYSKEYDOWN. Тем не менее документы ясно предполагают, что Бит 29 lparam должен быть установлен, чтобы указать, что клавиша ALT была нажата.

ushort action = (ushort)WM_SYSKEYDOWN;
ushort key = (ushort)System.Windows.Forms.Keys.F1;
uint lparam = (0x01 << 28);
SendMessage(hWnd, action, key, lparam);
person selbie    schedule 02.12.2012
comment
Очень красивое селби! Я на шаг ближе. Установка lParam и захват сообщений в Spy++ теперь показывают, что я правильно устанавливаю параметр fAltDown. Также обратите внимание, что я перешел с SendMessage на PostMessage, и это устранило лишние сообщения, отмеченные в моем посте. - person Jan Tacci; 02.12.2012
comment
Вещи приближаются, но все еще не работают. У меня такое ощущение, что мне также нужно установить другие параметры (cRepeat, ScanCode и т. д.). - person Jan Tacci; 02.12.2012
comment
Скорее всего. Вы можете использовать spy++ (поставляется с Visual Studio), чтобы определить большую часть того, что вам нужно отправить. - person selbie; 02.12.2012

Я не думаю, что SendMessage и WM_SYSKEYDOWN - это то, что вы хотите использовать. Вместо этого используйте функцию SendInput. Насколько я помню, вы, скорее всего, будете передавать четыре ввода для каждого нажатия клавиши, которое хотите имитировать. Один для события «вниз» клавиши alt. Еще одно событие down для соответствующей клавиши. Затем два события вверх для каждого.

Прошло некоторое время с тех пор, как мне приходилось писать такой код, но я считаю, что документы предполагают, что VK_MENU — это код для клавиши «alt».

person selbie    schedule 02.12.2012
comment
Разве SendInput не является совершенно другой библиотекой? И если я правильно помню, SendInput требует, чтобы целевое приложение было активным и находилось на переднем плане, чему мое приложение не должно препятствовать. (Я должен иметь возможность отправлять сообщения свернутым приложениям.) - person Jan Tacci; 02.12.2012
comment
Верно. Ожидается новый ответ. - person selbie; 02.12.2012