Page 1 of 1

SendKeysToWindow (windows only)

Posted: Mon Oct 10, 2011 8:13 pm
by infratec
Hi,

inspired from some codes her arround, I created a working (and maybe usefull) standalone application.

You can use it as a single command, or with an optional file as a tool to automate some things.
I wrote this, because I had to automate a file upload to a web shop which is using https.
So it was not possible for me to detect the exact parameters of the web pages.
That was the reasopn for this tool.

Save it as SendKeysToWindow.pb

Code: Select all

;
; SendKeysToWindow.pb
;
; based on code from PB, Danilo, ts-soft and freepurebasic
;
; extended to a standalone console program
; extended the DELAY command with an optional delay value {DELAY}{1.5}
; addded RUN command {RUN}{notepad}
; replaced 'superseeded' keybd_event() with SendInput()
;
; added additional delay for fast CPUs and windows 7

Structure FindWindowData
  hFW.l ; variable to store a handle
  sFW.s ; variable to store a Window name
  cFW.s ; variable to store a window class name
EndStructure

Global NewList WindowList.FindWindowData()


Procedure.l EnumWindowsCallBack(hFind, lParam)
  WindowName.s = Space(255)
  WindowClass.s = Space(255)
  If GetWindowText_(hFind, WindowName, 255)
   
    If FindString(WindowName, "SendKeysToWindow", 1) = 0 ; not our own window !!!
      Result = GetClassName_(hFind, WindowClass, 255)
      AddElement(WindowList())
      WindowList()\hFW = hFind
      WindowList()\sFW = WindowName
      WindowList()\cFW = WindowClass
    EndIf
   
  EndIf
 
  ProcedureReturn #True
 
EndProcedure


