SendKeysToWindow (windows only)

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

SendKeysToWindow (windows only)

Post 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
Last edited by infratec on Mon Oct 17, 2011 7:30 am, edited 2 times in total.
buddymatkona
Enthusiast
Enthusiast
Posts: 252
Joined: Mon Aug 16, 2010 4:29 am

Re: SendKeysToWindow (windows only)

Post 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. :)
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SendKeysToWindow (windows only)

Post 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
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SendKeysToWindow (windows only)

Post 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
MachineCode
Addict
Addict
Posts: 1482
Joined: Tue Feb 22, 2011 1:16 pm

Re: SendKeysToWindow (windows only)

Post by MachineCode »

Only in Firefox? Weird. Doesn't sound like a bug in the source above, then.
Microsoft Visual Basic only lasted 7 short years: 1991 to 1998.
PureBasic: Born in 1998 and still going strong to this very day!
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SendKeysToWindow (windows only)

Post 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
MachineCode
Addict
Addict
Posts: 1482
Joined: Tue Feb 22, 2011 1:16 pm

Re: SendKeysToWindow (windows only)

Post 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.
Microsoft Visual Basic only lasted 7 short years: 1991 to 1998.
PureBasic: Born in 1998 and still going strong to this very day!
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: SendKeysToWindow (windows only)

Post 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 ???
ImageThe happiness is a road...
Not a destination
infratec
Always Here
Always Here
Posts: 7575
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SendKeysToWindow (windows only)

Post 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
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: SendKeysToWindow (windows only)

Post 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
ImageThe happiness is a road...
Not a destination
Post Reply