Recent

Author Topic: Send keypresses to X-Plane 11(windows 10)  (Read 467 times)

xardomain

  • New Member
  • *
  • Posts: 12
Send keypresses to X-Plane 11(windows 10)
« on: January 08, 2020, 04:59:09 am »
Hi,
trying to send simple keystrokes to X-Plane 11 but it is not working.

I am able to send them to notepad but X-Plane does not work, it just ignores them.

procedure TForm1.Button5Click(Sender: TObject);
var XplaneWindowHWND : HWND;

begin
  XplaneWindowHWND := findwindow('notepad',nil);  // This works
//XplaneWindowHWND := findwindow('X-System',nil);  // this "works"
  if (XplaneWindowHWND <> 0) then
  begin
    XplaneWindowHWND := FindWindowEx(XplaneWindowHWND, FindWindow('Edit', nil), nil, nil); // this works
  //XplaneWindowHWND := FindWindowEx(XplaneWindowHWND, FindWindow('X-System', nil), nil, nil);  // this doesn't, not sure what to specify for FindWindo
    SendMessage(XplaneWindowHWND, WM_CHAR, word('g'), 0);
    Memo1.Lines.Add('Focused and sent char')
  end
  else
    Memo1.Lines.Add('Failed to find the window')
end;

The example using notepad looks for a window(child window?) named "Edit", X-plane does not have any child window so I don't know how/if to use findwindow.

Now, a simple autohotkey is able to send the keypress 'g' without any problem (using CTRL+J for example):

^j::
Send, g
return

So it is possible. What prevents me to send the keystrokes to the simulator?

I have tried KeyInput.Press but nothing, it works only for notepad

TIA

Giuseppe Marullo
Lazarus 2.0.6 / FPC 3.0.4

GetMem

  • Hero Member
  • *****
  • Posts: 3559
Re: Send keypresses to X-Plane 11(windows 10)
« Reply #1 on: January 08, 2020, 07:16:23 am »
According to your screenshot, X-Plane 11 doesn't have any childwindow.  Your only option is to send the keystroke to the main window(X-System). Also replace SendMessage to PostMessage, the later is an asynchronous function, it won't block the message queue:
Code: Pascal  [Select]
  1. var
  2.   XplaneWindowHWND : HWND;
  3. begin
  4.   XplaneWindowHWND := FindWindow('X-System', nil);
  5.   if (XplaneWindowHWND <> 0) then
  6.   begin
  7.     PostMessage(XplaneWindowHWND, WM_CHAR, Word('g'), 0);
  8.     Memo1.Lines.Add('Focused and sent char')
  9.   end
  10.   else
  11.     Memo1.Lines.Add('Failed to find the window')
  12.  

A slightly improved version would be the following, first find "XPlane 11" process, then find the process main window. Like this:
Code: Pascal  [Select]
  1. uses JwaWindows, jwatlhelp32;
  2.  
  3. function GetProcessMainWindow(const PID: DWORD; const wFirstHandle: HWND): HWND;
  4. var
  5.   wHandle: HWND;
  6.   ProcID: DWord;
  7. begin
  8.   Result := 0;
  9.   wHandle := GetWindow(wFirstHandle, GW_HWNDFIRST);
  10.   while wHandle > 0 do
  11.   begin
  12.     GetWindowThreadProcessID(wHandle, @ProcID);
  13.     if (ProcID = PID) and (GetParent(wHandle) = 0) and (IsWindowVisible(wHandle)) then
  14.     begin
  15.       Result := wHandle;
  16.       Break;
  17.     end;
  18.     wHandle := GetWindow(wHandle, GW_HWNDNEXT);
  19.   end;
  20. end;
  21.  
  22. function FindProcessByName(const ProcName:string): DWORD;
  23. var
  24.   Proc: TPROCESSENTRY32;
  25.   hSnap: HWND;
  26.   Looper: BOOL;
  27. begin
  28.   Result := 0;
  29.   Proc.dwSize := SizeOf(Proc);
  30.   hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  31.   Looper := Process32First(hSnap, Proc);
  32.   while Integer(Looper) <> 0 do
  33.   begin
  34.     if UpperCase(ExtractFileName(proc.szExeFile)) = UpperCase(ProcName) then
  35.     begin
  36.       Result:=proc.th32ProcessID;
  37.       Break;
  38.     end;
  39.     Looper := Process32Next(hSnap, Proc);
  40.   end;
  41.   CloseHandle(hSnap);
  42. end;
  43.  
  44. procedure TForm1.Button1Click(Sender: TObject);
  45. var
  46.   PID: DWord;
  47.   XplaneWindow: THandle;
  48. begin
  49.   PID := FindProcessByName('XSystem.exe'); //change this to the appropriate value(see task manager)
  50.   if PID <> 0 then
  51.   begin
  52.     XplaneWindow := GetProcessMainWindow(PID, Form1.Handle);
  53.     if XplaneWindow <> 0 then
  54.       PostMessage(XplaneWindow, WM_CHAR, Word('g'), 0);
  55.   end;
  56. end;

