Forum > Windows

Sending keystrokes to the previously focused app similar to how Autohotkey does.

(1/2) > >>

Rave:
So I am developing an on-screen shortcut app to help me with drawing and such (will be releasing it as GPL on Github), but I am stumped by the actual process of sending the keystrokes to the windows.

The closest I got was by using FindWindow thing, however it requires to specify window class or at least its name, whereas I need the key strokes to be passed to the window that was in focus just before the user clicked shortcut button on my form, without any knowledge of its name or class.

The shortcut window would be always on top and semi-transparent, but I need to make it so the thing can actually be used.

The software will be Windows-only for the time being, so the portability is not the issue right now.

GetMem:

--- Quote from: Rave on December 25, 2021, 08:59:56 am ---So I am developing an on-screen shortcut app to help me with drawing and such (will be releasing it as GPL on Github), but I am stumped by the actual process of sending the keystrokes to the windows.

The closest I got was by using FindWindow thing, however it requires to specify window class or at least its name, whereas I need the key strokes to be passed to the window that was in focus just before the user clicked shortcut button on my form, without any knowledge of its name or class.

The shortcut window would be always on top and semi-transparent, but I need to make it so the thing can actually be used.

The software will be Windows-only for the time being, so the portability is not the issue right now.

--- End quote ---

1. Download, build then run attached project
2. Start notepad
3. Switch back to attached project, then click "Send Keys" button

Rave:
That's a step in a good direction, however I can't use it as it depends on MouseAndKeyInput which doesn't support pressing alt (which would be an awful limitation of my program to have). What I need is an ability to send specific keystrokes (like Ctrl+N or Alt+F4 to the previously focused application, not random text.

Also, notice what happens when the text to be sent is "Ctrl+A" (without quotes). No, it doesn't press Ctrl+A in the notepad, neither it does make it write Ctrl+A as a text.

