Recent

Author Topic: Using SendMessage or Postmessage to simulate keyboard  (Read 3147 times)

ArminLinder

  • Sr. Member
  • ****
  • Posts: 314
  • Keep it simple.
Using SendMessage or Postmessage to simulate keyboard
« on: December 22, 2022, 12:59:50 am »
Hi all,

Trying to send keystrokes to an application on Windows 11 I have this code ...

Code: Pascal  [Select][+][-]
  1. var
  2.   wndMain: HWND;
  3.   r:LResult;
  4.   scanCode:UINT;
  5.   l:LPARAM;
  6.   w:WPARAM;
  7.  
  8. begin
  9.   // wndMain := FindWindow(PChar(aClassName),nil);
  10.   wndMain := FindWindow('Notepad',nil);
  11.   DoShowStatus('Window Handle: ' + IntToStr(wndMain));
  12.   if wndMain <> 0 then
  13.     begin
  14.     l := 0;
  15.     // scanCode := MapVirtualKey(VK_A, 0);
  16.     // l := ($00000001 OR (scanCode SHL 16));
  17.     r := SendMessage(wndMain,WM_KEYDOWN,VK_A,l);
  18.     // l := l OR $C0000000;
  19.     r := SendMessage(wndMain,WM_KEYUP,VK_A,l);
  20.     end;
  21. end;
  22.  
  23.  

The objective is to send a specific keystroke (I choose VK_A for testing) to an application, to try I choose notepad.

Finding the hwnd for notepad seems to work, a value is returned.

Unfortunately there is no reaction in Notepad, no keystrokes show up, though SendMessage returns 0. I tried PostMessage too, same behaviour. Regarding the bitmask for lParam, I took it from the AutoIt source code, but using it (commented out) doesn't change anything.

Can anyone provide a working code sample showing how I need to use SendMessage or PostMessage? From what I read I should prefer PostMessage.

Thnx, Armin.
Lazarus 3.3.2 on Windows 7,10,11, Debian 10.8 "Buster", macOS Catalina, macOS BigSur, VMWare Workstation 15, Raspberry Pi

balazsszekely

  • Guest
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #1 on: December 22, 2022, 06:27:42 am »
@Nimarl
Quote
The objective is to send a specific keystroke (I choose VK_A for testing) to an application, to try I choose notepad.

Finding the hwnd for notepad seems to work, a value is returned.

Unfortunately there is no reaction in Notepad, no keystrokes show up, though SendMessage returns 0. I tried PostMessage too, same behaviour. Regarding the bitmask for lParam, I took it from the AutoIt source code, but using it (commented out) doesn't change anything.

Can anyone provide a working code sample showing how I need to use SendMessage or PostMessage? From what I read I should prefer PostMessage.
1. You are sending the keystroke to the wrong window, notepad has a child window named "Edit".
Code: Pascal  [Select][+][-]
  1. uses windows;
  2.  
  3. procedure TForm1.Button1Click(Sender: TObject);
  4. var
  5.   wHandle: HWND;
  6. begin
  7.   wHandle := FindWindow('notepad', nil);
  8.   if wHandle = 0 then
  9.     Exit;
  10.   wHandle := FindWindowEx(wHandle, FindWindow('Edit', nil), nil, nil);
  11.   if wHandle = 0 then
  12.     Exit;
  13.   SendMessage(wHandle, WM_CHAR, Word('a'), 0);
  14. end;  
2. Replace SendMessage with PostMessage, the later is an asynchronous function, it won't block the message queue

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #2 on: December 22, 2022, 05:41:22 pm »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Thaddy

  • Hero Member
  • *****
  • Posts: 14203
  • Probably until I exterminate Putin.
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #3 on: December 22, 2022, 08:49:17 pm »
Alas, several consts and the INPUT struct are missing in Windows, so the SendInput() example from msdn won't work even if correctly translated to Pascal. Busy on it. Should be easy.
[edit] progress, it compiles (but does not work yet):
Code: Pascal  [Select][+][-]
  1. {$mode delphiunicode}{$apptype console}
  2. uses jwawindows;
  3. //**********************************************************************
  4. //
  5. // Sends Win + D to toggle to the desktop
  6. //
  7. //**********************************************************************
  8. type
  9.   Tinputs = array[0..3] of INPUT;
  10.  
  11. const
  12.   VK_D:word = $0044;
  13. procedure ShowDesktop;
  14. var
  15.   inputs:TInputs;
  16.   uSent:UINT;
  17. begin
  18.     writeln('Sending Win-D');
  19.     ZeroMemory(@inputs, length(inputs));
  20.  
  21.     inputs[0].type_ := INPUT_KEYBOARD;
  22.     inputs[0].ki.wVk := VK_LWIN;
  23.    
  24.     inputs[1].type_ := INPUT_KEYBOARD;
  25.     inputs[1].ki.wVk := VK_D;
  26.  
  27.     inputs[2].type_ := INPUT_KEYBOARD;
  28.     inputs[2].ki.wVk := VK_D;
  29.     inputs[2].ki.dwFlags := KEYEVENTF_KEYUP;
  30.  
  31.     inputs[3].type_ := INPUT_KEYBOARD;
  32.     inputs[3].ki.wVk := VK_LWIN;
  33.     inputs[3].ki.dwFlags := KEYEVENTF_KEYUP;
  34.     uSent := SendInput(SizeOf(inputs),inputs, sizeof(INPUT));
  35.     if (uSent <> length(inputs)) then
  36.         writeln(HRESULT_FROM_WIN32(GetLastError()));
  37. end;
  38. begin
  39.   ShowDeskTop;
  40. end.
