Delphi 使用WinAPI向另一个应用程序发送击键

Delphi 使用WinAPI向另一个应用程序发送击键,delphi,winapi,delphi-7,sendmessage,postmessage,Delphi,Winapi,Delphi 7,Sendmessage,Postmessage,我必须通过向另一个应用程序发送按键来控制它,如CTRL、CTRLSHIFTC或CTRLF 我试过很多东西,但都没法用。所以我试图在一个更简单的案例中正确处理这个问题 这将成功地将Hey发送到记事本: procedure TForm1.Button1Click(Sender: TObject); var notepad, edit: HWND; begin notepad := FindWindow('notepad', nil); edit := FindWindowEx(notep

我必须通过向另一个应用程序发送按键来控制它,如CTRL、CTRLSHIFTC或CTRLF

我试过很多东西,但都没法用。所以我试图在一个更简单的案例中正确处理这个问题

这将成功地将
Hey
发送到记事本:

procedure TForm1.Button1Click(Sender: TObject);
  var notepad, edit: HWND;
begin
  notepad := FindWindow('notepad', nil);
  edit := FindWindowEx(notepad, FindWindow('Edit', nil), nil, nil);

  SendMessage(edit, WM_CHAR, dword('H'), 0);
  SendMessage(edit, WM_CHAR, dword('e'), 0);
  SendMessage(edit, WM_CHAR, dword('y'), 0);
end;
这将成功地将F5键发送到记事本,并且F3弹出“查找”对话框

notepad := FindWindow('notepad', nil);
PostMessage(notepad, WM_KEYDOWN, VK_F5, 0);
PostMessage(notepad, WM_KEYUP, VK_F5, 0);
但是我不知道为什么在上面的例子中使用
SendMessage
不起作用

我能想到的最好的东西就是这样的东西,它什么也没用

notepad := FindWindow('notepad', nil);
PostMessage(notepad, WM_KEYDOWN, VK_CONTROL, 0);
PostMessage(notepad, WM_KEYDOWN, VkKeyScan('F'), 0);
PostMessage(notepad, WM_KEYUP, VkKeyScan('F'), 0);
PostMessage(notepad, WM_KEYUP, VK_CONTROL, 0);
我在这里的某个地方发现了一个类似VBScript发送键函数的库,但只要查看代码,它似乎只是向当前应用程序或所有应用程序广播键,因为没有句柄参数。

警告:此方法取决于实现细节,如果需要保证程序的正确性,则不应使用。(另一方面,您已经走上了这条道路。例如,IIRC,在Windows 95中甚至没有“转到”对话框。)

我在我最喜欢的资源编辑器中打开了
notepad.exe
,并查看了菜单栏。我注意到
Save
菜单项的ID是
3
。因此,以下代码在记事本中执行
Save
菜单命令:

var
  notepad: HWND;
begin
  notepad := FindWindow('notepad', nil);

  SendMessage(notepad, WM_COMMAND, 3, 0);
类似地,
Find
在我的
notepad.exe版本中是
21
<代码>转到
is
24

更新,根据注释:如果需要发送Ctrl+键,可以使用
SendInput

var
  notepad: HWND;
  inputArray: array[0..3] of TInput;
begin
  notepad := FindWindow('notepad', nil);

  // TODO: Either exit if notepad isn't focused, or set focus to notepad

  FillChar(inputArray, length(inputArray) * sizeof(TInput), 0);

  inputArray[0].Itype := INPUT_KEYBOARD;
  inputArray[0].ki.wVk := VK_LCONTROL;
  inputArray[1].Itype := INPUT_KEYBOARD;
  inputArray[1].ki.wVk := VkKeyScan('S');
  inputArray[2].Itype := INPUT_KEYBOARD;
  inputArray[2].ki.wVk := VkKeyScan('S');
  inputArray[2].ki.dwFlags := KEYEVENTF_KEYUP;
  inputArray[3].Itype := INPUT_KEYBOARD;
  inputArray[3].ki.wVk := VK_LCONTROL;
  inputArray[3].ki.dwFlags := KEYEVENTF_KEYUP;

  SendInput(length(inputArray), inputArray[0], sizeof(TInput));

我已经使用keybd_事件很多年了。即使其他一切都失败了,它也会一直工作,因为它直接将输入输入到键盘驱动程序和Windows之间的接口中。手动键入和使用下面的函数生成键之间没有什么区别。唯一的缺点是目标窗口必须始终保持在前景中

procedure SendKey(Wnd,VK : Cardinal; Ctrl,Alt,Shift : Boolean);
var
  MC,MA,MS : Boolean;
begin
  // Try to bring target window to foreground
  ShowWindow(Wnd,SW_SHOW);
  SetForegroundWindow(Wnd);

  // Get current state of modifier keys
  MC:=Hi(GetAsyncKeyState(VK_CONTROL))>127;
  MA:=Hi(GetAsyncKeyState(VK_MENU))>127;
  MS:=Hi(GetAsyncKeyState(VK_SHIFT))>127;

  // Press modifier keys if necessary (unless already pressed by real user)
  if Ctrl<>MC then keybd_event(VK_CONTROL,0,Byte(MC)*KEYEVENTF_KEYUP,0);
  if Alt<>MA then keybd_event(VK_MENU,0,Byte(MA)*KEYEVENTF_KEYUP,0);
  if Shift<>MS then keybd_event(VK_SHIFT,0,Byte(MS)*KEYEVENTF_KEYUP,0);

  // Press key
  keybd_event(VK,0,0,0);
  keybd_event(VK,0,KEYEVENTF_KEYUP,0);

  // Release modifier keys if necessary
  if Ctrl<>MC then keybd_event(VK_CONTROL,0,Byte(Ctrl)*KEYEVENTF_KEYUP,0);
  if Alt<>MA then keybd_event(VK_MENU,0,Byte(Alt)*KEYEVENTF_KEYUP,0);
  if Shift<>MS then keybd_event(VK_SHIFT,0,Byte(Shift)*KEYEVENTF_KEYUP,0);
end;
procedure SendKey(Wnd,VK:Cardinal;Ctrl,Alt,Shift:Boolean);
变量
MC,MA,MS:布尔型;
开始
//尝试将目标窗口置于前景
展示窗口(西北、西南展示);
SetforeGroundindow(西尼罗河);
//获取修改器关键点的当前状态
MC:=Hi(GetAsyncKeyState(VK_控制))>127;
MA:=Hi(GetAsyncKeyState(VK_菜单))>127;
MS:=Hi(GetAsyncKeyState(VK_SHIFT))>127;
//如有必要,按修改键(除非真实用户已按下)
如果为CtrlMC,则为keybd_事件(VK_控件,0,字节(MC)*KEYEVENTF_KEYUP,0);
如果为AltMA,则为keybd_事件(VK_菜单,0,字节(MA)*KEYEVENTF_键向上,0);
如果是ShiftMS,则为keybd_事件(VK_移位,0,字节(毫秒)*KEYEVENTF_KEYUP,0);
//按键
keybd_事件(VK,0,0,0);
keybd_事件(VK,0,KEYEVENTF_KEYUP,0);
//如有必要,释放修改器键
如果为CtrlMC,则为keybd_事件(VK_控件,0,字节(Ctrl)*KEYEVENTF_KEYUP,0);
如果为AltMA,则为keybd_事件(VK_菜单,0,字节(Alt)*KEYEVENTF_键向上,0);
如果为ShiftMS,则为keybd_事件(VK_移位,0,字节(移位)*KEYEVENTF_键向上,0);
结束;

这非常有用,但对我不起作用,因为我需要在一个没有标准windows组件的游戏中自动执行任务,所以我可能找不到任何命令ID,我只需将消息发送到顶部的窗口句柄,唯一可靠的方法是击键总是一样的。有“旧”吗不支持
绝对
的Delphi版本?我认为这是从伯兰·帕斯卡那里继承来的。@AndriyM:我的理解是,它是在德尔福2009年引入的,但显然是大卫在这一点上。我想我错了。我可以看出你已经删除了关于
绝对值的部分,所以这只是为了确认。Delphi 6和Delphi 2006是我最近积极使用的两个版本,它们都支持它,我确信这与我在Borland/Turbo Pascal中经常使用的
absolute
相同。所以,是的,你可以相信所有的Delphi版本都有
absolute
。@Ken:是的,这就是我想要的。它可能会失败。当另一个进程插入交错的键盘事件时,它将失败。这就是SendInput存在的原因。我不知道你为什么认为keybd_事件比SendInput好。游戏可能并不总是响应SendMessage(),所以需要一个较低级别的函数。我假设SendInput是基于SendMessage()的高级函数。我被纠正了。