Delphi例程模拟打字

时间:2018-06-23 14:13:15

标签: delphi sendinput

我正在尝试在Delphi XE中开发一个例程,以将文本输出到屏幕上,以模拟游戏中的键入文本。例如,这并不像将字符连接到TMemo那样简单,因为即使使用beginupdate / endupdate也会导致闪烁。我使用了here技术,已尽我所能将其转换为Delphi代码,并根据此处的注释对其进行了修改,但我没有得到正确的文本。我猜我正在浪费,因为字符不正确地以某种格式出现。有没有人看到这里有什么问题或知道更好的模拟打字方法?

procedure SendEnter();
var
  KeyInputs: array [0..1] of TInput;
begin
  ZeroMemory(@KeyInputs,sizeof(KeyInputs));
  KeyInputs[0].Itype := INPUT_KEYBOARD;
  KeyInputs[0].ki.wVk := VK_RETURN;
  KeyInputs[0].ki.dwFlags := 0;
  KeyInputs[1].Itype := INPUT_KEYBOARD;
  KeyInputs[1].ki.wVk := VK_RETURN;
  KeyInputs[1].ki.dwFlags := KEYEVENTF_EXTENDEDKEY;
  SendInput(2, KeyInputs[0], SizeOf(TInput));
end;

function TForm2.PrintOutLine(sLine: string): boolean;
var
  i: integer;
  VKRes: SmallInt;
  VK: byte;
  KeyInputsU: array [0..3] of TInput;
  KeyInputsL: array [0..1] of TInput;
  bUppercase: boolean;
begin
  Memo1.SetFocus;
  i:= 1;
  while (i<=Length(sLine)) do begin
    bUppercase:= sLine[i]=UpCase(sLine[i]);
    VKRes:= VkKeyScanEx(sLine[i],GetKeyboardLayout(0));
    VK:= VKRes;
    if (bUppercase) then begin
      ZeroMemory(@KeyInputsU,sizeof(KeyInputsU));
      KeyInputsU[0].Itype := INPUT_KEYBOARD;
      KeyInputsU[0].ki.wVk := VK_LSHIFT;
      //KeyInputsU[0].ki.dwFlags := 0;
      KeyInputsU[1].Itype := INPUT_KEYBOARD;
      KeyInputsU[1].ki.wVk := vk;
      KeyInputsU[1].ki.dwFlags := KEYEVENTF_UNICODE ;
      KeyInputsU[2].Itype := INPUT_KEYBOARD;
      KeyInputsU[2].ki.wVk := vk;
      KeyInputsU[2].ki.dwFlags := KEYEVENTF_UNICODE or KEYEVENTF_KEYUP;
      KeyInputsU[3].Itype := INPUT_KEYBOARD;
      KeyInputsU[3].ki.wVk := VK_LSHIFT;
      KeyInputsU[3].ki.dwFlags := KEYEVENTF_KEYUP;
      SendInput(4, KeyInputsU[0], SizeOf(TInput));
    end
    else begin
      ZeroMemory(@KeyInputsL,sizeof(KeyInputsL));
      KeyInputsL[0].Itype := INPUT_KEYBOARD;
      KeyInputsL[0].ki.wVk := vk;
      KeyInputsL[0].ki.dwFlags := KEYEVENTF_UNICODE;
      KeyInputsL[1].Itype := INPUT_KEYBOARD;
      KeyInputsL[1].ki.wVk := vk;
      KeyInputsL[1].ki.dwFlags := KEYEVENTF_UNICODE or KEYEVENTF_KEYUP;
      SendInput(2, KeyInputsL[0], SizeOf(TInput));
    end;
    Application.ProcessMessages;
    Sleep(80);
    inc(i);
  end;
  SendEnter;
  Form2.SetFocus;
  Result:= True;
end;

3 个答案:

答案 0 :(得分:1)

要回答您修改过的问题,并且您对非字母字符的最新评论仍无法正常工作:

您对大写/小写字母的检测均在a .. z以外的任何字符上失败。如果您查看System.UpCase()的文档,它指出不在a..z范围内的字符值不受影响。因此,如果将其输入<,您将得到返回相同的<。您的代码会将其解释为大写字符,尽管不是。

