Announcement

Collapse
No announcement yet.

Feedback: Sending keystrokes with SendInput

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Roberto Porcar
    replied
    Thanks for the replies.

    I would to show the last active window (i know this can give me problems because some text editors change the name of the title when the file is modified and not saved).

    Code:
    'Compilable Example:
    #COMPILER PBWIN 10
    #COMPILE EXE
    #DIM ALL
    %Unicode=1 : %IDC_Button = 500
    #INCLUDE "win32api.inc"
    
    %ID_Timer1 = 500
    
    GLOBAL hDlg,hFound AS DWORD, szText AS WSTRINGZ * %MAX_PATH, temp AS WSTRINGZ * %MAX_PATH
    
    GLOBAL hForeground AS DWORD
    
    
    FUNCTION PBMAIN() AS LONG
       hForeground = GetForegroundWindow()
       DIALOG NEW PIXELS, 0, "Get Last Active Window",300,300,420,100,%WS_OVERLAPPEDWINDOW TO hDlg
       CONTROL ADD LABEL, hDlg, 205, "Start", 5, 50, 400, 15, %SS_LEFT
       CONTROL ADD BUTTON, hDlg, %IDC_Button, "Check now!", 10,10,120,20
       DIALOG SHOW MODAL hDlg CALL DlgProc
    END FUNCTION
    
    CALLBACK FUNCTION DlgProc() AS LONG
       SELECT CASE CB.MSG
    
          CASE %WM_INITDIALOG
             SetTimer(CB.HNDL, %ID_Timer1, 500, BYVAL %NULL)   'uses callback messages
    
          CASE %WM_TIMER
             IF CB.WPARAM = %ID_Timer1 THEN
                   hForeground = GetForegroundWindow()
                   GetWindowText(hForeground, temp, SIZEOF(szText))
                   IF temp <> "Get Last Active Window" THEN
                     szText = temp
                   END IF
                   CONTROL SET TEXT hDlg, 205, szText
             END IF
    
          CASE %WM_COMMAND
             IF CB.CTL = %IDC_Button AND CB.CTLMSG = %BN_CLICKED THEN
                 GetWindowText(hForeground, szText, SIZEOF(szText))
                 'msgbox szText
                 CONTROL SET TEXT hDlg, 205, szText
             END IF
       END SELECT
    END FUNCTION
    this code its all that i get until now, based on a Gary sample. ill try to see first if the last focused window exists and if not found ill try to sendkeys to the "alt+tab" window.

    But, whats the best choice to know about some window was focused? i have to set a timer and check at some fast intervals or there is some api?

    feel free to atack my code... this is my first PB project.
    Last edited by Roberto Porcar; 9 Aug 2012, 04:14 PM.

    Leave a comment:


  • S Stamp
    replied
    if you know the window title ( or a subset of the window title) of the target window to receive the keystrokes, then Eddy right that the best way to activate the target window is the of the API. ( note that if you have two windows of the same name, then the API will activate the one that was most recently active... which is the behavior that we want).

    Here is a post with another sample program for activating the target window and then sending keystrokes to it


    But if the program needs to be more flexible to send keystrokes to the previous active window regardless of that window's title, then the only solution that I found is to first send keystrokes to alt-tab to that window, allowing nominal sleep, and then send the rest of the keystrokes... just like you proposed.

    Leave a comment:


  • Eddy Van Esch
    replied
    Roberto,

    In this link:


    Look for function 'AppActivate'. It allows you to specify a window caption name to make that window receive focus.

    Kind regards

    Leave a comment:


  • Roberto Porcar
    replied
    Im thinking about how to send the keystrokes to the previous focused window (i want to do a toolbar and i wish to send the keystrokes to the notepad).

    One choice is send this string "{ALT_D}{TAB}{ALT_U}" with a small delay, and start sending the keystrokes, like this:

    Code:
              WaitForNoKeys  'use this to wait for user to let go of keys (so our keys dont end up as ctrl keys)
              SendString "{ALT_D}{TAB}{ALT_U}"
              SLEEP 100
              SendString "Hello World{ENTER}"
    is there some elegant way to do this?

    i post an image for clarify, sorry my english.

    still no code, this is a mockup (no real windows injured during my tests)


    PD: another question, should i translate REDIM nAtom(1:2) to REDIM nAtom(2) OR (1 TO 2) to compile with pb 10.3?

    Code:
        STATIC nAtom()  AS DWORD
        LOCAL  sText    AS STRING
        LOCAL  iRet     AS LONG
        LOCAL  iCount   AS LONG
    
        SELECT CASE CBMSG
            CASE %WM_INITDIALOG
                iRet = TIMER
                REDIM nAtom(1:2)  ' HERE I GET A COMPILER ERROR
                nAtom(1) = GLOBALADDATOM ("SendInput_demo2" + STR$(iRet + 1))
                nAtom(2) = GLOBALADDATOM ("SendInput_demo2" + STR$(iRet + 2))
                REGISTERHOTKEY CBHNDL, nAtom(1), 2, ASC("Y") ' Ctrl-J
                REGISTERHOTKEY CBHNDL, nAtom(2), 2, ASC("K") ' Ctrl-K
    Attached Files
    Last edited by Roberto Porcar; 9 Aug 2012, 09:26 AM.

    Leave a comment:


  • Gary Beene
    replied
    Thanks Eddy,
    I'll be away from my desk for a bit (tennis match) but will look at it again when I return.

    Leave a comment:


  • Eddy Van Esch
    replied
    Originally posted by Gary Beene View Post
    Hi Eddy!
    I was giving your code a try, but I'm not getting any results.
    What should I be seeing?
    If you have Notepad open and it has focus, pressing CTRL J should paste the following 4 lines of text in Notepad:
    First line
    next line
    PowerBASIC
    éééé
    Pressing CTRL K should bring up an inputbox asking to enter some text. Pressing enter closes the inputbox and pastes the entered text in the active text window (Notepad).

    I just tried in Win XP and Win 7 and works fine here.
    Maybe the hotkey combinations CTRL J and CTRL K are already used on your system by another application?
    Try replacing DlgProc of my little demo program by this DlgProc.
    When you run the program, you have 9 seconds to make sure Notepad (or another text window) has focus.
    It will paste the lines of text without you having to do anything.

    Code:
    '-------------------------------------------------------------------------
    CALLBACK FUNCTION DlgProc
        STATIC nAtom()  AS DWORD
        LOCAL  sText    AS STRING
        LOCAL  iRet     AS LONG
        LOCAL  iCount   AS LONG
       
        SELECT CASE CBMSG
            CASE %WM_INITDIALOG
            
                SLEEP 9000   'Make sure Notepad has focus before this time has elapsed...
                WaitForNoKeys  'use this to wait for user to let go of keys (so our keys dont end up as ctrl keys)
                SendString "First-{BACKSPACE} line{ENTER}next line{ENTER}"
                SendString "Pooo{BACKSPACE}{BACKSPACE}werBASIC{ENTER}" 
                SendString "éééé{ENTER}"
                     
    END FUNCTION
    Kind regards

    Leave a comment:


  • Gary Beene
    replied
    Hi Eddy!
    I was giving your code a try, but I'm not getting any results.

    I opened a small PB app that has nothing but an edit control. Then I started your app on my Win7 PC, and pressed Ctrl J. When I keep typing, nothing appears.

    What should I be seeing?

    I also tried opening 2 instances of the app as well. Same results.

    Likewise I opened Notepad and then pressed Ctrl-J. Nothing.

    Can you give me some guidance?

    Leave a comment:


  • Michael Mattias
    replied
    Oops, I thought one of my demos showed WaitForInputIdle... but it doesn't.
    Last edited by Michael Mattias; 29 May 2009, 08:56 AM. Reason: My bad

    Leave a comment:


  • Eddy Van Esch
    replied
    Originally posted by Michael Mattias View Post
    ..why don't you turn to the pages where GetWindowThreadProcessID() and OpenProcess() are described?
    I did exactly that and those indeed seem like the functions required. Thanks !

    Kind regards

    Leave a comment:


  • Michael Mattias
    replied
    > Would it be possible to find that handle by enumerating windows

    As long as you have your WinAPI reference open today, why don't you turn to the pages where GetWindowThreadProcessID() and OpenProcess() are described?

    Leave a comment:


  • Eddy Van Esch
    replied
    Michael,

    I did not know that API function (WaitForInputIdle).
    Of course, you do have to know the process handle.
    Would it be possible to find that handle by enumerating windows (for which you 'only' have to know caption text and class name)?

    Enumerating windows can also be useful to detect when or if a window exists.
    For example: you send a menu command which should open a dialog or window. The time for that window to actually open might vary. After sending the menu command you can start enumerating windows until that particular window exists. Then you can send the next series of keystrokes.

    Kind regards

    Leave a comment:


  • Michael Mattias
    replied
    But that is exactly a weakness of keybd_event since you often have to finetune it from one pc to another.
    Maybe... but if you want to know the receiving application is ready to process an input command, you can call WaitForInputIdle() against a handle to that process . (Only works if target application is a GUI application).


    MCM

    Leave a comment:


  • S Stamp
    replied
    Eddy

    I already played with the Time parameter and also found that it did not make a difference. I'd like to continue to use the SendInput API, so I'll just divide up the keystroke string so that I send it to the routine one key at a time. When I'm done adding it to my code (which calls your .inc), I'll post my code to the same thread with your file. My program takes the keystroke sequence (and keystroke delay, after these changes) from the commandline and then outpulses them to the active window via your routine.

    Thanks again,

    Scott

    Leave a comment:


  • Eddy Van Esch
    replied
    Scott,

    The API SendInput command receives all the keystrokes together and then sends them. In contrast to keybd_event which sends one keystroke at the time.
    With keybd_event you can put time delays in between multiple keystrokes. But that is exactly a weakness of keybd_event since you often have to finetune it from one pc to another.

    The keystrokes buffer of the API SendInput command has a 'Time' parameter: inpAPI(lBufCnt).m.ki.time
    According to MSDN you can specify a time stamp. It doesn't say exactly what this time stamp does.
    I experimented with this some time ago, to see if it influences the keystroke feeding rate, but I did not see much difference. Maybe you can try again and see if you get better results.
    If that does not work, the only solution I see is what you already suggested: to split up your keystrokes in different chunks and insert time delays (SLEEP ..) in between them.
    Let me know if you find anything.

    Kind regards

    Leave a comment:


  • S Stamp
    replied
    Thanks Eddy. This works really nice.

    Can you give me any advice regarding how to slow down the rate that the keystrokes outpulsed?

    If I'm sending the keystrokes to an application that is slower to respond, I might want to make sure that the keys are sent at a slower rate than normal (e.g., to ensure that the keystrokes to open a menu have been processed before the application receives the subsequent keystrokes for selecting the menu item).

    In my commercial macro software, I simply configure it such that any outpulsed keys use my specified delay between each simulated keystroke (eg, 10 ms).

    I guess I could create a wrapper around your program that takes my desired string, divides it up one key at a time, and then calls your SUB for each individual key. That way, I could use the power basic sleep command in between calls to your SUB to introduce inter-keystroke delay.

    Before I did that, I wanted to see if you had a more elegant suggestion for a way to control the keystroke speed in the SUB.

    Leave a comment:


  • Eddy Van Esch
    replied
    Scott,

    I already added some more of the special keys, but have now added all that you listed.
    Thanks for your input !

    Source code is updated (see link in my first post in this thread).

    Kind regards

    Leave a comment:


  • S Stamp
    replied
    Eddy

    If you are updating the .inc file and wish to incorporate the definitions for the remaining special keys, here are the case statements that I added to the file.

    Scott

    CASE "TAB" : bVkCode = %VK_TAB : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "ESCAPE" : bVkCode = %VK_ESCAPE : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "PGUP" : bVkCode = %VK_PGUP : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "PGDN" : bVkCode = %VK_PGDN : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    'arrow keys
    CASE "LEFT" : bVkCode = %VK_LEFT : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "RIGHT" : bVkCode = %VK_RIGHT : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "UP" : bVkCode = %VK_UP : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "DOWN" : bVkCode = %VK_DOWN : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    CASE "END" : bVkCode = %VK_END : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "HOME" : bVkCode = %VK_HOME : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    CASE "F1" : bVkCode = %VK_F1 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F2" : bVkCode = %VK_F2 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F3" : bVkCode = %VK_F3 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F4" : bVkCode = %VK_F4 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F5" : bVkCode = %VK_F5 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F6" : bVkCode = %VK_F6 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F7" : bVkCode = %VK_F7 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F8" : bVkCode = %VK_F8 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F9" : bVkCode = %VK_F9 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F10" : bVkCode = %VK_F10 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F11" : bVkCode = %VK_F11 : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "F12" : bVkCode = %VK_F12 : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    CASE "INSERT" : bVkCode = %VK_INSERT : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "DELETE" : bVkCode = %VK_DELETE : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "APPS" : bVkCode = %VK_APPS : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "NUMLOCK" : bVkCode = %VK_NUMLOCK : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    'caps lock
    CASE "CAPITAL" : bVkCode = %VK_CAPITAL : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    ' i did not see a normal win key. only left win and right win, so I defined it as left win
    CASE "WIN_D" : bVkCode = %VK_LWIN : GOSUB bVkCode_Down
    CASE "WIN_U" : bVkCode = %VK_LWIN : GOSUB bVkCode_Up

    Leave a comment:


  • Eddy Van Esch
    replied
    Hi ..'S' ..

    Yep, I noticed the same problem here.
    If I compare the keystrokes that we send, and that Windows actually generates, I see a difference when using the HOME and/or the RIGHT keys in combinations with SHIFT_D and SHIFT_U.

    If SHIFT_D is generated with HOME or RIGHT, Windows actually sends a WM_KEYDOWN, immediately followed by a WM_KEYUP (which we did not send!).

    With other 'standard' keys, SHIFT_D and SHIFT_U work as they should.

    I searched the internet and found a number of references to this problem, what often is referred to as the 'SHIFT-ARROW' problem.

    Fortunately, I also found a solution. Atleast it worked with your example.
    It seems that the HOME, END, INSERT, DELETE and the arrow keys are extended keys.
    They require the extended keys flag to be set.

    I will clean up my code and post an update asap.

    Kind regards

    Leave a comment:


  • S Stamp
    replied
    problem with key combinations involving shift

    Eddy,

    The sample you provided for outpulsing keystrokes is very useful, and I've been able to rewrite some of the macros and scripts I previously created in windows automation software (Macro Express and autohotkey) in power basic, which gives me more flexibility and better performance for certain types of operations than the macro application packages.

    But I discovered a problem with sending the keystroke for the shift key, which really limits me.

    I added (to the .inc file) the remaining special keyboard characters, such as the right arrow key and home key
    CASE "RIGHT" : bVkCode = %VK_RIGHT : GOSUB bVkCode_Down : GOSUB bVkCode_Up
    CASE "HOME" : bVkCode = %VK_HOME : GOSUB bVkCode_Down : GOSUB bVkCode_Up

    In general, the additional special keys work properly, however they do not work as key combinations with shift.

    For example, if I run your sample program and execute control k to send the following sequence (where the program will outpulse the keys to my text editor):
    {SHIFT_D}test{SHIFT_U}
    the program gives me the expected outcome. That is, the word
    TEST
    in uppercase is typed into the text editor.

    However, if I send the following sequence:
    test{SHIFT_D}{HOME}{RIGHT}{SHIFT_U}
    the result should be the word
    test
    with the letters "est" highlighted as the selection
    But instead, I get a different result: The word test with the cursor in between "t" and "e" and no highlighted selection.
    It is as if the program has completely ignored the fact that the shift key is down while the other keys like home or right are being pressed. I tried variations, like having only the home key or only the arrow key in between the shift down and shift up. It did not make a difference.

    Note that the following sequence
    test{SHIFT_D}{RIGHT}again{SHIFT_U}
    does produce the word AGAIN in uppercase, so I know that the shift key is still down (and that the arrow key is not somehow forcing the shift key to automatically release)

    Do you have any suggestions for how to fix this?

    Leave a comment:


  • Dubravko Tuckoric
    replied
    Hi Eddy,
    I have downloaded a zip and test program now compiles. On first view test program is OK. But when I looked at SendInput.inc same diacritics characters looked like I suppose was not meant to be. Attached is the JPG picture of thet replace part of code so you can compare it with what you see on your screen.
    I suspect that cause of this differencies is in REGIONAL setings. On my PC I have CROATIAN regional setings - that means I am using Code page 1250, and my keyboard layout is Croatian. Visual presentations of characters is affected by code page so I think that sendinput rutine will run differently (for same diacritics characters) on PC-s with different code page than yours. To make it universal it will be a little bit tricky. For characters without diacritics all will work fine.

    Dubravko
    Attached Files

    Leave a comment:

Working...
X
😀
🥰
🤢
😎
😡
👍
👎