how to update PB's SendKeys procedure to avoid flashing

Windows specific forum
mskuma
Enthusiast
Enthusiast
Posts: 573
Joined: Sat Dec 03, 2005 1:31 am
Location: Australia

how to update PB's SendKeys procedure to avoid flashing

Post by mskuma »

I've been playing with the famous SendKeys procedure (here courtesy of PB - thanks :) ). I've set it up to send the keystrokes to Notepad upon pressing a button on my main application window (which is sticky). I believe this process works thanks to PB's attachthreadinput solution, however there is some flashing as the window loses focus and Notepad gets the focus. I think it "flashes" since it does not prevent the changing of focus to itself when clicked, and then focus goes back to the target window when the keystrokes are generated. Try the following code, and you'll see what I mean especially when you repeatedly press the 'send keys!' button. How can I avoid this 'loss of focus flashing', so it looks like Notepad has the focus all the time when pressing the 'send keys!' button. Thanks for any suggestion.

Code: Select all

; SendKeys procedure by PB -- do whatever you want with it.  :) 
; Syntax: r=SendKeys(handle,window$,keys$) ; r = 0 for failure. 
; Specify either a handle or window$ title to type to, but not both! 
; You cannot type curly braces { } as part of the keystrokes, sorry! 

Procedure SendKeys(handle,window$,keys$) 
  If window$<>"" : handle=FindWindow_(0,window$) : EndIf ; Use window$ instead of handle. 
  If IsWindow_(handle)=0 ; Does the target window actually exist? 
    ProcedureReturn 0 ; 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. 
    Sleep_(125) ; 1/8 second pause before typing to prevent fast CPU problems. 
    ; Now the actual typing starts. 
    keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0) ; Release ALT key before typing. 
    keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0) ; Release CONTROL key before typing. 
    keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) ; Release SHIFT key before typing. 
    keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0) ; Release WINDOWS key before typing. 
    For r=1 To Len(keys$) 
      vk=0 : vk$=Mid(keys$,r,1) 
      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 "ALTDOWN" : keybd_event_(#VK_MENU,0,0,0) ; Hold ALT down. 
          Case "ALTUP" : keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0) ; Release ALT. 
          Case "BACKSPACE" : vk=#VK_BACK 
          Case "CONTROLDOWN" : keybd_event_(#VK_CONTROL,0,0,0) ; Hold CONTROL down. 
          Case "CONTROLUP" : keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0) ; Release CONTROL. 
          Case "DELAY" : vk=0 : Sleep_(1000) ; Delay typing for one second. 
          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 : keybd_event_(#VK_SHIFT,0,0,0) ; Hold SHIFT down. 
          Case "SHIFTUP" : shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) ; Release SHIFT. 
          Case "TAB" : vk=#VK_TAB 
          Case "UP" : vk=#VK_UP 
          Case "WINDOWS" : vk=#VK_LWIN 
        EndSelect 
        If Left(s$,3)<>"ALT" And Left(s$,7)<>"CONTROL" And Left(s$,5)<>"SHIFT" 
          If vk<>0 
            keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0) ; Press the special key. 
          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>303 And shifted=0 : keybd_event_(#VK_SHIFT,0,0,0) : EndIf ; Due to shifted character. 
        keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0) ; Press the normal key. 
        If vk>303 And shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) : EndIf ; Due to shifted character. 
      EndIf 
    Next 
    If thread1<>thread2 : AttachThreadInput_(thread1,thread2,#False) : EndIf ; Finished typing to target window! 
    keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0) ; Release ALT key in case user forgot. 
    keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0) ; Release CONTROL key in case user forgot. 
    keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) ; Release SHIFT key in case user forgot. 
    keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0) ; Release WINDOWS key in case user forgot. 
    ProcedureReturn 1 ; Report successful typing!  :) 
  EndIf 
EndProcedure 

; -- create window, button & launch notepad

OpenWindow(0,0,0,100,75,"test")
StickyWindow(0, #True)
CreateGadgetList(WindowID(0))
btnID = ButtonGadget(#PB_Any, 10, 10, 100, 25, "send keys!")

RunProgram("notepad.exe") ; Start Notepad... 

; -- loop

Repeat 
  EventID = WaitWindowEvent() 
  Select EventID 

    Case #PB_Event_Gadget
      GadgetID = EventGadget()  ; determine gadget number that had an event
      
      If GadgetID = btnID
        Repeat : Delay(1) : Until FindWindow_(0,"Untitled - Notepad")<>0 ; ...and wait for it to open. 
        SendKeys(0,"Untitled - Notepad","Doesn't PureBasic kick serious butt! ;){ENTER}") 
      EndIf
    
    Case #PB_Event_CloseWindow 
      quit=1
      
  EndSelect
      
Until quit
real
User
User
Posts: 49
Joined: Fri Oct 08, 2004 5:17 am

Post by real »

What app should have the focus? Your app or notepad?

Notepad is getting focus because in SendKeys() there is a line

Code: Select all

SetForegroundWindow_(handle)
. The SendKeys-procedure does the following:
1. it looks for the window to type to
2. it sets the focus to thas window
3. it simulates keyboard-events to that thread.
mskuma
Enthusiast
Enthusiast
Posts: 573
Joined: Sat Dec 03, 2005 1:31 am
Location: Australia

Post by mskuma »

real wrote:What app should have the focus? Your app or notepad?
Essentially it should be notepad all the time.
Notepad is getting focus because in SendKeys() there is a line

Code: Select all

SetForegroundWindow_(handle)
I realise that, and I'd say that part needs to be modified. If you've tried the example, you'll see the focus oscillates between the app & notepad.

For a concrete example of how I'd like it to work, if you're running XP, try the following:

(1) open notepad
(2) Start -> Run... -> osk [enter] (this is the on-screen keyboard)
(3) give the notepad the focus (stick a caret in there)
(4) type some keystrokes using the on-screen keyboard

You'll see the notepad appears to have focus all the time, while the osk is on-top but not retaining focus.

Thanks.
Phoenix
Enthusiast
Enthusiast
Posts: 141
Joined: Sun Sep 04, 2005 2:25 am

Re: how to update PB's SendKeys procedure to avoid flashing

Post by Phoenix »

mskuma wrote: How can I avoid this 'loss of focus flashing', so it looks like Notepad has the focus all the time when pressing the 'send keys!' button.
The way you've written it, you can't stop Notepad losing the focus because clicking your application window will steal the focus away from Notepad. The OSK application is probably hooking the input somehow which is why Notepad doesn't lose the focus.
mskuma
Enthusiast
Enthusiast
Posts: 573
Joined: Sat Dec 03, 2005 1:31 am
Location: Australia

Post by mskuma »

Thanks Phoenix - that sounds like it makes sense.. I've heard about this kind of thing, but unsure about it's implementation - I'll do a search. If anyone can make a suggestion, I'd be appreciative. Thanks again.
mskuma
Enthusiast
Enthusiast
Posts: 573
Joined: Sat Dec 03, 2005 1:31 am
Location: Australia

Post by mskuma »

After searching & trying to investigate hooks, I am not convinced that this is the solution (but I admit to being a non-expert). What I've noticed from looking at the osk application is that when you press any of the buttons in that window, the osk application never seems to get the focus, which seems like a contradiction in terms (clicking anywhere in the window automatically gets focus). This 'lack of focus' functionality is the key thing I need, rather than a hook, I think.

[Edit: OK I finally figured it out - need to include

Code: Select all

SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(hMainWindow, #GWL_EXSTYLE) | #WS_EX_NOACTIVATE)
which offers the 'lack of focus' feature required].
Post Reply