已在注释中告知您使用dwFlags KEYEVENTF_UNICODE发送密钥(实际上是字符)。您似乎仅部分地并且错误地采用了该方法。

请注意,MSDN documentation表示:

  

wVk:...如果dwFlags成员指定KEYEVENTF_UNICODE,则 wVk必须为0

     

wScan:...如果dwFlags指定KEYEVENTF_UNICODE,则wScan指定Unicode字符

,此外,对于标志KEYEVENTF_UNICODE

  

如果指定,则系统将合成VK_PACKET击键。 wVk参数必须为零。该标志只能与KEYEVENTF_KEYUP标志结合使用。

Ergo,您无需检测并分别处理大写和小写字符。您只需将wScan设置为要发送的UTF-16字符的序数即可。因此,您的非字母字符也可以在您修改的以下代码中正常使用:

function TForm3.PrintOutLine(sLine: string): boolean;
var
  i: integer;
  KeyInputsL: array [0..1] of TInput;
begin
  Memo1.SetFocus;
  i:= 1;
  while (i<=Length(sLine)) do begin
    ZeroMemory(@KeyInputsL,sizeof(KeyInputsL));

    KeyInputsL[0].Itype := INPUT_KEYBOARD;
//    KeyInputsL[0].ki.wVk := vk;            // don't use wVk with KEYEVENTF_UNICODE
    KeyInputsL[0].ki.wScan := ord(sLine[i]); // instead use wScan
    KeyInputsL[0].ki.dwFlags := KEYEVENTF_UNICODE;

    KeyInputsL[1].Itype := INPUT_KEYBOARD;
//    KeyInputsL[1].ki.wVk := vk;
    KeyInputsL[1].ki.wScan := ord(sLine[i]);
    KeyInputsL[1].ki.dwFlags := KEYEVENTF_UNICODE or KEYEVENTF_KEYUP;

    SendInput(2, KeyInputsL[0], SizeOf(TInput));

    Application.ProcessMessages;
    Sleep(80);
    inc(i);
  end;
  SendEnter;
  Form3.SetFocus;
  Result:= True;
end;

以上内容回答了您的实际问题,但未考虑替代的UTF-16代码点对。该shown here

有完整的代码(在C ++中)

同样,这也不是您的问题的一部分,但是我不能不加评论地让它通过:Application.ProcessMessagesSleep()并不是一次发送一个字符的正确方法。改用计时器触发每个字符的发送。

答案 1 :(得分:0)

雷米·勒博(Remy Lebeau)的解决方案也很好用。

innerLoop

答案 2 :(得分:0)

procedure TypeKey(Key: Cardinal);
const KEYEVENTF_KEYDOWN = 0;
      KEYEVENTF_UNICODE = 4;
var rec: TInput;
    rLen: Integer;
    shift: Boolean;
begin
  rLen:=SizeOf(TInput);

  shift:=(Key shr 8)=1;

  if shift then begin
    rec.Itype:=INPUT_KEYBOARD;
    rec.ki.wVk:=VK_SHIFT;
    rec.ki.dwFlags:=KEYEVENTF_KEYDOWN; // or KEYEVENTF_UNICODE;
    SendInput(1,rec,rLen);
  end;

  rec.Itype:=INPUT_KEYBOARD;
  rec.ki.wVk:=Key;
  rec.ki.dwFlags:=KEYEVENTF_KEYDOWN; // or KEYEVENTF_UNICODE;
  SendInput(1,rec,rLen);

  rec.Itype:=INPUT_KEYBOARD;
  rec.ki.wVk:=Key;
  rec.ki.dwFlags:=KEYEVENTF_KEYUP; // or KEYEVENTF_UNICODE;
  SendInput(1,rec,rLen);

  if shift then begin
    rec.Itype:=INPUT_KEYBOARD;
    rec.ki.wVk:=VK_SHIFT;
    rec.ki.dwFlags:=KEYEVENTF_KEYUP; // or KEYEVENTF_UNICODE;
    SendInput(1,rec,rLen);
  end;
end;

procedure TypeString(Str: String);
var i, sLen: Integer;
begin
  sLen:=Length(Str);
  if sLen>0 then for i:=1 to sLen do TypeKey(vkKeyScan(Str[i]));
  TypeKey(VK_RETURN);
end;