Announcement

Collapse
No announcement yet.

Feedback: Sending keystrokes with SendInput

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

    Feedback: Sending keystrokes with SendInput

    I posted source code to send keystrokes to another application using the SendInput API function.

    If you should notice any strange behaviour, especially when sending characters with diacritics, could you let me know?
    Thanks.

    Kind regards
    Eddy

    #2
    Eddy,
    I don't have time to look at your code at present but unless you used some new windows api ( will your code run on Win95?) it is probably doomed to failure unless you use the Journal Playback Hook.

    I will quote reasons from the Jeffrey Richter / Jonathan Locke Book if needed.


    James

    Comment


      #3
      Originally posted by jcfuller View Post
      (will your code run on Win95?)
      --- I honestly don't know since I don't have Win95 anymore.
      MSDN says says about SendInput:
      Minimum operating systems: Windows XP, Windows NT 4.0 Service Pack 3
      Originally posted by jcfuller View Post
      ...unless you use the Journal Playback Hook.
      The Journal Playback Hook is always mentioned if someone talks about sending keystrokes to another application.
      I played with it, some years ago, and I could not get it to work reliably. Of course, that doesn't have to mean anything ..
      SendInput does what it does: sending keystrokes to the keyboard input buffer without risking that the keystrokes sequence gets interspersed with other events sent by the user.
      I have been using keyBd_Event for years with no more than a few timing problems. Of course, that was just for my own applications and not for 'consumer software'. And when reading about keyBd_Event, usually one recommends to use SendInput.

      If you know about some real-world situations where SendInput would cause problems, I am always interested to hear.

      Kind regards
      Last edited by Eddy Van Esch; 15 May 2009, 08:50 AM.
      Eddy

      Comment


        #4
        Had to remove this text from line 220 of the .inc file before the program would compile.. ' zTrace "VKKEYSCANEX error" : '

        Otherwise sems to work OK here.. WinXP SP2 English (Australia) input language, US Keyboard.
        Rgds, Dave

        Comment


          #5
          Nice addition to the the source code library. If I may make a couple of comments I think including both the key code and scan code is overkill as I suspect (without testing) that the scan code is being ignored or it could cause problems In particular I refer to the extended keys such as the function keys which require the use of the KEYEVENTF_EXTENDEDKEY flag and the KEYEVENTF_SCANCODE flag which would cause the key code to be ignored. However many extended keys like the function have valid keycodes (less than 255) so without the flags it would need to ignore the scan code for it to work. The Microsoft documentation is quite vague on the subject. Again apologies for not testing but will be travelling from tommorow and have run out of time.
          My other comment is that of course for practical use the application to receive the keystrokes must be (or forced to be) the active application as like KEYBD_EVENT it does not specify a Window handle. Some good methods to ensure that have already been posted.
          Again without testing I think it worked in 95 onwards for keyboard ANSI entry but not Unicode, handwriting etc. Don't have any computers left to test that on.

          Comment


            #6
            Originally posted by Dave Biggs View Post
            Had to remove this text ..
            Correct, Dave.
            Some leftover debugging code I could have sworn I had removed before posting. It must have snuck back in ... .

            Kind regards
            Eddy

            Comment


              #7
              Hi Eddy,
              I have some trouble with SendInput.inc
              For unknown reason (to me) some of the diacritics characters like in the line:
              REPLACE "è" WITH "`e" IN sString
              force me to save SendInput.inc in UNICODE format
              after that PB refuse to compile it. If I save it as ANSI then the line in question is saved as
              REPLACE "e" WITH "`e" IN sString
              and then ordinary e is transformed ...
              Do you have any suggestion - it seems that nobody else have the same problem. I am runing Win XP SP3 Croation locale CP1250

              Dubravko
              Last edited by Dubravko Tuckoric; 17 May 2009, 10:49 AM. Reason: typing errors
              Dubravko

              Comment


                #8
                Hi Dubravko,

                How are you saving SendInput.inc?
                I mean are you copying the text to the clipboard and then pasting it into PBEdit before saving? Perhaps you are using some other editor / method?
                Rgds, Dave

                Comment


                  #9
                  Hi Dave,
                  I hillite the text and then copy to clipboard and after that I paste to:
                  1. Notepad -> then happend what I have decribed in my previous post ( Have to save in Unicode......)
                  2. PBedit -> then conversion ofè to e is done by pasting

                  As I can see some of the diacritic characters are threted as UNICODE char. Perhaps is it an issue of copying from forum pages ??
                  Dubravko

                  Comment


                    #10
                    Hi Dubravko,

                    Strange. I have never experienced or heard this before.
                    I have attached the files in a zip file (see source code forum).
                    Try to directly open the SendInput.inc file from the zip file in PBEdit. See what that gives.

                    On your keyboard, which keys do you have to enter to create a "é" ? Also the "'" and the "e" ?

                    Kind regards
                    Eddy

                    Comment


                      #11
                      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
                      Dubravko

                      Comment


                        #12
                        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?

                        Comment


                          #13
                          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
                          Eddy

                          Comment


                            #14
                            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

                            Comment


                              #15
                              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
                              Eddy

                              Comment


                                #16
                                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.

                                Comment


                                  #17
                                  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
                                  Eddy

                                  Comment


                                    #18
                                    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

                                    Comment


                                      #19
                                      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
                                      Michael Mattias
                                      Tal Systems (retired)
                                      Port Washington WI USA
                                      [email protected]
                                      http://www.talsystems.com

                                      Comment


                                        #20
                                        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
                                        Eddy

                                        Comment

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