Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Sending keystrokes with SendInput

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

  • Sending keystrokes with SendInput

    Save this file as "SendInput.inc".
    See demo program in next post.

    Code:
    '===============================================================================
    ' SendInput.inc
    ' 2009 - Eddy Van Esch
    ' 
    ' History:
    ' --------
    ' V1.0: 14-May-2009 : Initial version
    ' 
    ' V1.1: 27-May-2009 : 
    '     - Fixed a bug where SHIFT_D with HOME or an arrow key combination produced
    '       incorrect results. The shift key appeared to be ignored.
    '       Thanks to S. Stamp for reporting the problem.
    '
    '       Apparently, the HOME and arrow keys (and a number of others) are extended keys and
    '       they require the extended key flag to be set.
    '       A forum thread about the problem:
    '       http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21737163.html
    '     - Added more special keys (see list below)
    '     - SUB WaitForNoKeys slightly modified
    '
    ' ---------------------------------------------------------------------------
    ' This software allows to send keystrokes to the application that
    ' currently has focus.
    ' Rather than using KEYBD_EVENT, this routine uses SENDINPUT.
    ' The advantage is that when using SendInput, the sent keystrokes can not
    ' be accidently mixed with keystrokes that the user is entering on the keyboard.
    ' Also, there is no problem with timings between multiple Keybd_Event commands.
    ' This routine also handles characters with diacritics, like é,è,ê,ñ, etc. (See **)
    ' Writing this code, I took inspiration (and a few lines of code :-)) from
    ' William Burns' file 'SendKeys.inc'
    ' Not all possible special keys are implemented yet, but it is easy to add more.
    '
    'Sub WaitForNoKeys was reused from William Burns' file 'SendKeys.inc'. Thanks William!
    '
    '----------------------------
    '**  IMPORTANT !!!
    'Although SendString should handle strings with diacritics properly, other active programs
    'can mess this up. Especially programs that use a global keyboard hook, such
    'as AutoHotKey for example.
    'The reason is that these programs use the ToAscii(Ex) API function in their keyboard hook.
    'Using ToAscii(Ex) in a hook messes up diacritics handling.
    'What you then will notice is an inconsistent behaviour when sending characters with diacritics.
    'For example, sending "éééé" could result in "eeéé", "''eé" or some other inconsistent result.
    'Very annoying. The only solution is to terminate the application that is using the global hook. 
    '----------------------------
    '
    '===============================================================================
    
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "win32api.inc"                        'add win32api if not already added
    #ENDIF
    
    
    '-------------------------------------------------------------------------
    'This sub places the characters in 'sString' in the keyboard buffer. Then, the characters are sent to
    'whatever application currently has focus.
    'So, this sub allows to simulate keyboard input.
    'Apart from normal ascii characters, SendString can also handle some special characters:
    '
    'The special characters currently implemented are:
    '{CTRL_D}, {CTRL_U}, {ALT_D}, {ALT_U}, {SHIFT_D}, {SHIFT_U}, {WIN_D}, {WIN_U}, {BACKSPACE}
    '{ENTER}, {INSERT}, {DELETE}, {RIGHT}, {LEFT}, {UP}, {DOWN}, {HOME}, {END}, {TAB}, {ESCAPE} 
    '{PGUP}, {PGDN}, {F1}, {F2}, {F3}, {F4}, {F5}, {F6}, {F7}, {F8}, {F9}, {F10}, {F11}, {F12}
    '{APPS}, {NUMLOCK}
    'The {.._D} are the 'key down' codes. The {.._U} are the 'key up' codes.
    ' 
    'Examples of use: 
    'SendString "This is a test"
    'SendString "{BACKSPACE}{BACKSPACE}abcdef{ENTER}"
    'SendString "{CTRL_D}c{CTRL_U}" simulates a CTRL-C key combination. In most applications this copies the selected data.
    '
    %TimeIncr = 0
    SUB SendString(BYVAL sString AS STRING)
        LOCAL i                    AS LONG    
        LOCAL lBufCnt              AS LONG    
        LOCAL lFetchingSpecialChar AS LONG    
        LOCAL sChar                AS STRING
        LOCAL sSpecialChar         AS STRING  
        LOCAL iRet                 AS INTEGER 
        LOCAL dKeybLayout          AS DWORD   
        LOCAL bVkCode              AS BYTE    
        LOCAL bShiftState          AS BYTE    
        LOCAL Time                 AS DWORD
        LOCAL inpAPI() AS INPUTAPI
        REDIM inpAPI(0:20)
             
        IF sString = "" THEN EXIT SUB
        
        dKeybLayout = GETKEYBOARDLAYOUT(0)
        lBufCnt = -1
        Time = 0
        
                'Replace some characters with diacritics by multiple characters. 
        REPLACE "è" WITH "`e" IN sString
        REPLACE "é" WITH "'e" IN sString
        REPLACE "ê" WITH "^e" IN sString
        REPLACE "ë" WITH $DQ + "e" IN sString
    
        REPLACE "È" WITH "`E" IN sString
        REPLACE "É" WITH "'E" IN sString
        REPLACE "Ê" WITH "^E" IN sString
        REPLACE "Ë" WITH $DQ + "E" IN sString
        
        REPLACE "ì" WITH "`i" IN sString
        REPLACE "í" WITH "'i" IN sString
        REPLACE "î" WITH "^i" IN sString
        REPLACE "ï" WITH $DQ + "i" IN sString
    
        REPLACE "Ì" WITH "`I" IN sString
        REPLACE "Í" WITH "'I" IN sString
        REPLACE "Î" WITH "^I" IN sString
        REPLACE "Ï" WITH $DQ + "I" IN sString
         
        REPLACE "à" WITH "`a" IN sString
        REPLACE "á" WITH "'a" IN sString
        REPLACE "â" WITH "^a" IN sString
        REPLACE "ä" WITH $DQ + "a" IN sString
        REPLACE "ã" WITH "~a" IN sString
        
        'REPLACE "å" WITH "°a" IN sString
        REPLACE "À" WITH "`A" IN sString
        REPLACE "Á" WITH "'A" IN sString
        REPLACE "Â" WITH "^A" IN sString
        REPLACE "Ã" WITH "~A" IN sString
        REPLACE "Ä" WITH $DQ + "A" IN sString
        
        REPLACE "ò" WITH "`o" IN sString
        REPLACE "ó" WITH "'o" IN sString
        REPLACE "ô" WITH "^o" IN sString
        REPLACE "ö" WITH $DQ + "o" IN sString
        REPLACE "õ" WITH "~o" IN sString
    
        REPLACE "Ò" WITH "`O" IN sString
        REPLACE "Ó" WITH "'O" IN sString
        REPLACE "Ô" WITH "^O" IN sString
        REPLACE "Ö" WITH $DQ + "O" IN sString
        REPLACE "Õ" WITH "~O" IN sString
    
        REPLACE "Ù" WITH "`U" IN sString
        REPLACE "Ú" WITH "'U" IN sString
        REPLACE "Û" WITH "^U" IN sString
        REPLACE "Ü" WITH $DQ + "U" IN sString
        
        REPLACE "ù" WITH "`u" IN sString
        REPLACE "ú" WITH "'u" IN sString
        REPLACE "û" WITH "^u" IN sString
        REPLACE "ü" WITH $DQ + "u" IN sString
             
        REPLACE "ñ" WITH "~n" IN sString
        REPLACE "Ñ" WITH "~N" IN sString
        
        REPLACE "ç" WITH "'c" IN sString
        REPLACE "Ç" WITH "'C" IN sString
    
        REPLACE "ý" WITH "'y" IN sString
        REPLACE "ÿ" WITH $DQ + "y" IN sString
        
        REPLACE "Ý" WITH "'Y" IN sString
        REPLACE "Ÿ" WITH $DQ + "Y" IN sString    
        
    
        lFetchingSpecialChar = 0
        FOR i = 1 TO LEN(sString)                           'Parse the input string character by character
            sChar = MID$(sString, i, 1)
            SELECT CASE sChar
                CASE "{"                                    'Start of a special-character sequence
                    lFetchingSpecialChar = 1
                    sSpecialChar = ""
                
                CASE "}"                                    'End of a special-character sequence
                    lFetchingSpecialChar = 0
                    GOSUB AddSpecialCharToBuffer
                    
                CASE ELSE
                    IF lFetchingSpecialChar THEN
                        sSpecialChar = sSpecialChar + sChar 'Compose the special character string
                    ELSE
                        GOSUB AddNormalCharToBuffer
                    END IF
                
            END SELECT
        NEXT i
    
        REDIM PRESERVE inpAPI(0 TO lBufCnt)
        i = SENDINPUT(lBufCnt+1, inpAPI(0), SIZEOF(inpAPI(0)))
        
    EXIT SUB
    
    AddSpecialCharToBuffer:
                'Special characters (such as ENTER, BACKSPACE, CTRL-down, etc) that can not be represented
                'by an ASCII character, are defined here. 
        sSpecialChar = UCASE$(sSpecialChar)
        SELECT CASE sSpecialChar
            CASE "CTRL_D"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Down                     
            CASE "CTRL_U"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Up                       
            CASE "ALT_D"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Down                     
            CASE "ALT_U"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Up                       
            CASE "SHIFT_D"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Down                     
            CASE "SHIFT_U"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Up                       
            CASE "WIN_D"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Down                     
            CASE "WIN_U"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Up 
                    
            CASE "BACKSPACE" : bVkCode = %VK_BACK    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
            CASE "ENTER"     : bVkCode = %VK_RETURN  : 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 "RIGHT"     : bVkCode = %VK_RIGHT   : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
            CASE "LEFT"      : bVkCode = %VK_LEFT    : 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 "HOME"      : bVkCode = %VK_HOME    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
            CASE "END"       : bVkCode = %VK_END     : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
            
            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  
            
            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 "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  
            
            CASE ELSE
        END SELECT
        
    RETURN
    
    bVkCode_Down:       'key 'bVkCode' down
        IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
        INCR lBufCnt
        inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
        inpAPI(lBufCnt).m.ki.wVk         = bVkCode             
        inpAPI(lBufCnt).m.ki.time        = Time                
        inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(bVkCode, 0)
        
                'Intercept extended keys. They require the extended key flag.
                '
        SELECT CASE bVkCode
            CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                inpAPI(lBufCnt).m.ki.dwFlags = %KEYEVENTF_EXTENDEDKEY
            CASE ELSE
                inpAPI(lBufCnt).m.ki.dwFlags = 0
        END SELECT    
    RETURN
    
    
    bVkCode_Up:         'key 'bVkCode' up
        IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
        INCR lBufCnt
        inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
        inpAPI(lBufCnt).m.ki.wVk         = bVkCode             
        inpAPI(lBufCnt).m.ki.time        = Time
        inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(bVkCode, 0)
                
                'Intercept extended keys. They require the extended key flag.
        SELECT CASE bVkCode
            CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                inpAPI(lBufCnt).m.ki.dwFlags = %KEYEVENTF_KEYUP OR %KEYEVENTF_EXTENDEDKEY
            CASE ELSE
                inpAPI(lBufCnt).m.ki.dwFlags = %KEYEVENTF_KEYUP
        END SELECT     
    RETURN
    
    
    
    AddNormalCharToBuffer:
        IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
        
        iRet = VKKEYSCANEX(ASC(sChar), dKeybLayout)
        
        bVkCode     = LO(BYTE, iRet)
        bShiftState = HI(BYTE, iRet)
        IF (bVkCode = 255) AND (bShiftState = 255) THEN RETURN  'Cant do this char so skip
        
        IF BIT(bShiftState, 0) THEN     'Shift
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
            inpAPI(lBufCnt).m.ki.wVk         = %VK_SHIFT             
            inpAPI(lBufCnt).m.ki.dwFlags     = 0                
            inpAPI(lBufCnt).m.ki.time        = Time
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_SHIFT, 0)         
        END IF
        
        IF BIT(bShiftState, 1) THEN     'Ctrl
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
            inpAPI(lBufCnt).m.ki.wVk         = %VK_CONTROL             
            inpAPI(lBufCnt).m.ki.dwFlags     = 0
            inpAPI(lBufCnt).m.ki.time        = Time                
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_CONTROL, 0)        
        END IF
    
        IF BIT(bShiftState, 2) THEN     'Alt
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
            inpAPI(lBufCnt).m.ki.wVk         = %VK_MENU             
            inpAPI(lBufCnt).m.ki.dwFlags     = 0
            inpAPI(lBufCnt).m.ki.time        = Time                
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_MENU, 0)        
        END IF
    
        INCR lBufCnt
        inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
        inpAPI(lBufCnt).m.ki.wVk         = bVkCode             
        inpAPI(lBufCnt).m.ki.dwFlags     = 0
        inpAPI(lBufCnt).m.ki.time        = Time                
        inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(bVkCode, 0)                
    
        INCR lBufCnt
        inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
        inpAPI(lBufCnt).m.ki.wVk         = bVkCode             
        inpAPI(lBufCnt).m.ki.dwFlags     = %KEYEVENTF_KEYUP
        inpAPI(lBufCnt).m.ki.time        = Time  
        inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(bVkCode, 0)
    
    
        IF BIT(bShiftState, 0) THEN     'Shift
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
            inpAPI(lBufCnt).m.ki.wVk         = %VK_SHIFT             
            inpAPI(lBufCnt).m.ki.dwFlags     = %KEYEVENTF_KEYUP  
            inpAPI(lBufCnt).m.ki.time        = Time
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_SHIFT, 0)        
        END IF
        
        IF BIT(bShiftState, 1) THEN     'Ctrl
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
            inpAPI(lBufCnt).m.ki.wVk         = %VK_CONTROL             
            inpAPI(lBufCnt).m.ki.dwFlags     = %KEYEVENTF_KEYUP  
            inpAPI(lBufCnt).m.ki.time        = Time
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_CONTROL, 0)         
        END IF
    
        IF BIT(bShiftState, 2) THEN     'Alt
            INCR lBufCnt
            inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
            inpAPI(lBufCnt).m.ki.wVk         = %VK_MENU             
            inpAPI(lBufCnt).m.ki.dwFlags     = %KEYEVENTF_KEYUP  
            inpAPI(lBufCnt).m.ki.time        = Time
            inpAPI(lBufCnt).m.ki.wScan       = MAPVIRTUALKEY(%VK_MENU, 0)        
        END IF            
    RETURN
        
    END SUB
    '-------------------------------------------------------------------------
    
    
    '-------------------------------------------------------------------------
    'Sub borrowed from SendKeys.inc - by William Burns
    'Wait until user lets go of the ctrl/Alt/Shift and any other keys
    SUB WaitForNoKeys    
        LOCAL KeyWasPressed  AS LONG
        LOCAL iKey           AS LONG
        DO                'loop here until user lets go of the ctrl/Alt/Shift and any other keys
            SLEEP 5                       '<-- Modified in V1.1
            KeyWasPressed = 0
            'FOR iKey = 19 TO 255         '0-9 and a-z
            FOR iKey = 0 TO 255           '<-- Modified in V1.1
                IF (GETASYNCKEYSTATE(iKey) AND &H8000) THEN KeyWasPressed = 1
            NEXT iKey
            IF (GETASYNCKEYSTATE(%VK_CONTROL) AND &H8000) OR (GETASYNCKEYSTATE(%VK_SHIFT) AND &H8000) OR (GETASYNCKEYSTATE(%VK_MENU) AND &H8000) THEN KeyWasPressed = 1  'note: %VK_MENU = %VK_ALT
        LOOP WHILE KeyWasPressed
    END SUB
    '-------------------------------------------------------------------------
    Last edited by Eddy Van Esch; 27 May 2009, 03:59 PM. Reason: Removed line with debug code
    Eddy

  • #2
    The demo program.

    Code:
    '===============================================================================
    ' Demo code to demonstrate the use of 'SendInput.inc'
    ' 2009 - Eddy Van Esch
    ' V1.0: 14-May-2009 : Initial version
    '
    '===============================================================================
    
    #COMPILE EXE
    #REGISTER NONE
    #DIM ALL
    #INCLUDE "SendInput.Inc"
    
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "win32api.inc"  'add win32api if not already added
    #ENDIF
    
    
    GLOBAL hDlg AS LONG
    
    '-------------------------------------------------------------------------
    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
                iRet = TIMER
                REDIM nAtom(1:2)
                nAtom(1) = GLOBALADDATOM ("SendInput_demo" + STR$(iRet + 1))
                nAtom(2) = GLOBALADDATOM ("SendInput_demo" + STR$(iRet + 2))
                REGISTERHOTKEY CBHNDL, nAtom(1), 2, ASC("J") ' Ctrl-J
                REGISTERHOTKEY CBHNDL, nAtom(2), 2, ASC("K") ' Ctrl-K
                
            CASE %WM_DESTROY
                FOR iCount = 1 TO 2
                    UNREGISTERHOTKEY CBHNDL, nAtom(iCount)
                    GLOBALDELETEATOM nAtom(iCount)
                NEXT iCount
            
            CASE %WM_HOTKEY
                SELECT CASE CBWPARAM
                    CASE nAtom(1) 'Ctrl-J
                        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}"
                    
                    CASE nAtom(2) 'Ctrl-K
                        sText = INPUTBOX$("Enter string to send" + $CRLF + $CRLF + "You have 1 second " _
                           + "to switch back to the program to send the string.", "Enter string:")
                        SLEEP 1000
                        SendString sText
                        
                END SELECT
        END SELECT
    END FUNCTION
    
    '-------------------------------------------------------------------------
    
    
    '-------------------------------------------------------------------------
    FUNCTION PBMAIN
       DIALOG NEW 0, "SendInput demo",,, 300, 30,%WS_MINIMIZEBOX OR %WS_SYSMENU OR %WS_CAPTION, 0 TO hDlg
       CONTROL ADD LABEL, hDlg, 205, "Start a program like Notepad and then press Ctrl-J or Ctrl-K to send keystrokes", 5, 10, 290, 15, %SS_CENTER
       DIALOG SHOW MODAL hDlg CALL DlgProc
    END FUNCTION
    '-------------------------------------------------------------------------
    Eddy

    Comment


    • #3
      Comments, questions, feedback, here please.

      Kind regards
      Eddy

      Comment


      • #4
        Include file 'SendInput.inc' was updated.

        Kind regards
        Last edited by Eddy Van Esch; 27 May 2009, 04:02 PM.
        Eddy

        Comment


        • #5
          send keystrokes utility

          I've written a utility based on Eddy's "SendInput.inc"

          After you save the file posted at the top of this thread as "SendInput.inc", save my source code shown below.

          This program takes a commandline string specifying a sequence of keyboard keys and then types them out to the active window. There is also an option for slowing the typing rate in case the keys are typed too quickly for the target application.

          Special keys are supported, such as ALT and CONTROL, which means that commands can be sent to the target application, such as selecting menu items.

          This allows your program to control a windows GUI application in the same way that the user can from the keyboard (of course, without the functions that require a mouse), similar to macro/windows automation packages.

          The commandline syntax and examples are given in the comments in the code.


          ----------------------------

          Code:
          ' May 27, 2009 rev 10 pm Eastern
          ' this program takes the text and key commands specified in the commandline and types them out to the active window
          
          ' commandline options
          ' at minimum, the commandline must contain the key sequence you wish to type out
          ' special keys are permitted, using the syntax defined in Eddy's SendInput.inc file eg, {TAB} {CTRL_D}
          ' optionally, the entire sequence can be surrounded by quotation marks if you wish to have leading or trailing space characters
          
          ' examples
          ' program.exe This is a test
          ' program.exe "   This is a test       "
          
          ' keystroke delay
          ' in order to slow the rate at which your keystrokes are oupulsed to the active window, use the string
          ' KEYDELAY=#: at the BEGINNING of the key sequence (after the opening quotation mark if you are using quotation marks)
          ' replace # with one or more digits indicating the interkeystroke delay in ms
          ' the : must be after the last digit
          ' if you don't have a KEYDELAY specified in the commandline, the program treats it like KEYDELAY=0: except that
          ' if KEYDELAY omitted from command line, then all the keys are sent together as a single string to the SendInput API ' if KEYDELAY=0, then keys are sent one at a time to the SendInput API with zero delay between sending keys
          ' so omitting KEYDELAY gives more efficient processing than KEYDELAY=0, but perhaps the imperceptible delay 
          ' that accompanies this inefficiency may be desirable if your keystrokes were otherwise just a tiny bit too fast for the target application
          
          ' examples
          
          ' with zero keystroke delay, do an ALT-f to open the file menu, n to select the new document item, and type text into the new document window
          ' program.exe KEYDELAY=0:{ALT_D}f{ALT_U}nThis is a test       
          
          ' same as above, but keystroke delay specified to give the target application more time to process the ALT-f and new document commands before starting to type text.  Also, quotation marks are used to allow trailing spaces
          ' program.exe "KEYDELAY=10:{ALT_D}f{ALT_U}nThis is a test       "
          
          ' Alt-f (open file menu), c (c to select copy item from menu), shift-tab, control-c (copy), tab, control-v (paste), 5 left arrows, shift-home (to select/highlight text), shift-right arrow (to modify selection)
          'program.exe KEYDELAY=100:{ALT_D}f{ALT_U}c{SHIFT_D}{TAB}{SHIFT_U}{CTRL_D}c{CTRL_U}{TAB}{CTRL_D}v{CTRL_U}{LEFT}{LEFT}{LEFT}{LEFT}{LEFT}{SHIFT_D}{HOME}{RIGHT}{SHIFT_U}
          
          
          #COMPILE EXE
          #DIM ALL
          #INCLUDE "win32api.inc"
          #INCLUDE "SendInput.inc"
          
          FUNCTION PBMAIN () AS LONG
          
          DIM keystrokedelay AS INTEGER ' sleep time between keystrokes (units=ms)
          DIM keystrokedelaystartphrase AS STRING ' string at start of commandline when a keystrokedelay is specified
          DIM keystrokedelayendphrase AS STRING ' SINGLE CHARACTER to divide the keystrokedelay from the keystroke sequence
          DIM keystrokedelaystr  AS STRING ' the keystrokedelay when it is a string extracted from the commandline
          
          DIM keystrokes AS STRING 'the keystroke or special key as sent to .inc routine
          DIM singlekey AS STRING ' character by character extract from keysequence. may be a single key or subset of special key
          DIM keysequence AS STRING 'sequence of keys sent by user or other app
          DIM keysequencepos AS INTEGER
          DIM specialkeyflag AS STRING
          
          DIM originalinput AS STRING
          DIM modifiedinput AS STRING
          
          originalinput=LTRIM$(COMMAND$)
          modifiedinput=originalinput
          
          ' if you need leading or trailing spaces, then surrounded command line with quotes, which will be stripped off here
          IF LEFT$(modifiedinput,1)=$DQ AND RIGHT$(modifiedinput,1)=$DQ THEN
              modifiedinput=MID$(modifiedinput,2,LEN(modifiedinput)-2)
          END IF
          
          keystrokedelaystartphrase="KEYDELAY="
          keystrokedelayendphrase=":"
          IF LEFT$(modifiedinput,LEN(keystrokedelaystartphrase))=keystrokedelaystartphrase THEN ' a keystrokedelay is specified
              modifiedinput=RIGHT$(modifiedinput,LEN(modifiedinput)-LEN(keystrokedelaystartphrase))
              keystrokedelaystr=""
              FOR keysequencepos=1 TO LEN(modifiedinput) ' extract the keystrokedelay
                  singlekey=MID$(modifiedinput,keysequencepos,1)
          
                  IF singlekey=keystrokedelayendphrase THEN ' end of keystrokedelay found
                      keystrokedelay=VAL(keystrokedelaystr)
                      keysequence=RIGHT$(modifiedinput,LEN(modifiedinput)-keysequencepos)
          
                      EXIT FOR
                  ELSE
                      keystrokedelaystr=keystrokedelaystr+singlekey
                  END IF
              NEXT  keysequencepos
          
              ' for testing purposes
              ' MSGBOX originalinput+$CRLF+"delay="+STR$(keystrokedelay)+$CRLF+keysequence
          
          ELSE ' no keystroke delay is specified
              keysequence=modifiedinput
              keystrokedelay=-1
              ' for testing purposes
              ' MSGBOX originalinput+$CRLF+"delay not specified"+$CRLF+keysequence
          END IF
          
          keystrokes=""
          singlekey=""
          specialkeyflag="N"
          
          IF keystrokedelay>=0 THEN
          
              FOR keysequencepos=1 TO LEN(keysequence)
                  singlekey=MID$(keysequence,keysequencepos,1)
                  keystrokes=keystrokes+singlekey
          
                  IF keystrokes="{" THEN  'start of special key
                      specialkeyflag="Y"
                  ELSEIF singlekey="}" AND specialkeyflag="Y" THEN 'end of special key
                      specialkeyflag="N"
                  END IF
          
                  IF specialkeyflag="N" THEN
                      SendString keystrokes
                      keystrokes=""
                      SLEEP keystrokedelay
                  END IF
          
              NEXT  keysequencepos
          
          ELSE
              keystrokes=keysequence
              SendString keystrokes
          END IF
          
          END FUNCTION
          Last edited by S Stamp; 27 May 2009, 09:08 PM. Reason: added differential behavior for delay=0 vs delay unspecified

          Comment


          • #6
            I expanded my commandline utility so that the program makes the window you specify active before typing out the keystrokes. My original version posted earlier in this thread sends the keystrokes to whichever window is currently active and does not allow you to specify which window should be active.

            I put my expanded version in a new thread. See
            http://www.powerbasic.com/support/pb...ad.php?t=40652

            Comment


            • #7
              The code needed some modifications for PBWin 10 and its Win32API.

              Code:
              '===============================================================================
              ' SendInput.inc
              ' 2009 - 2012 - Eddy Van Esch
              ' 
              ' History:
              ' --------
              ' V1.0: 14-May-2009 : Initial version
              ' 
              ' V1.1: 27-May-2009 : 
              '     - Fixed a bug where SHIFT_D with HOME or an arrow key combination produced
              '       incorrect results. The shift key appeared to be ignored.
              '       Thanks to S. Stamp for reporting the problem.
              '
              '       Apparently, the HOME and arrow keys (and a number of others) are extended keys and
              '       they require the extended key flag to be set.
              '       A forum thread about the problem:
              '       http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21737163.html
              '     - Added more special keys (see list below)
              '     - SUB WaitForNoKeys slightly modified
              '
              ' V1.2: 17-June-2009 : 
              '     - Added special keys: {CTRL}, {ALT}, {SHIFT}, {WIN}
              '       In contrast with the {.._D}, {.._U} keys that can be used with multiple keys in between, 
              '       the above keys are used with one character at the time only, like this:
              '       "{CTRL_D}ac{CTRL_U}" is equivalent to "{CTRL}a{CTRL}c"
              '     - Added sub "UniCode_OnOff" to turn UniCode on or off. 
              '       UniCode_OnOff(1) will make SendString send unicode keystrokes.
              '       UniCode_OnOff(0) will make SendString send ansi keystrokes.
              '
              ' V1.3: 09-June-2012 : 
              '     Some modifications needed to be able to compile with PBWin 10. 
              '     - INPUTAPI type def is changed in Win32API.
              '     - Changed: i = SendInput(lBufCnt+1, inpAPI(0), SIZEOF(inpAPI(0)))
              '       into:    i = SendInput(lBufCnt+1, VARPTR(inpAPI(0)), SIZEOF(inpAPI(0))) 
              '     - VkKeyScanEx replaced by VkKeyScanExW and VkKeyScanExA  
              '         
              ' ---------------------------------------------------------------------------
              ' This software allows to send keystrokes to the application that
              ' currently has focus.
              ' Rather than using KEYBD_EVENT, this routine uses SENDINPUT.
              ' The advantage is that when using SendInput, the sent keystrokes can not
              ' be accidently mixed with keystrokes that the user is entering on the keyboard.
              ' Also, there is no problem with timings between multiple Keybd_Event commands.
              ' This routine also handles characters with diacritics, like é,è,ê,ñ, etc. (See **)
              ' Writing this code, I took inspiration (and a few lines of code :-)) from
              ' William Burns' file 'SendKeys.inc'
              ' Not all possible special keys are implemented yet, but it is easy to add more.
              '
              'Sub WaitForNoKeys was reused from William Burns' file 'SendKeys.inc'. Thanks William!
              '
              '----------------------------
              '**  IMPORTANT !!!
              'Although SendString should handle strings with diacritics properly, other active programs
              'can mess this up. Especially programs that use a global keyboard hook, such
              'as AutoHotKey for example.
              'The reason is that these programs use the ToAscii(Ex) API function in their keyboard hook.
              'Using ToAscii(Ex) in a hook messes up diacritics handling.
              'What you then will notice is an inconsistent behaviour when sending characters with diacritics.
              'For example, sending "éééé" could result in "eeéé", "''eé" or some other inconsistent result.
              'Very annoying. The only solution is to terminate the application that is using the global hook. 
              '----------------------------
              '
              '===============================================================================
              
              #IF NOT %DEF(%WINAPI)
                  #INCLUDE "win32api.inc"                        'add win32api if not already added
              #ENDIF
              
              GLOBAL glSendString_UniCode AS LONG
              
              
              '-------------------------------------------------------------------------
              'Turn unicode on or off. This can be changed on the fly. SendString will send keystrokes in unicode or
              'ansi, depending on the flag set by this sub.
              'INPUT: 0 : Turn unicode off
              '       1 : Turn unicode on
              SUB UniCode_OnOff(lOn AS LONG)
                  IF lOn THEN
                      glSendString_UniCode = %KEYEVENTF_UNICODE
                  ELSE
                      glSendString_UniCode = 0
                  END IF
              END SUB
              '-------------------------------------------------------------------------
              
              
              
              '-------------------------------------------------------------------------
              'This sub places the characters in 'sString' in the keyboard buffer. Then, the characters are sent to
              'whatever application currently has focus.
              'So, this sub allows to simulate keyboard input.
              'Apart from normal ascii characters, SendString can also handle some special characters:
              '
              'The special characters currently implemented are:
              '{CTRL_D}, {CTRL_U}, {ALT_D}, {ALT_U}, {SHIFT_D}, {SHIFT_U}, {WIN_D}, {WIN_U}
              '{CTRL}, {ALT}, {SHIFT}, {WIN}
              '{BACKSPACE}, {ENTER}, {INSERT}, {DELETE}, {RIGHT}, {LEFT}, {UP}, {DOWN}, {HOME}, {END}, {TAB}, {ESCAPE} 
              '{PGUP}, {PGDN}, {F1}, {F2}, {F3}, {F4}, {F5}, {F6}, {F7}, {F8}, {F9}, {F10}, {F11}, {F12}
              '{APPS}, {NUMLOCK}
              'The {.._D} are the 'key down' codes. The {.._U} are the 'key up' codes.
              ' 
              'Examples of use: 
              'SendString "This is a TEST"
              'SendString "12{BACKSPACE}{BACKSPACE}abcdef{ENTER}"
              'SendString "{CTRL_D}ac{CTRL_U}" simulates a CTRL-a  CTRL-c key combination. In most applications this copies the selected data.
              'SendString "{CTRL}a{CTRL}c" simulates a CTRL-a  CTRL-c key combination. In most applications this copies the selected data.
              %TimeIncr = 1
              SUB SendString(BYVAL sString AS STRING)
                  LOCAL  i                     AS LONG     
                  LOCAL  lBufCnt               AS DWORD     
                  LOCAL  lFetchingSpecialChar  AS LONG     
                  LOCAL  lSpecialKeyDown_Ctrl  AS LONG     
                  LOCAL  lSpecialKeyDown_Shift AS LONG     
                  LOCAL  lSpecialKeyDown_Alt   AS LONG     
                  LOCAL  lSpecialKeyDown_Win   AS LONG     
                  LOCAL  sChar                 AS STRING   
                  LOCAL  sSpecialChar          AS STRING   
                  LOCAL  iRet                  AS INTEGER  
                  LOCAL  dKeybLayout           AS DWORD    
                  LOCAL  bVkCode               AS BYTE     
                  LOCAL  bShiftState           AS BYTE     
                  STATIC Time                  AS DWORD    
                 
              
                  LOCAL inpAPI() AS INPUTAPI
                  REDIM inpAPI(0 TO 20)
                       
                  IF sString = "" THEN EXIT SUB
                  
                  dKeybLayout = GETKEYBOARDLAYOUT(0)
                  lBufCnt = -1
                  Time = 1
                  
                          'Replace some characters with diacritics by multiple characters. 
                  REPLACE "è" WITH "`e" IN sString
                  REPLACE "é" WITH "'e" IN sString
                  REPLACE "ê" WITH "^e" IN sString
                  REPLACE "ë" WITH $DQ + "e" IN sString
              
                  REPLACE "È" WITH "`E" IN sString
                  REPLACE "É" WITH "'E" IN sString
                  REPLACE "Ê" WITH "^E" IN sString
                  REPLACE "Ë" WITH $DQ + "E" IN sString
                  
                  REPLACE "ì" WITH "`i" IN sString
                  REPLACE "í" WITH "'i" IN sString
                  REPLACE "î" WITH "^i" IN sString
                  REPLACE "ï" WITH $DQ + "i" IN sString
              
                  REPLACE "Ì" WITH "`I" IN sString
                  REPLACE "Í" WITH "'I" IN sString
                  REPLACE "Î" WITH "^I" IN sString
                  REPLACE "Ï" WITH $DQ + "I" IN sString
                   
                  REPLACE "à" WITH "`a" IN sString
                  REPLACE "á" WITH "'a" IN sString
                  REPLACE "â" WITH "^a" IN sString
                  REPLACE "ä" WITH $DQ + "a" IN sString
                  REPLACE "ã" WITH "~a" IN sString
                  
                  'REPLACE "å" WITH "°a" IN sString
                  REPLACE "À" WITH "`A" IN sString
                  REPLACE "Á" WITH "'A" IN sString
                  REPLACE "Â" WITH "^A" IN sString
                  REPLACE "Ã" WITH "~A" IN sString
                  REPLACE "Ä" WITH $DQ + "A" IN sString
                  
                  REPLACE "ò" WITH "`o" IN sString
                  REPLACE "ó" WITH "'o" IN sString
                  REPLACE "ô" WITH "^o" IN sString
                  REPLACE "ö" WITH $DQ + "o" IN sString
                  REPLACE "õ" WITH "~o" IN sString
              
                  REPLACE "Ò" WITH "`O" IN sString
                  REPLACE "Ó" WITH "'O" IN sString
                  REPLACE "Ô" WITH "^O" IN sString
                  REPLACE "Ö" WITH $DQ + "O" IN sString
                  REPLACE "Õ" WITH "~O" IN sString
              
                  REPLACE "Ù" WITH "`U" IN sString
                  REPLACE "Ú" WITH "'U" IN sString
                  REPLACE "Û" WITH "^U" IN sString
                  REPLACE "Ü" WITH $DQ + "U" IN sString
                  
                  REPLACE "ù" WITH "`u" IN sString
                  REPLACE "ú" WITH "'u" IN sString
                  REPLACE "û" WITH "^u" IN sString
                  REPLACE "ü" WITH $DQ + "u" IN sString
                       
                  REPLACE "ñ" WITH "~n" IN sString
                  REPLACE "Ñ" WITH "~N" IN sString
                  
                  REPLACE "ç" WITH "'c" IN sString
                  REPLACE "Ç" WITH "'C" IN sString
              
                  REPLACE "ý" WITH "'y" IN sString
                  REPLACE "ÿ" WITH $DQ + "y" IN sString
                  
                  REPLACE "Ý" WITH "'Y" IN sString
                  REPLACE "Ÿ" WITH $DQ + "Y" IN sString    
                  
              
                  lFetchingSpecialChar = 0
                  FOR i = 1 TO LEN(sString)                           'Parse the input string character by character
                      sChar = MID$(sString, i, 1)
                      SELECT CASE sChar
                          CASE "{"                                    'Start of a special-character sequence
                              lFetchingSpecialChar = 1
                              sSpecialChar = ""
                          
                          CASE "}"                                    'End of a special-character sequence
                              lFetchingSpecialChar = 0
                              GOSUB AddSpecialCharToBuffer
                              
                          CASE ELSE
                              IF lFetchingSpecialChar THEN
                                  sSpecialChar = sSpecialChar + sChar 'Compose the special character string
                              ELSE
                                  GOSUB AddNormalCharToBuffer
                              END IF
                          
                      END SELECT
                  NEXT i
              
                  REDIM PRESERVE inpAPI(0 TO lBufCnt)
                  'i = SendInput(lBufCnt+1, inpAPI(0), SIZEOF(inpAPI(0)))
                  i = SendInput(lBufCnt+1, VARPTR(inpAPI(0)), SIZEOF(inpAPI(0)))
                      
              EXIT SUB
              
              AddSpecialCharToBuffer:
                          'Special characters (such as ENTER, BACKSPACE, CTRL-down, etc) that can not be represented
                          'by an ASCII character, are defined here. 
                  sSpecialChar = UCASE$(sSpecialChar)
                  SELECT CASE sSpecialChar
                      CASE "CTRL"      : lSpecialKeyDown_Ctrl  = 1
                      CASE "ALT"       : lSpecialKeyDown_Alt   = 1
                      CASE "SHIFT"     : lSpecialKeyDown_Shift = 1                                     
                      CASE "WIN"       : lSpecialKeyDown_Win   = 1                 
              
                      CASE "CTRL_D"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Down                     
                      CASE "CTRL_U"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Up                       
                      CASE "ALT_D"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Down                     
                      CASE "ALT_U"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Up                       
                      CASE "SHIFT_D"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Down                     
                      CASE "SHIFT_U"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Up                       
                      CASE "WIN_D"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Down                     
                      CASE "WIN_U"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Up 
                              
                      CASE "BACKSPACE" : bVkCode = %VK_BACK    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "ENTER"     : bVkCode = %VK_RETURN  : 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 "RIGHT"     : bVkCode = %VK_RIGHT   : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "LEFT"      : bVkCode = %VK_LEFT    : 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 "HOME"      : bVkCode = %VK_HOME    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "END"       : bVkCode = %VK_END     : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      
                      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  
                      
                      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 "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  
                      
                      CASE ELSE
                  END SELECT
                  
              RETURN
              
              bVkCode_Down:       'key 'bVkCode' down
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                  inpAPI(lBufCnt).iki.wVk          = bVkCode            
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
                  
                          'Intercept extended keys. They require the extended key flag.
                          '
                  SELECT CASE bVkCode
                      CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_EXTENDEDKEY OR glSendString_UniCode
                      CASE ELSE
                          inpAPI(lBufCnt).iki.dwFlags = 0 or glSendString_UniCode
                  END SELECT    
              RETURN
              
              
              bVkCode_Up:         'key 'bVkCode' up
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
                          
                          'Intercept extended keys. They require the extended key flag.
                  SELECT CASE bVkCode
                      CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_KEYUP OR %KEYEVENTF_EXTENDEDKEY or glSendString_UniCode
                      CASE ELSE
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_KEYUP OR glSendString_UniCode
                  END SELECT     
              RETURN
              
              
              
              AddNormalCharToBuffer:
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                    
                  IF glSendString_UniCode = %KEYEVENTF_UNICODE THEN 
                      iRet = VkKeyScanExW(ASC(sChar), dKeybLayout)    
                  ELSE
                      iRet = VkKeyScanExA(ASC(sChar), dKeybLayout)
                  END IF
                      
                  bVkCode     = LO(BYTE, iRet)
                  bShiftState = HI(BYTE, iRet)
                  IF (bVkCode = 255) AND (bShiftState = 255) THEN RETURN  'Cant do this char so skip
                  
                  IF (BIT(bShiftState, 0)) OR lSpecialKeyDown_Shift THEN     'Shift
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_SHIFT             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR glSendString_UniCode                
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_SHIFT, 0)         
                  END IF
                  
                  IF (BIT(bShiftState, 1)) OR lSpecialKeyDown_Ctrl THEN     'Ctrl
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_CONTROL             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR glSendString_UniCode
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_CONTROL, 0)        
                  END IF
              
                  IF (BIT(bShiftState, 2)) OR lSpecialKeyDown_Alt THEN     'Alt
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_MENU             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR glSendString_UniCode
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_MENU, 0)        
                  END IF
              
                  IF lSpecialKeyDown_Win THEN     'Win
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_LWIN             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR glSendString_UniCode
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_LWIN, 0)        
                  END IF
                  
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dwFlags      = 0 OR glSendString_UniCode
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)                
              
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR glSendString_UniCode
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr   
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
              
              
                  IF (BIT(bShiftState, 0)) OR lSpecialKeyDown_Shift THEN     'Shift
                      lSpecialKeyDown_Shift = 0
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_SHIFT             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR glSendString_UniCode  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_SHIFT, 0)        
                  END IF
                  
                  IF (BIT(bShiftState, 1)) OR lSpecialKeyDown_Ctrl THEN     'Ctrl
                      lSpecialKeyDown_Ctrl  = 0
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_CONTROL             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR glSendString_UniCode  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_CONTROL, 0)         
                  END IF
              
                  IF (BIT(bShiftState, 2)) OR lSpecialKeyDown_Alt THEN     'Alt
                      lSpecialKeyDown_Alt   = 0
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_MENU             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR glSendString_UniCode  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_MENU, 0)        
                  END IF
                  
                  IF lSpecialKeyDown_Win THEN     'Win
                      lSpecialKeyDown_Win   = 0
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_LWIN             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR glSendString_UniCode  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_LWIN, 0)        
                  END IF 
              RETURN
                  
              END SUB
              '-------------------------------------------------------------------------
              
               
              '-------------------------------------------------------------------------
              'Sub borrowed from SendKeys.inc - by William Burns
              'Wait until user lets go of the ctrl/Alt/Shift and any other keys
              SUB WaitForNoKeys    
                  LOCAL KeyWasPressed  AS LONG
                  LOCAL iKey           AS LONG
                  DO                'loop here until user lets go of the ctrl/Alt/Shift and any other keys
                      SLEEP 1                       '<-- Modified in V1.1
                      KeyWasPressed = 0
                      'FOR iKey = 19 TO 255         '0-9 and a-z
                      FOR iKey = 0 TO 255           '<-- Modified in V1.1
                          IF (GETASYNCKEYSTATE(iKey) AND &H8000) THEN 
                              KeyWasPressed = 1
                              EXIT FOR
                          END IF    
                      NEXT iKey
                      IF (GETASYNCKEYSTATE(%VK_CONTROL) AND &H8000) OR (GETASYNCKEYSTATE(%VK_SHIFT) AND &H8000) OR (GETASYNCKEYSTATE(%VK_MENU) AND &H8000) THEN KeyWasPressed = 1  'note: %VK_MENU = %VK_ALT
                  LOOP WHILE KeyWasPressed
              END SUB
              '-------------------------------------------------------------------------
              
              
              '-------------------------------------------------------------------------
              SUB SendString_Uni(BYVAL sString AS STRING)
                  LOCAL i                    AS LONG    
                  LOCAL lBufCnt              AS LONG    
                  LOCAL lFetchingSpecialChar AS LONG    
                  LOCAL sChar                AS STRING
                  LOCAL sSpecialChar         AS STRING  
                  LOCAL iRet                 AS INTEGER 
                  LOCAL dKeybLayout          AS DWORD   
                  LOCAL bVkCode              AS BYTE    
                  LOCAL bShiftState          AS BYTE    
                  LOCAL Time                 AS DWORD
                  LOCAL inpAPI() AS INPUTAPI
                  REDIM inpAPI(0 TO 20)
                       
                  IF sString = "" THEN EXIT SUB
                  
                  dKeybLayout = GETKEYBOARDLAYOUT(0)
                  lBufCnt = -1
                  Time = 10
                  
                          'Replace some characters with diacritics by multiple characters. 
                  REPLACE "è" WITH "`e" IN sString
                  REPLACE "é" WITH "'e" IN sString
                  REPLACE "ê" WITH "^e" IN sString
                  REPLACE "ë" WITH $DQ + "e" IN sString
              
                  REPLACE "È" WITH "`E" IN sString
                  REPLACE "É" WITH "'E" IN sString
                  REPLACE "Ê" WITH "^E" IN sString
                  REPLACE "Ë" WITH $DQ + "E" IN sString
                  
                  REPLACE "ì" WITH "`i" IN sString
                  REPLACE "í" WITH "'i" IN sString
                  REPLACE "î" WITH "^i" IN sString
                  REPLACE "ï" WITH $DQ + "i" IN sString
              
                  REPLACE "Ì" WITH "`I" IN sString
                  REPLACE "Í" WITH "'I" IN sString
                  REPLACE "Î" WITH "^I" IN sString
                  REPLACE "Ï" WITH $DQ + "I" IN sString
                   
                  REPLACE "à" WITH "`a" IN sString
                  REPLACE "á" WITH "'a" IN sString
                  REPLACE "â" WITH "^a" IN sString
                  REPLACE "ä" WITH $DQ + "a" IN sString
                  REPLACE "ã" WITH "~a" IN sString
                  
                  'REPLACE "å" WITH "°a" IN sString
                  REPLACE "À" WITH "`A" IN sString
                  REPLACE "Á" WITH "'A" IN sString
                  REPLACE "Â" WITH "^A" IN sString
                  REPLACE "Ã" WITH "~A" IN sString
                  REPLACE "Ä" WITH $DQ + "A" IN sString
                  
                  REPLACE "ò" WITH "`o" IN sString
                  REPLACE "ó" WITH "'o" IN sString
                  REPLACE "ô" WITH "^o" IN sString
                  REPLACE "ö" WITH $DQ + "o" IN sString
                  REPLACE "õ" WITH "~o" IN sString
              
                  REPLACE "Ò" WITH "`O" IN sString
                  REPLACE "Ó" WITH "'O" IN sString
                  REPLACE "Ô" WITH "^O" IN sString
                  REPLACE "Ö" WITH $DQ + "O" IN sString
                  REPLACE "Õ" WITH "~O" IN sString
              
                  REPLACE "Ù" WITH "`U" IN sString
                  REPLACE "Ú" WITH "'U" IN sString
                  REPLACE "Û" WITH "^U" IN sString
                  REPLACE "Ü" WITH $DQ + "U" IN sString
                  
                  REPLACE "ù" WITH "`u" IN sString
                  REPLACE "ú" WITH "'u" IN sString
                  REPLACE "û" WITH "^u" IN sString
                  REPLACE "ü" WITH $DQ + "u" IN sString
                       
                  REPLACE "ñ" WITH "~n" IN sString
                  REPLACE "Ñ" WITH "~N" IN sString
                  
                  REPLACE "ç" WITH "'c" IN sString
                  REPLACE "Ç" WITH "'C" IN sString
              
                  REPLACE "ý" WITH "'y" IN sString
                  REPLACE "ÿ" WITH $DQ + "y" IN sString
                  
                  REPLACE "Ý" WITH "'Y" IN sString
                  REPLACE "Ÿ" WITH $DQ + "Y" IN sString    
                  
              
                  lFetchingSpecialChar = 0
                  FOR i = 1 TO LEN(sString)                           'Parse the input string character by character
                      sChar = MID$(sString, i, 1)
                      SELECT CASE sChar
                          CASE "{"                                    'Start of a special-character sequence
                              lFetchingSpecialChar = 1
                              sSpecialChar = ""
                          
                          CASE "}"                                    'End of a special-character sequence
                              lFetchingSpecialChar = 0
                              GOSUB AddSpecialCharToBuffer
                              
                          CASE ELSE
                              IF lFetchingSpecialChar THEN
                                  sSpecialChar = sSpecialChar + sChar 'Compose the special character string
                              ELSE
                                  GOSUB AddNormalCharToBuffer
                              END IF
                          
                      END SELECT
                  NEXT i
              
                  REDIM PRESERVE inpAPI(0 TO lBufCnt)
                  'i = SENDINPUT(lBufCnt+1, inpAPI(0), SIZEOF(inpAPI(0)))
                  i = SendInput(lBufCnt+1, VARPTR(inpAPI(0)), SIZEOF(inpAPI(0)))
                  
              EXIT SUB
              
              AddSpecialCharToBuffer:
                          'Special characters (such as ENTER, BACKSPACE, CTRL-down, etc) that can not be represented
                          'by an ASCII character, are defined here. 
                  sSpecialChar = UCASE$(sSpecialChar)
                  SELECT CASE sSpecialChar
                      CASE "CTRL_D"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Down                     
                      CASE "CTRL_U"    : bVkCode = %VK_CONTROL : GOSUB bVkCode_Up                       
                      CASE "ALT_D"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Down                     
                      CASE "ALT_U"     : bVkCode = %VK_MENU    : GOSUB bVkCode_Up                       
                      CASE "SHIFT_D"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Down                     
                      CASE "SHIFT_U"   : bVkCode = %VK_SHIFT   : GOSUB bVkCode_Up                       
                      CASE "WIN_D"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Down                     
                      CASE "WIN_U"     : bVkCode = %VK_LWIN    : GOSUB bVkCode_Up 
                              
                      CASE "BACKSPACE" : bVkCode = %VK_BACK    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "ENTER"     : bVkCode = %VK_RETURN  : 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 "RIGHT"     : bVkCode = %VK_RIGHT   : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "LEFT"      : bVkCode = %VK_LEFT    : 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 "HOME"      : bVkCode = %VK_HOME    : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      CASE "END"       : bVkCode = %VK_END     : GOSUB bVkCode_Down : GOSUB bVkCode_Up  
                      
                      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  
                      
                      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 "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  
                      
                      CASE ELSE
                  END SELECT
                  
              RETURN
              
              bVkCode_Down:       'key 'bVkCode' down
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
                  
                          'Intercept extended keys. They require the extended key flag.
                          '
                  SELECT CASE bVkCode
                      CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_EXTENDEDKEY OR %KEYEVENTF_UNICODE
                      CASE ELSE
                          inpAPI(lBufCnt).iki.dwFlags = 0  OR %KEYEVENTF_UNICODE
                  END SELECT    
              RETURN
              
              
              bVkCode_Up:         'key 'bVkCode' up
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
                          
                          'Intercept extended keys. They require the extended key flag.
                  SELECT CASE bVkCode
                      CASE %VK_UP, %VK_DOWN, %VK_LEFT, %VK_RIGHT, %VK_HOME, %VK_END, %VK_INSERT, %VK_DELETE, %VK_PGUP, %VK_PGDN  
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_KEYUP OR %KEYEVENTF_EXTENDEDKEY OR %KEYEVENTF_UNICODE
                      CASE ELSE
                          inpAPI(lBufCnt).iki.dwFlags = %KEYEVENTF_KEYUP OR %KEYEVENTF_UNICODE
                  END SELECT     
              RETURN
              
              
              
              AddNormalCharToBuffer:
                  IF (UBOUND(inpAPI()) - lBufCnt) < 5 THEN REDIM PRESERVE inpAPI(lBufCnt + 16)
                  
                  IF glSendString_UniCode = %KEYEVENTF_UNICODE THEN 
                      iRet = VkKeyScanExW(ASC(sChar), dKeybLayout)    
                  ELSE
                      iRet = VkKeyScanExA(ASC(sChar), dKeybLayout)
                  END IF
                      
                  bVkCode     = LO(BYTE, iRet)
                  bShiftState = HI(BYTE, iRet)
                  IF (bVkCode = 255) AND (bShiftState = 255) THEN RETURN  'Cant do this char so skip
                  
                  IF BIT(bShiftState, 0) THEN     'Shift
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_SHIFT             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR %KEYEVENTF_UNICODE                
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_SHIFT, 0)         
                  END IF
                  
                  IF BIT(bShiftState, 1) THEN     'Ctrl
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_CONTROL             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR %KEYEVENTF_UNICODE
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_CONTROL, 0)        
                  END IF
              
                  IF BIT(bShiftState, 2) THEN     'Alt
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                      inpAPI(lBufCnt).iki.wVk          = %VK_MENU             
                      inpAPI(lBufCnt).iki.dwFlags      = 0 OR %KEYEVENTF_UNICODE
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_MENU, 0)        
                  END IF
              
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD  
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dwFlags      = 0 OR %KEYEVENTF_UNICODE
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr                 
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)                
              
                  INCR lBufCnt
                  inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                  inpAPI(lBufCnt).iki.wVk          = bVkCode             
                  inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR %KEYEVENTF_UNICODE
                  inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr   
                  inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(bVkCode, 0)
              
              
                  IF BIT(bShiftState, 0) THEN     'Shift
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_SHIFT             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR %KEYEVENTF_UNICODE  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_SHIFT, 0)        
                  END IF
                  
                  IF BIT(bShiftState, 1) THEN     'Ctrl
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_CONTROL             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR %KEYEVENTF_UNICODE  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_CONTROL, 0)         
                  END IF
              
                  IF BIT(bShiftState, 2) THEN     'Alt
                      INCR lBufCnt
                      inpAPI(lBufCnt).dtype            = %INPUT_KEYBOARD   
                      inpAPI(lBufCnt).iki.wVk          = %VK_MENU             
                      inpAPI(lBufCnt).iki.dwFlags      = %KEYEVENTF_KEYUP OR %KEYEVENTF_UNICODE  
                      inpAPI(lBufCnt).iki.dtime        = Time : Time = Time + %TimeIncr 
                      inpAPI(lBufCnt).iki.wScan        = MAPVIRTUALKEY(%VK_MENU, 0)        
                  END IF            
              RETURN
                  
              END SUB
              '-------------------------------------------------------------------------
              Eddy

              Comment


              • #8
                Eddy,

                Just wanted to thank you for the SendInput.inc updates here and at http://www.powerbasic.com/support/pb...ad.php?t=40567

                Thx!!!
                3.14159265358979323846264338327950
                "Ok, yes... I like pie... um, I meant, pi."

                Comment


                • #9
                  You are very welcome, Jim!

                  Kind regards
                  Eddy

                  Comment


                  • #10

                    Slightly Updated Demo for SendInput.inc by Jim Fritts on 12 SEP 2018
                    Works with PBWIN 10.4 and José includes

                    Note:
                    If you use Ctrl J on Notepad a default message from this app will appear there.
                    If you use Ctrl K a popup dialog will appear on the screen asking what you would like to send.
                    The dialog may well be hidden behind Notepad so look for it.


                    Code:
                    '===============================================================================
                    ' Demo code to demonstrate the use of 'SendInput.inc'
                    ' 2009 - Eddy Van Esch
                    ' V1.0: 14-May-2009 : Initial version
                    '
                    '===============================================================================
                    'Slightly updated by Jim Fritts on 12 SEP 2018
                    'Works with PBWIN 10.4 and José includes
                    
                    'Note:
                    'If you use Ctrl J on Notepad a default message from this app will appear there.
                    'If you use Ctrl K a popup dialog will appear on the screen asking what you would like to send.
                    'The dialog may well be hidden behind Notepad so look for it.
                    
                    #COMPILE EXE
                    #REGISTER NONE
                    #DIM ALL
                    #INCLUDE ONCE "win32api.inc"  'add win32api if not already added
                    #INCLUDE ONCE "SendInput.Inc"
                    
                    GLOBAL hDlg AS LONG
                    
                    '-------------------------------------------------------------------------
                    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
                                iRet = TIMER
                                REDIM nAtom(1 TO 2)
                                nAtom(1) = GLOBALADDATOM ("SendInput_demo" + STR$(iRet + 1))
                                nAtom(2) = GLOBALADDATOM ("SendInput_demo" + STR$(iRet + 2))
                                REGISTERHOTKEY CBHNDL, nAtom(1), 2, ASC("J") ' Ctrl-J
                                REGISTERHOTKEY CBHNDL, nAtom(2), 2, ASC("K") ' Ctrl-K
                    
                            CASE %WM_DESTROY
                                FOR iCount = 1 TO 2
                                    UNREGISTERHOTKEY CBHNDL, nAtom(iCount)
                                    GLOBALDELETEATOM nAtom(iCount)
                                NEXT iCount
                    
                            CASE %WM_HOTKEY
                                SELECT CASE CBWPARAM
                                    CASE nAtom(1) 'Ctrl-J
                                        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}"
                    
                                    CASE nAtom(2) 'Ctrl-K
                                        sText = INPUTBOX$("Enter string to send" + $CRLF + $CRLF + "You have 1 second " _
                                           + "to switch back to the program to send the string.", "Enter string:")
                                        SLEEP 1000
                                        SendString sText
                    
                                END SELECT
                        END SELECT
                    END FUNCTION
                    
                    '-------------------------------------------------------------------------
                    
                    
                    '-------------------------------------------------------------------------
                    FUNCTION PBMAIN
                       DIALOG NEW 0, "SendInput demo",,, 300, 30,%WS_MINIMIZEBOX OR %WS_SYSMENU OR %WS_CAPTION, 0 TO hDlg
                       CONTROL ADD LABEL, hDlg, 205, "Start a program like Notepad and then press Ctrl-J or Ctrl-K to send keystrokes", 5, 10, 290, 15, %SS_CENTER
                       DIALOG SHOW MODAL hDlg CALL DlgProc
                    END FUNCTION
                    '-------------------------------------------------------------------------

                    Comment

                    Working...
                    X