#delphi #hotkeys #sendinput #registerhotkey
#delphi #горячие клавиши #отправка ввода #registerhotkey
Вопрос:
Я пишу исполняемый файл Windows в Dephi 2007 (32-разрядная версия), который отправляет нажатия клавиш в другое приложение.
Нажатие горячей клавиши или комбинации горячих клавиш из другого приложения приводит к тому, что мое приложение отправляет нажатия клавиш этому приложению. Мой подход работает нормально, если комбинация горячих клавиш не содержит Ctrl: т.е. F11 или Shift F11 работает должным образом, но Ctrl F11 приводит к сбою моего первого вызова SendInput после обработки горячей клавиши в моем приложении.
Я пробовал свое приложение с несколькими различными целевыми приложениями и как в 64-разрядной Windows 7, так и в 32-разрядной Windows XP. Такая же схема сбоя возникает во всех случаях, которые я пробовал.
При обработке комбинации горячих клавиш приведенный ниже код пытается отправить две клавиши Shift Tab, за которыми следует клавиша Tab, в целевое приложение. Однако, когда Ctrl является частью комбинации горячих клавиш, первая Shift клавиша Tab никогда не отображается в целевом приложении. (Последующие клавиши отображаются просто отлично.)
Чего мне не хватает?
РЕДАКТИРОВАТЬ: я изменил приведенный ниже код (согласно моему комментарию Sertac) в разделе «РЕШЕНИЕ» … теперь он работает нормально, даже с Ctrl как частью последовательности горячих клавиш.
unit mainTEST;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus;
type
T_Mainform = class(TForm)
private
procedure WMHotkey(var msg: TWMHotkey); message WM_HOTKEY;
procedure DoRegisterHotkey;
procedure SendTabKey(bShifted: boolean);
public
procedure AfterConstruction; override;
end;
var
_Mainform: T_Mainform;
implementation
{$R *.dfm}
const
OurHotkeyID = 10;
procedure T_Mainform.AfterConstruction;
begin
inherited;
DoRegisterHotkey;
end;
procedure T_Mainform.DoRegisterHotkey;
const
sHOTKEY = 'Shift Ctrl F11'; //Fails!
//sHOTKEY = 'Shift F11'; //Succeeds
// sHOTKEY = 'F11'; //Succeeds
//sHOTKEY = 'Shift Ctrl F9'; //Fails!
//sHOTKEY = 'Shift F9'; //Succeeds
var
AHotkey : TShortCut;
hkModifiers: UINT;
hkText: string;
hkKey: Word;
hkShiftState: TShiftState;
begin
AHotkey := TextToShortcut(sHOTKEY);
hkModifiers := 0;
hkText := sHOTKEY;
if Pos('Shift', hkText) > 0 then
hkModifiers := hkModifiers or MOD_SHIFT;
if Pos('Ctrl', hkText) > 0 then
hkModifiers := hkModifiers or MOD_CONTROL;
if Pos('Alt', hkText) > 0 then
hkModifiers := hkModifiers or MOD_ALT;
ShortCutToKey(AHotkey, hkKey, hkShiftState);
if not RegisterHotkey(Handle, OurHotkeyID, hkModifiers, hkKey) then
ShowMessageFmt( 'Unable to register hotkey %s.'#13#13
'LastError: %d', [GetLastError]);
end;
procedure T_Mainform.SendTabKey(bShifted: boolean);
var
KeyInputs: array of TInput;
KeyInputCount: Integer;
//------------------------------
procedure KeybdInput(VKey: Byte; Flags: DWORD);
begin
Inc(KeyInputCount);
SetLength(KeyInputs, KeyInputCount);
KeyInputs[KeyInputCount - 1].Itype := INPUT_KEYBOARD;
with KeyInputs[KeyInputCount - 1].ki do
begin
wVk := VKey;
wScan := MapVirtualKey(wVk, 0);
dwFlags := KEYEVENTF_EXTENDEDKEY;
dwFlags := Flags or dwFlags;
time := 0;
dwExtraInfo := 0;
end;
end;
//------------------------------
begin
KeyInputCount := 0;
if bShifted then
KeybdInput(VK_SHIFT, 0);
KeybdInput(VK_TAB, 0);
KeybdInput(VK_TAB, KEYEVENTF_KEYUP);
if bShifted then
KeybdInput(VK_SHIFT, KEYEVENTF_KEYUP);
SendInput(KeyInputCount, KeyInputs[0], SizeOf(KeyInputs[0]));
Sleep(50);
end;
procedure T_Mainform.WMHotkey(var msg: TWMHotkey);
begin
case msg.Hotkey of
OurHotkeyID:
if (Screen.ActiveForm = Self) or //None of our modal dlgs is showing
(GetLastActivePopup(Application.Handle) = Application.Handle) //No system dlgs are modal (FileOpen, etc.)
then begin
Sleep(200); //<== THE SOLUTION. Note: values of 150 or less failed!
SendTabKey(True); //True=Shift Tab
SendTabKey(True);
Sleep(1000); //...to observe UI effect
SendTabKey(False); //False=Tab
end;
end;
end;
end.
Ответ №1:
Я предполагаю, что целевое приложение считывает состояние Ctrl клавиши и, распознав его, фактически реагирует на Shift Ctrl Tab вместо Shift Tab .
Отправьте клавишу «ctrl» перед Shift Tab, когда ваша горячая клавиша включает клавишу «ctrl».
begin
KeyInputCount := 0;
if CtrlInHotkey then // to be implemented
KeybdInput(VK_CONTROL, KEYEVENTF_KEYUP);
if bShifted then
KeybdInput(VK_SHIFT, 0);
...
Комментарии:
1. Отличная идея … спасибо! В конечном итоге это не было точным решением, но при тестировании вашей идеи я наткнулся на решение как на проблему с синхронизацией, поэтому я принимаю ваш ответ и изменил код, чтобы показать, что добавление Sleep (200) перед последовательностью нажатий клавиш решило проблему …. теперь мне просто нужно изучить, почему и при других обстоятельствах. (Меня удивляет, что 200 мс. требовалось, и меньшие значения работали ненадежно.)
2. @Mark — Всегда пожалуйста. Я немного озадачен тем, что вы наблюдаете. Режим ожидания, являющийся решением, предполагает, что вы, возможно, ожидаете нажатия клавиши «ctrl». Но в этом случае то, что я предложил, должно работать надежно.
3. Я тоже озадачен. Я только сейчас понял, что у аналогичного приложения, которое я написал давным-давно, нет такой проблемы с Ctrl в последовательности горячих клавиш (не требуется длительный режим ожидания () перед отправкой нажатий клавиш). Он использует цикл PeekMessage (), чтобы проверить, обрабатывало ли целевое приложение каждое отправленное мной нажатие клавиши SendInput, чего не делает вышеупомянутое тестовое приложение, потому что у меня нет дескриптора окна цели. Мне придется поэкспериментировать … и я прокомментирую здесь, когда что-нибудь узнаю. Еще раз спасибо.
4. Я попробовал несколько подходов, и на данный момент я отказался от попыток выяснить, почему Sleep (200) работает и / или необходим. Может быть, когда-нибудь я наткнусь на причину. До тех пор добавление этого в мой код не сильно мешает.