xardomain

  • New Member
  • *
  • Posts: 12
Re: Send keypresses to X-Plane 11(windows 10)
« Reply #2 on: January 08, 2020, 12:18:47 pm »
Hi Getmem,
many thanks for your answer. However, both solutions don't work for me.
This is the code now:

procedure TForm1.Button5Click(Sender: TObject);
var XplaneWindowHWND : HWND;
     PID : DWORD;
     XplaneWindow: THandle;
begin
  // Wait 3s to allow manual switch if desired
  Form1.Timer4.Enabled := TRUE;
  while Timer4.Enabled do
        Application.Processmessages;

  PID := FindProcessByName('X-Plane.exe');
  if (PID <> 0) then
  begin
    Memo1.Lines.Add('Process found. PID is %d',[PID])  ;

    XplaneWindow := GetProcessMainWindow(PID, Form1.Handle);

    if (XplaneWindow <> 0) then
    begin
      Memo1.Lines.Add('Main Window found. Handle is %d',[XplaneWindow]);
      PostMessage(XplaneWindow, WM_CHAR, Word('g'), 0)
    end
    else
      Memo1.Lines.Add('Main Window NOT found.')  ;

No effect. BTW this doesn't even work for notepad.

GetMem

  • Hero Member
  • *****
  • Posts: 3559
Re: Send keypresses to X-Plane 11(windows 10)
« Reply #3 on: January 08, 2020, 01:31:59 pm »
Quote
many thanks for your answer.
You're welcome.

Quote
However, both solutions don't work for me.
It works fine at my side. What is your OS? Windows 10 is the most restrictive windows version so far. Maybe you should run your program with elevated privileges.

Quote
No effect. BTW this doesn't even work for notepad.
It wasn't meant to work with notepad, notepad has a childwindow.

As a last attempt please try this(don't forget to set Timer1.Interval to 5000, Timer1.Enabled to False by default, and run your program as administrator):
Code: Pascal  [Select]
  1. uses JwaWindows;
  2.  
  3. procedure TForm1.Button1Click(Sender: TObject);
  4. begin
  5.   Timer1.Enabled := True; //allow to switch to XPlane 11
  6. end;
  7.  
  8. procedure TForm1.Timer1Timer(Sender: TObject);
  9. var
  10.   Input: array [0..1] of TInput;
  11. begin
  12.   Timer1.Enabled := False;
  13.  
  14.   ZeroMemory(@Input, SizeOf(Input));
  15.   Input[0].type_      := INPUT_KEYBOARD;
  16.   Input[0].ki.dwFlags := KEYEVENTF_UNICODE;
  17.   Input[0].ki.wVk     := VKKeyScan('g');
  18.  
  19.   Input[1].type_      := INPUT_KEYBOARD;
  20.   Input[1].ki.dwFlags := KEYEVENTF_KEYUP;
  21.   Input[1].ki.wScan   := VKKeyScan('g');
  22.   SendInput(2, @Input[0], SizeOf(TInput));
  23. end;

« Last Edit: January 08, 2020, 01:42:39 pm by GetMem »