Thaddy:
There is a unit sendkeys.pas that compiles in mode delph with my minor changes.
 Tested win32/win64

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit sendkeys;{$mode delphi}{ originally written for delphi 1 by Ken Henderson. Freeware.  modified for Freepascal by Thaddy de Koning } interface Uses SysUtils, Windows, Messages; Function fpSendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;function AppActivate(WindowName : PChar) : boolean; {Buffer for working with PChar's} const  WorkBufLen = 40;var  WorkBuf : array[0..WorkBufLen] of Char; implementationtype  THKeys = array[0..pred(MaxLongInt)] of byte;var  AllocationSize : integer; (*Converts a string of characters and key names to keyboard events andpasses them to Windows. Example syntax: SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);*) Function fpSendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;type  WBytes = array[0..pred(SizeOf(Word))] of Byte;   TSendKey = record    Name : ShortString;    VKey : Byte;  end; const  {Array of keys that SendKeys recognizes.  If you add to this list, you must be sure to keep it sorted alphabetically  by Name because a binary search routine is used to scan it.}   MaxSendKeyRecs = 41;  SendKeyRecs : array[1..MaxSendKeyRecs] of TSendKey =  (   (Name:'BACKSPACE';       VKey:VK_BACK),   (Name:'BKSP';            VKey:VK_BACK),   (Name:'BREAK';           VKey:VK_CANCEL),   (Name:'BS';              VKey:VK_BACK),   (Name:'CAPSLOCK';        VKey:VK_CAPITAL),   (Name:'CLEAR';           VKey:VK_CLEAR),   (Name:'DEL';             VKey:VK_DELETE),   (Name:'DELETE';          VKey:VK_DELETE),   (Name:'DOWN';            VKey:VK_DOWN),   (Name:'END';             VKey:VK_END),   (Name:'ENTER';           VKey:VK_RETURN),   (Name:'ESC';             VKey:VK_ESCAPE),   (Name:'ESCAPE';          VKey:VK_ESCAPE),   (Name:'F1';              VKey:VK_F1),   (Name:'F10';             VKey:VK_F10),   (Name:'F11';             VKey:VK_F11),   (Name:'F12';             VKey:VK_F12),   (Name:'F13';             VKey:VK_F13),   (Name:'F14';             VKey:VK_F14),   (Name:'F15';             VKey:VK_F15),   (Name:'F16';             VKey:VK_F16),   (Name:'F2';              VKey:VK_F2),   (Name:'F3';              VKey:VK_F3),   (Name:'F4';              VKey:VK_F4),   (Name:'F5';              VKey:VK_F5),   (Name:'F6';              VKey:VK_F6),   (Name:'F7';              VKey:VK_F7),   (Name:'F8';              VKey:VK_F8),   (Name:'F9';              VKey:VK_F9),   (Name:'HELP';            VKey:VK_HELP),   (Name:'HOME';            VKey:VK_HOME),   (Name:'INS';             VKey:VK_INSERT),   (Name:'LEFT';            VKey:VK_LEFT),   (Name:'NUMLOCK';         VKey:VK_NUMLOCK),   (Name:'PGDN';            VKey:VK_NEXT),   (Name:'PGUP';            VKey:VK_PRIOR),   (Name:'PRTSC';           VKey:VK_PRINT),   (Name:'RIGHT';           VKey:VK_RIGHT),   (Name:'SCROLLLOCK';      VKey:VK_SCROLL),   (Name:'TAB';             VKey:VK_TAB),   (Name:'UP';              VKey:VK_UP)  );   {Extra VK constants missing from Delphi's Windows API interface}  VK_NULL=0;  VK_SemiColon=186;  VK_Equal=187;  VK_Comma=188;  VK_Minus=189;  VK_Period=190;  VK_Slash=191;  VK_BackQuote=192;  VK_LeftBracket=219;  VK_BackSlash=220;  VK_RightBracket=221;  VK_Quote=222;  VK_Last=VK_Quote;   ExtendedVKeys : set of byte =  [VK_Up,   VK_Down,   VK_Left,   VK_Right,   VK_Home,   VK_End,   VK_Prior,  {PgUp}   VK_Next,   {PgDn}   VK_Insert,   VK_Delete]; const  INVALIDKEY = $FFFF {Unsigned -1};  VKKEYSCANSHIFTON = $01;  VKKEYSCANCTRLON = $02;  VKKEYSCANALTON = $04;  UNITNAME = 'SendKeys';var  UsingParens, ShiftDown, ControlDown, AltDown, FoundClose : Boolean;  PosSpace : Byte;  I, L : Integer;  NumTimes, MKey : Word;  KeyString : String[20]; procedure DisplayMessage(Message : PChar);begin  MessageBox(0,Message,UNITNAME,0);end; function BitSet(BitTable, BitMask : Byte) : Boolean;begin  Result:=ByteBool(BitTable and BitMask);end; procedure SetBit(var BitTable : Byte; BitMask : Byte);begin  BitTable:=BitTable or Bitmask;end; Procedure KeyboardEvent(VKey, ScanCode : Byte; Flags : Longint);var  KeyboardMsg : TMsg;begin  keybd_event(VKey, ScanCode, Flags,0);  If (Wait) then    While (PeekMessage(KeyboardMsg,0,WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do    begin      TranslateMessage(KeyboardMsg);      DispatchMessage(KeyboardMsg);    end;end; Procedure SendKeyDown(VKey: Byte; NumTimes : Word; GenUpMsg : Boolean);var  Cnt : Word;  ScanCode : Byte;  NumState : Boolean;  KeyBoardState : TKeyboardState;begin  if (VKey=VK_NUMLOCK) then  begin    NumState:=ByteBool(GetKeyState(VK_NUMLOCK) and 1);    GetKeyBoardState(KeyBoardState);    If NumState then      KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] and not 1)    else      KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] or 1);    SetKeyBoardState(KeyBoardState);    Exit;  end;   ScanCode:=Lo(Byte(MapVirtualKey(VKey,0)));  For Cnt:=1 to NumTimes do    If (VKey in ExtendedVKeys) then    begin      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);      If (GenUpMsg) then        KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)    end    else    begin      KeyboardEvent(VKey, ScanCode, 0);      If (GenUpMsg) then KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);    end;end; Procedure SendKeyUp(VKey: Byte);var  ScanCode : Byte;begin  ScanCode:=Lo(Byte(MapVirtualKey(VKey,0)));  If (VKey in ExtendedVKeys)then    KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)  else    KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);end; Procedure SendKey(MKey: Word; NumTimes : Word; GenDownMsg : Boolean);begin  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then    SendKeyDown(VK_SHIFT,1,False);  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then    SendKeyDown(VK_CONTROL,1,False);  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then    SendKeyDown(VK_MENU,1,False);  SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then    SendKeyUp(VK_SHIFT);  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then    SendKeyUp(VK_CONTROL);  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then    SendKeyUp(VK_MENU);end; {Implements a simple binary search to locate special key name strings} Function StringToVKey(KeyString : ShortString) : Word;var  Found, Collided : Boolean;  Bottom, Top, Middle : Byte;begin  Result:=INVALIDKEY;  Bottom:=1;  Top:=MaxSendKeyRecs;  Found:=false;  Middle:=(Bottom+Top) div 2;  Repeat    Collided:=((Bottom=Middle) or (Top=Middle));    If (KeyString=SendKeyRecs[Middle].Name) then    begin       Found:=True;       Result:=SendKeyRecs[Middle].VKey;    end    else    begin       If (KeyString>SendKeyRecs[Middle].Name) then        Bottom:=Middle       else        Top:=Middle;       Middle:=(Succ(Bottom+Top)) div 2;    end;  Until (Found or Collided);  If (Result=INVALIDKEY) then    DisplayMessage('Invalid Key Name');end; procedure PopUpShiftKeys;begin  If (not UsingParens) then  begin    If ShiftDown then SendKeyUp(VK_SHIFT);    If ControlDown then SendKeyUp(VK_CONTROL);    If AltDown then SendKeyUp(VK_MENU);    ShiftDown:=false;    ControlDown:=false;    AltDown:=false;  end;end; begin  AllocationSize:=MaxInt;  Result:=false;  UsingParens:=false;  ShiftDown:=false;  ControlDown:=false;  AltDown:=false;  I:=0;  L:=StrLen(SendKeysString);  If (L>AllocationSize) then L:=AllocationSize;  If (L=0) then Exit;   While (I<L) do  begin    case SendKeysString[I] of    '(' : begin            UsingParens:=True;            Inc(I);          end;    ')' : begin            UsingParens:=False;            PopUpShiftKeys;            Inc(I);          end;    '%' : begin             AltDown:=True;             SendKeyDown(VK_MENU,1,False);             Inc(I);          end;    '+' :  begin             ShiftDown:=True;             SendKeyDown(VK_SHIFT,1,False);             Inc(I);           end;    '^' :  begin             ControlDown:=True;             SendKeyDown(VK_CONTROL,1,False);             Inc(I);           end;    '{' : begin            NumTimes:=1;            If (SendKeysString[Succ(I)]='{') then            begin              MKey:=VK_LEFTBRACKET;              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);              SendKey(MKey,1,True);              PopUpShiftKeys;              Inc(I,3);              Continue;            end;            KeyString:='';            FoundClose:=False;            While (I<=L) do            begin              Inc(I);              If (SendKeysString[I]='}') then              begin                FoundClose:=True;                Inc(I);                Break;              end;              KeyString:=KeyString+Upcase(SendKeysString[I]);            end;            If (Not FoundClose) then            begin              DisplayMessage('No Close');              Exit;            end;            If (SendKeysString[I]='}') then            begin              MKey:=VK_RIGHTBRACKET;              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);              SendKey(MKey,1,True);              PopUpShiftKeys;              Inc(I);              Continue;            end;            PosSpace:=Pos(' ',KeyString);            If (PosSpace<>0) then            begin              NumTimes := StrToInt(                Copy(KeyString,Succ(PosSpace),Length(KeyString)-PosSpace)              );              KeyString:=Copy(KeyString,1,Pred(PosSpace));            end;            If (Length(KeyString)=1) then              MKey:=vkKeyScan(KeyString[1])            else              MKey:=StringToVKey(KeyString);            If (MKey<>INVALIDKEY) then            begin              SendKey(MKey,NumTimes,True);              PopUpShiftKeys;              Continue;            end;          end;    '~' : begin            SendKeyDown(VK_RETURN,1,True);            PopUpShiftKeys;            Inc(I);          end;    else      begin        MKey:=vkKeyScan(SendKeysString[I]);        if (MKey<>INVALIDKEY) then        begin          SendKey(MKey,1,True);          PopUpShiftKeys;        end        else          DisplayMessage('Invalid KeyName');        Inc(I);      end;    end;  end;  Result:=true;  PopUpShiftKeys;end; {AppActivate This is used to set the current input focus to a given window using itsname.  This is especially useful for ensuring a window is active beforesending it input messages using the SendKeys function.  You can specifya window's name in its entirety, or only portion of it, beginning fromthe left.} var  WindowHandle : HWND; function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;type  TWindowName = array[0..max_path] of char;var  WindowName : TWindowName;begin  WindowName :=default(TWindowName);  {Can't test GetWindowText's return value since some windows don't have a  title}  GetWindowText(WHandle,WindowName,MAX_PATH);  Result := (StrLIComp(WindowName,PChar(lParam), StrLen(PChar(lParam))) <> 0);  If (not Result) then    WindowHandle:=WHandle;end; function AppActivate(WindowName : PChar) : boolean;begin  try    Result:=true;    WindowHandle:=FindWindow(nil,WindowName);    If (WindowHandle=0) then      EnumWindows(@EnumWindowsProc,PtrInt(PChar(WindowName)));    If (WindowHandle<>0) then    begin      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);      SetForegroundWindow(WindowHandle);    end    else      Result:=false;  except    on Exception do Result:=false;  end;end; end.
Use: startup notepad and minimize. (In my case it is named kladblok, because I am on a dutch win64. Then run this program:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program testkeys;{$mode delphi}uses windows,sendkeys;begin  AppActivate('kladblok');  fpSendKeys('abcdefg',false);end.
Result:

Rave:
Please re-read the OP. I can't know the previous window's name or class, because what I am developing is a macro program, like this thing only free and open-source. So anything involving knowing what the previous window class and or name is is a no go for me, as I need to send the keys to an arbitrary app (usually the one that was in the focus before user clicked one of the buttons in the app).

GetMem's solution was better in that regard, but still not something I can really use.

Navigation

[0] Message Index

[#] Next page

Go to full version