Procedure SendKeys(handle, window$, keys$)
 
  Dim InputData.INPUT(10)
  
  If window$ <> "" : handle = FindWindow_(0, window$) : EndIf ; Use window$ instead of handle.
  
  If IsWindow_(handle) = 0 ; Does the target window actually exist?
    ProcedureReturn #False ; Nope, so report 0 for failure to type.
  Else
    ; This block gives the target window the focus before typing.
    thread1 = GetWindowThreadProcessId_(GetForegroundWindow_(), 0)
    thread2 = GetWindowThreadProcessId_(handle, 0)
   
    If thread1 <> thread2 : AttachThreadInput_(thread1, thread2, #True) : EndIf
   
    SetForegroundWindow_(handle) ; Target window now has the focus for typing.
   
    Delay(250) ; 1/4 second pause before typing to prevent fast CPU problems.
   
    ; Now the actual typing starts.
    
CompilerIf #True
    ; Release ALT key before typing.
    InputData(0)\Type = #INPUT_KEYBOARD
    InputData(0)\ki\wVk = #VK_MENU
    InputData(0)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release CONTROL key before typing.
    InputData(1)\Type = #INPUT_KEYBOARD
    InputData(1)\ki\wVk = #VK_CONTROL
    InputData(1)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release SHIFT key before typing.
    InputData(2)\Type = #INPUT_KEYBOARD
    InputData(2)\ki\wVk = #VK_SHIFT
    InputData(2)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release WINDOWS key before typing.
    InputData(3)\Type = #INPUT_KEYBOARD
    InputData(3)\ki\wVk = #VK_LWIN
    InputData(3)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ;SendInput_(4, InputData(), SizeOf(INPUT))
    For n = 0 To 3
      SendInput_(1, @InputData(n), SizeOf(INPUT))
      Delay(20)
    Next n
CompilerEndIf
   
    For r = 1 To Len(keys$)      
      
      InputCounter = 0
     
      vk = 0 : vk$ = Mid(keys$,r,1)
     
;      PrintN("+" + vk$ + "+" + Str(r))
     
      If vk$="{" ; Special key found.
        s = FindString(keys$, "}", r + 1) - (r + 1) ; Get length of special key.
        s$ = Mid(keys$, r + 1, s) ; Get special key name.
        Select s$ ; Get virtual key code of special key.
          Case "DELAY"
            vk = 0
            If Mid(keys$, r + 7, 1) = "{"
              help = FindString(keys$, "}", r + 7)
              Time$ = Mid(keys$, r + 8, help - r - 8)
;             PrintN("-" + Time$ + "-")
              Time = ValF(Time$) * 1000
              s + Len(Time$) + 2
            Else
              Time = 1000
            EndIf
            ;PrintN("Delay: " + Str(Time))
            Delay(Time) ; Delay typing for one second.
          Case "RUN"
            vk = 0
            If Mid(keys$, r + 5, 1) = "{"
              help = FindString(keys$, "}", r + 5)
              Program$ = Mid(keys$, r + 6, help - r - 6)
              ;PrintN("-" + Program$ + "-")
              s + Len(Program$) + 2
              ;PrintN("Program: " + Program$)
              RunProgram(Program$) ; Start the program
            EndIf
          Case "ALTDOWN"
            alted = 1
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_MENU
            InputData(InputCounter)\ki\dwFlags = 0
            InputCounter + 1
          Case "ALTUP"
            alted = 0
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_MENU
            InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
            InputCounter + 1
          Case "BACKSPACE"    : vk = #VK_BACK
          Case "CONTROLDOWN"
            ctrled = 1
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_CONTROL
            InputData(InputCounter)\ki\dwFlags = 0
            InputCounter + 1
          Case "CONTROLUP"
            ctrled = 0
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_CONTROL
            InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
            InputCounter + 1
          Case "DELETE"       : vk = #VK_DELETE
          Case "DOWN"         : vk = #VK_DOWN
          Case "END"          : vk = #VK_END
          Case "ENTER"        : vk = #VK_RETURN
          Case "ESCAPE"       : vk = #VK_ESCAPE
          Case "F1"           : vk = #VK_F1
          Case "F2"           : vk = #VK_F2
          Case "F3"           : vk = #VK_F3
          Case "F4"           : vk = #VK_F4
          Case "F5"           : vk = #VK_F5
          Case "F6"           : vk = #VK_F6
          Case "F7"           : vk = #VK_F7
          Case "F8"           : vk = #VK_F8
          Case "F9"           : vk = #VK_F9
          Case "F10"          : vk = #VK_F10
          Case "F11"          : vk = #VK_F11
          Case "F12"          : vk = #VK_F12
          Case "HOME"         : vk = #VK_HOME
          Case "INSERT"       : vk = #VK_INSERT
          Case "LEFT"         : vk = #VK_LEFT
          Case "PAGEDOWN"     : vk = #VK_NEXT
          Case "PAGEUP"       : vk = #VK_PRIOR
          Case "PRINTSCREEN"  : vk = #VK_SNAPSHOT
          Case "RIGHT"        : vk = #VK_RIGHT
          Case "SCROLL"       : vk = #VK_SCROLL
          Case "SPACE"        : vk = #VK_SPACE
          Case "SHIFTDOWN"
            shifted = 1
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_SHIFT
            InputData(InputCounter)\ki\dwFlags = 0
            InputCounter + 1
          Case "SHIFTUP"
            shifted = 0
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = #VK_SHIFT
            InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
            InputCounter + 1
          Case "TAB"          : vk = #VK_TAB
          Case "UP"           : vk = #VK_UP
          Case "LWINDOWS"     : vk = #VK_LWIN
          Case "RWINDOWS"     : vk = #VK_RWIN
          Case "AWINDOWS"     : vk = #VK_APPS
        EndSelect
       
        If Left(s$, 3) <> "ALT" And Left(s$, 7) <> "CONTROL" And Left(s$, 5) <> "SHIFT"
          If vk <> 0
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = vk
            InputData(InputCounter)\ki\dwFlags = 0
            InputCounter + 1
           
            InputData(InputCounter)\Type = #INPUT_KEYBOARD
            InputData(InputCounter)\ki\wVk = vk
            InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
            InputCounter + 1
          EndIf
        EndIf
       
        r + s + 1 ; Continue getting the keystrokes that follow the special key
       
      Else

        vk = VkKeyScanEx_(Asc(vk$), GetKeyboardLayout_(0)) ; Normal key found
       
        If (vk & $0100) <> 0 And shifted = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_SHIFT
          InputData(InputCounter)\ki\dwFlags = 0
          InputCounter + 1 
        EndIf
       
        If (vk & $0200) <> 0 And ctrled = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_CONTROL
          InputData(InputCounter)\ki\dwFlags = 0
          InputCounter + 1
        EndIf
       
        If (vk & $0400) <> 0 And alted = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_MENU
          InputData(InputCounter)\ki\dwFlags = 0
          InputCounter + 1
        EndIf
       
       
        ; Press the normal key.
       
        InputData(InputCounter)\Type = #INPUT_KEYBOARD
        InputData(InputCounter)\ki\wVk = vk
        InputData(InputCounter)\ki\dwFlags = 0
        InputCounter + 1
       
        InputData(InputCounter)\Type = #INPUT_KEYBOARD
        InputData(InputCounter)\ki\wVk = vk
        InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
        InputCounter + 1
       
       
        If (vk & $0100) <> 0 And shifted = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_SHIFT
          InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
          InputCounter + 1
        EndIf
       
        If (vk & $0200) <> 0 And ctrled = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_CONTROL
          InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
          InputCounter + 1
        EndIf
       
        If (vk & $0400) <> 0 And alted = 0
          InputData(InputCounter)\Type = #INPUT_KEYBOARD
          InputData(InputCounter)\ki\wVk = #VK_MENU
          InputData(InputCounter)\ki\dwFlags = #KEYEVENTF_KEYUP
          InputCounter + 1
        EndIf
       
      EndIf
     
      If InputCounter
        ;        SendInput_(InputCounter, InputData(), SizeOf(INPUT))
        InputCounter - 1
        For n = 0 To InputCounter
          SendInput_(1, @InputData(n), SizeOf(INPUT))
          Delay(20)
        Next n
      EndIf
     
    Next
   
    If thread1 <> thread2 : AttachThreadInput_(thread1, thread2, #False) : EndIf ; Finished typing to target window!
    
CompilerIf #True
    ; Release ALT key in case user forgot.
    InputData(0)\Type = #INPUT_KEYBOARD
    InputData(0)\ki\wVk = #VK_MENU
    InputData(0)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release CONTROL key in case user forgot.
    InputData(1)\Type = #INPUT_KEYBOARD
    InputData(1)\ki\wVk = #VK_CONTROL
    InputData(1)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release SHIFT key in case user forgot.
    InputData(2)\Type = #INPUT_KEYBOARD
    InputData(2)\ki\wVk = #VK_SHIFT
    InputData(2)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ; Release WINDOWS key in case user forgot.
    InputData(3)\Type = #INPUT_KEYBOARD
    InputData(3)\ki\wVk = #VK_LWIN
    InputData(3)\ki\dwFlags = #KEYEVENTF_KEYUP
   
    ;SendInput_(4, InputData(), SizeOf(INPUT))
    For n = 0 To 3
      SendInput_(1, @InputData(n), SizeOf(INPUT))
      Delay(20)
    Next n
CompilerEndIf
   
    ProcedureReturn #True ; Report successful typing!  :)
   
  EndIf
 
EndProcedure


Procedure ProcessLine(Line$)
  Line$ = Trim(Line$)           ; remove spaces
  If Len(Line$) > 0             ; if something available
    If Left(Line$, 1) <> ";"    ; not a comment line
      FirstSpacePos = FindString(Line$, " ", 1)
      If FirstSpacePos
        Window$ = Left(Line$, FirstSpacePos - 1)
        Command$ = Mid(Line$, FirstSpacePos + 1)
       
        ; at the commandline starting and ending quote is eliminated
        ; to have the same behaviour in a file, the following was added:
        If Left(Command$, 1) = #DQUOTE$
          If Right(Command$, 1) = #DQUOTE$
            Command$ = Mid(Command$, 2, Len(Command$) - 2)
          EndIf
        EndIf
       
        Print(Window$ + "-")
        PrintN(Command$)
       
        ClearList(WindowList())
        If EnumWindows_(@EnumWindowsCallBack(), 0)
          ResetList(WindowList())
          While NextElement(WindowList())
            ;PrintN("Window: " + WindowList()\sFW)
            If FindString(WindowList()\sFW, Window$, 1)
              ;PrintN("Window: " + WindowList()\sFW)
              ;SendKeys(0, WindowList()\sFW, Command$)
              SendKeys(WindowList()\hFW, "", Command$)
              Break
            EndIf
          Wend
        EndIf
       
      EndIf
    EndIf
  EndIf
EndProcedure




If OpenConsole()
 
  Parameters = CountProgramParameters()
  If Parameters < 2
    PrintN("")
    PrintN("usage: SendKeysToWindow windowname keys")
    PrintN("   or: SendKeysToWindow -f filename")
    PrintN("")
  Else
   
    If ProgramParameter(0) = "-f"
      If FileSize(ProgramParameter(1))
        File = ReadFile(#PB_Any, ProgramParameter(1))
        If File
          While Not Eof(File)
            Line$ = ReadString(File)
            ProcessLine(Line$)
          Wend
          CloseFile(File)
        EndIf
      EndIf
    Else
      ProcessLine(ProgramParameter(0) + " " + ProgramParameter(1))
    EndIf
   
  EndIf
 
  CloseConsole()
 
EndIf
Here a small example:

Code: Select all

SendKeysToWindow Editor Hallo
or better

Save this as test.dat

Code: Select all

Manager {RUN}{notepad}{DELAY}
Editor Hello{ENTER}{DELAY}{2}
Editor That's a simple test{ENTER}{DELAY}{3.5}
Editor {ALTDOWN}{F4}{ALTUP}
Editor {TAB}{ENTER}
and run

Code: Select all

SendKeysToWindow -f test.dat
Have fun,

Bernd

P.S.: Maybe you have to find an other window than Manager.
But on my XP this 'window' is always available and so I use it as a dummy.

P.S.S: If you want more control search for AutoWin from ts-soft

Re: SendKeysToWindow (windows only)

Posted: Tue Oct 11, 2011 3:48 pm
by buddymatkona
@infratec
Thanks a lot for sharing. Much of this code will plug right into a ThreadTalk procedure I need. All I had to change for a quick test on Win7/PB64 was a wildcard match for the window name. Notepad windows with names like "Filename - Notepad" come complete with embedded blanks. :)

Re: SendKeysToWindow (windows only)

Posted: Tue Oct 11, 2011 3:59 pm
by infratec
Hi,

since I use FindString() you don't need the full name of the window.
A distinct part of it is enough :mrgreen:
So you can also use 'pad'

Bernd

Re: SendKeysToWindow (windows only)

Posted: Sun Oct 16, 2011 11:13 am
by infratec
Hi,

I had to adjust the code above.
The origial code runs in problems with a fast CPU Win 7 64bit and firefox 7.01.
The 'typed' filename in the openfilerequester of firefox suddenly stops.

Only possible workaround was to slow all things down.

Now I send every keystroke separated with a short delay between.

Bernd

Re: SendKeysToWindow (windows only)

Posted: Sun Oct 16, 2011 11:33 am
by MachineCode
Only in Firefox? Weird. Doesn't sound like a bug in the source above, then.

Re: SendKeysToWindow (windows only)

Posted: Sun Oct 16, 2011 1:17 pm
by infratec
MachineCode wrote:Only in Firefox? Weird. Doesn't sound like a bug in the source above, then.
I don't know if 'only in Firefox', but there it appears.
And only in the open file requester.
Inputs in the address line worked without any problems.

But at least it works now :D

Bernd

Re: SendKeysToWindow (windows only)

Posted: Sat Nov 12, 2011 4:26 pm
by MachineCode
I was just trying to use this to send the Escape key to the window of Crysis (PC game), but nothing happened. I tested with different keys, and Crysis ignores them all. Does anyone know why that would be? I can use it to type to other windows without issue. Would Crysis perhaps be preventing virtual keystrokes and reading the PC keyboard directly via hardware or something? I note that it works with another shooter, Unreal, without any problem.

Re: SendKeysToWindow (windows only)

Posted: Sat Nov 12, 2011 7:32 pm
by Kwai chang caine
Your splendid code not works for me with XP :(
The notepad is open, but nothing happened

Perhaps because i'm french, and the notepad have not the same name in the titlebar that english version ???

Re: SendKeysToWindow (windows only)

Posted: Sat Nov 12, 2011 11:00 pm
by infratec
Hi,
Kwaï chang caïne wrote:Perhaps because i'm french, and the notepad have not the same name in the titlebar that english version ???
That's the reason.
You have to adjust the name so that it is (at least) a part of the window name.

Is it possible that it is called 'Bloc-notes' in french XP?
If so, you can use 'notes' instead of 'Editor' and it should work.

Bernd

Re: SendKeysToWindow (windows only)

Posted: Sun Nov 13, 2011 7:28 pm
by Kwai chang caine
Thanks a lot INFRATEC, it's exactely that, that's works fine now 8)
And your code is again better, when it's works :D

I wish you a good day