Delphi рутина для имитации набора текста - PullRequest
0 голосов
/ 23 июня 2018

Я пытаюсь разработать в Delphi XE процедуру вывода текста на экран для имитации ввода текста в игре.Например, это не так просто, как объединение символов в TMemo, так как это приводит к мерцанию даже при использовании beginupdate / endupdate.Я использовал технику здесь , преобразовал ее в код 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;

Ответы [ 2 ]

0 голосов
/ 26 июня 2018

Решение от Реми Лебо тоже хорошо работает.

function TForm2.PrintOutLine(sLine: string): boolean;
var
  i: integer;
begin
  i:= 1;
  while (i<=Length(sLine)) do begin
    Memo1.SelStart := Memo1.GetTextLen;
    Memo1.SelText := sLine[i];
    Application.ProcessMessages;
    Sleep(80);
    inc(i);
  end;
  Memo1.SelStart := Memo1.GetTextLen;
  Memo1.SelText := #13#10;
  Result:= True;
end;
0 голосов
/ 25 июня 2018

Чтобы ответить на ваш измененный вопрос, и ваш последний комментарий о не-буквенных символах по-прежнему не работает правильно:

Ваше обнаружение верхнего / нижнего регистра не удается для любого символа, кроме a .. z. Если вы посмотрите на документацию для System.UpCase(), в ней говорится, что Значения символов, не входящие в диапазон a..z, не затронуты. Поэтому, если вы передадите ему <, вы получите то же самое * 1008. *. Ваш код будет интерпретировать это как символ верхнего регистра, хотя это не так.

Вам было сказано в комментарии отправить ключи (на самом деле символы) с dwFlags KEYEVENTF_UNICODE. Вы, кажется, приняли это только частично, а также ошибочно.

Обратите внимание, что в документации MSDN написано:

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. Для этого показан полный код (на С ++)

Кроме того, это не часть вашего вопроса, но я не могу обойтись без комментариев: Application.ProcessMessages и Sleep() - неправильный способ отправки одного символа за раз. Вместо этого используйте таймер, чтобы инициировать отправку каждого символа.

...