Note this code should also work from a console, tested with the C++ code.
Probably an oversight, maybe someone can spot it? Maybe User Interface Privilege Isolation, a.k.a. UIPI?
« Last Edit: December 22, 2022, 11:35:50 pm by Thaddy »
Specialize a type, not a var.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2007
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #4 on: December 22, 2022, 11:06:20 pm »
Code: [Select]
  SendMessage(wHandle, WM_CHAR, Word('a'), 0);
I am unsure what the OP try to do, maybe WM_SETTEXT is better.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Thaddy

  • Hero Member
  • *****
  • Posts: 14203
  • Probably until I exterminate Putin.
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #5 on: December 22, 2022, 11:36:59 pm »
I am unsure what the OP try to do, maybe WM_SETTEXT is better.
Note Remy's remark and Raymond Chen's column: they are right. (Now I only need to get my example to work, but al least it compiles)
All that post/sendmessage stuff worked somewhat in very old Win versions (pre XP, WIN2000) and still may work, but is not reliable enough.
« Last Edit: December 22, 2022, 11:41:56 pm by Thaddy »
Specialize a type, not a var.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2007
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #6 on: December 23, 2022, 12:24:45 am »
I've read the remarks and need to think for a while, I guess when I do connect to the target thread I can attach to play a little.
Here is something to send at least some Text if that is wanted  :-[ (based on GetMems version as source)
Code: Pascal  [Select][+][-]
  1. function SendTextAtToMsg(const AClassname, AControlClass, AMessage: WideString): Boolean;
  2. var
  3.   wHandle: HWND;
  4.   Len: DWORD;
  5.   s, z: WideString;
  6. begin
  7.   Result := False;
  8.   // get main window handle
  9.   wHandle := FindWindowW(PWideChar(AClassname), nil);
  10.   if (wHandle = 0) then
  11.     Exit;
  12.   // get control handle
  13.   wHandle := FindWindowExW(wHandle, FindWindowW(PWideChar(AControlClass), nil), nil, nil);
  14.   if (wHandle = 0) then
  15.     Exit;
  16.   // get current contant as proof of concept, i am sure its wrong somewhere :P
  17.   s := '';
  18.   Len := Succ(SendMessageW(wHandle, WM_GETTEXTLENGTH, 0, 0));
  19.   SetLength(s, (Succ(Len) * SizeOf(WideChar)));
  20.   SendMessageW(wHandle, WM_GETTEXT, WPARAM(Succ(Len)), LPARAM(@s[1]));
  21.   Form1.Memo1.Clear;
  22.   Form1.Memo1.Text := s;
  23.   // send to control a new string
  24.   z := AMessage;
  25.   Len := Succ((Length(z) * SizeOf(WideChar)));
  26.   SendMessageW(wHandle, WM_SETTEXT, WPARAM(Succ(Len)), LPARAM(@z[1]));
  27.   Result := True;
  28. end;
  29.  
  30. procedure TForm1.Button1Click(Sender: TObject);
  31. begin
  32.   if SendTextAtToMsg('notepad', 'edit', 'Hacked') then
  33.     Memo1.Lines.Add('New Text Set')
  34.     else
  35.     Memo1.Lines.Add('Error at finding Notepad!');
  36. end;
« Last Edit: December 23, 2022, 12:31:13 am by KodeZwerg »
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

balazsszekely

  • Guest
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #7 on: December 23, 2022, 10:20:36 am »
Sending a simple English char like "A" will definitely work with PostMessage/SendMessage too. There is no contradiction with Raymond Chen's article.
Keybd_Event is now superseded, the preferred method is SendInput, but in order to use it, first you have to activate the target window with SetForegroundWindow. Stackoverflow is full with examples on how to use SendInput.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: Using SendMessage or Postmessage to simulate keyboard
« Reply #8 on: December 23, 2022, 05:43:39 pm »
Code: Pascal  [Select][+][-]
  1. uSent := SendInput(SizeOf(inputs),inputs, sizeof(INPUT));

In the 1st parameter, you are passing in the byte size of the array, but the function wants the number of array elements instead, so you need to use Length() instead of SizeOf():

Code: Pascal  [Select][+][-]
  1. uSent := SendInput(Length(inputs), inputs, sizeof(INPUT));
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018