Forum > Windows
Sending keystrokes to the previously focused app similar to how Autohotkey does.
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