I wrote a commandline utility that makes the specified window active and then sends keystrokes to it. The commandline string specifies the target window and the sequence of keyboard keys (text and keyboard commands) to be typed to that window, and the keystroke rate.
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 calling program or bat file 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.
This program is based on the work of Pierre Bellisle and Gosta H. Lovgren for enumerating windows to find the desired window's handle (http://www.powerbasic.com/support/pb...ad.php?t=23634) and Eddy Van Esch for the SendInput API (http://www.powerbasic.com/support/pb...ad.php?t=40567)
Note the program does not currently work on child window names specified in the commandline. (If you wish to add support for child windows, please refer to Pierre Bellisle's post cited above.) Note that if the child window is already active and you specify * in the commandline to type into the active window, then it should work.
I included the code in-line as part of this post and also as an attachment.
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 calling program or bat file 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.
This program is based on the work of Pierre Bellisle and Gosta H. Lovgren for enumerating windows to find the desired window's handle (http://www.powerbasic.com/support/pb...ad.php?t=23634) and Eddy Van Esch for the SendInput API (http://www.powerbasic.com/support/pb...ad.php?t=40567)
Note the program does not currently work on child window names specified in the commandline. (If you wish to add support for child windows, please refer to Pierre Bellisle's post cited above.) Note that if the child window is already active and you specify * in the commandline to type into the active window, then it should work.
I included the code in-line as part of this post and also as an attachment.
Code:
' May 28, 2009 11 pm Eastern ' this program takes the text and key commands specified in the commandline and types them out to the window specified in the commandline ' your keys are outpulsed to the target window as if they were typed from the keyboard (that is, they are not pasted via the clipboard) ' and thus you are able to send commands to the target application (e.g., alt key combinations, control key combinations, etc.) ' This program is based on the examples for enumerating windows by Pierre Bellisle and Gosta H. Lovgren ' http://www.powerbasic.com/support/pbforums/showthread.php?t=23634 ' and on the SendInput API code written by Eddy Van Esch ' Edy's SendInput.inc code has been integrated into my source code so you don't need to compile the separate inc file ' http://www.powerbasic.com/support/pbforums/showthread.php?t=40567 ' commandline specification 'PROGRAM.EXE |window_name|match_type|keystroke_delay|keystrokes 'OR 'PROGRAM.EXE "|window_name|match_type|keystroke_delay|keystrokes" 'where PROGRAM.EXE is the name of this program 'Quotation marks surrounding the commandline arguments are optional, but they are necessary if there are trailing spaces to be included at the end of the keystrokes ' the first character in the commandline argument is the delimiter. 'The pipe | character is the delimiter in the examples above. However the delimiter must be unique ' so if the | is one of your keystrokes, then it cannot be the delimiter ' if you want to use | as a keystroke, then change the delimiter in the commandline argument to something else like # 'PROGRAM.EXE #window_name#match_type#keystroke_delay#keystrokes 'PROGRAM.EXE "#window_name#match_type#keystroke_delay#keystrokes" ' you do not need to alter the program code if changing the delimiter ' the program automatically determines your delimiter from the first position in the commandline argument ' you cannot use "{" or "}" as a delimiter, as these are reserved characters for special keystrokes ' the window name is the title of the window that should receive the keystrokes ' you can enter the full window name or a partial string ' if you want the keystrokes to go to the active window, regardless of the active window's title, then use an asterisk * ' for the window name ' match_type, one of the following (single letter or the whole word) ' E or EXACT = the program selects a window where the title exactly matches the window name in the commandline argument ' S or START = the program selects a window where the first X characters of the selected window name match the window name specified in the commandline argument ' A or ANYWHERE = the window name in the commandline argument appears anywhere within the title of the selected window ' any of these values can be used if the window name = *. They will give the same result in this case. ' keystroke_delay = any integer, time delay, units in ms ' time delay may be needed so that keystrokes are not sent to the target window too quickly ' eg, {ALT_D}f{ALT_U}n opens the file menu and then types n. You may need a keystroke delay to ensure the application has enough time to open its file menu before it receives the n. ' Values ' <0 = the keystrokes you specify are sent to the sendinput API as a single block ' 0 = the keystrokes you specify are typed out one at a time (i.e., the program sends the keys to the sendinput API one at a time), with no extra delay between keystrokes ' >0 = the program sends the keys to the sendinput API one at a time. The program inserts a delay between each sendinput API call for the specified number of milliseconds, in order to slow down keystroke rate. ' keystrokes ' these are the keys you want typed out ' most special keys, like enter, backspace, escape, page up can all be included in the sequence ' using the syntax shown below after the examples ' note that the program does not currently allow you to distinguish in the keystrokes between numpad keys and their equivalents (e.g, 1 on the regular keyboard versus 1 on the numpad) ' you should only use "{" or "}" as part of the special keys ' the program does not currently allow you to type out "{" or "}" as literal keystrokes ' examples ' 'go to the notepad and type two lines of text with all keys sent as one block 'program.exe "|notepad|a|-1|line 1 - this is a test{ENTER}line 2 testing" 'in whichever window happens to be active, type ALT-f 'program.exe "|*|a|0|{ALT_D}f{ALT_U}" ' go to the file manager window and type 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 "|file manager|start|60|{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}" 'Syntax for special keys ' 'note that control, shift, and alt have separate keystrokes for pressing the key down and releasing the key. e.g., alt-f, which means pressing f while alt is down, is represented as {ALT_D}f{ALT_U} ' '{BACKSPACE} '{ENTER} '{CTRL_D} '{CTRL_U} '{ALT_D} '{ALT_U} '{SHIFT_D} '{SHIFT_U} '{TAB} '{ESCAPE} '{PGUP} '{PGDN} 'arrow keys '{LEFT} '{RIGHT} '{UP} '{DOWN} ' arrow keys - alternate syntax '{ARROWRIGHT} '{ARROWLEFT} '{ARROWUP} '{ARROWDOWN} '{END} '{HOME} '{F1} '{F2} '{F3} '{F4} '{F5} '{F6} '{F7} '{F8} '{F9} '{F10} '{F11} '{F12} '{INSERT} '{DELETE} '{APPS} '{NUMLOCK} 'caps lock '{CAPITAL} #COMPILE EXE #DIM ALL #INCLUDE "win32api.inc" GLOBAL typeofmatch AS STRING 'We are going to use a pointer to a variable of this type. TYPE EnumType hndl AS DWORD zClass AS ASCIIZ * %Max_Path zCaption AS ASCIIZ * %Max_Path ID AS DWORD hParent AS DWORD COUNT AS DWORD END TYPE FUNCTION PBMAIN () AS LONG LOCAL hProc AS LONG LOCAL hControl AS LONG LOCAL idControl AS LONG LOCAL SomeText AS ASCIIZ * %Max_Path '** Pierre had it set at 50 LOCAL EnumTry1 AS EnumType 'I changed it for "playing" purposes LOCAL EnumTry2 AS EnumType DIM windowname AS STRING DIM delimiterchar AS STRING DIM matchtype AS STRING DIM keystrokedelay AS INTEGER ' sleep time between keystrokes (units=ms) 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$) ' if you need leading or trailing spaces, then surrounded command line with quotes, which will be stripped off here IF LEFT$(originalinput,1)=$DQ AND RIGHT$(originalinput,1)=$DQ THEN originalinput=MID$(originalinput,2,LEN(originalinput)-2) END IF delimiterchar = LEFT$(originalinput,1) ' extract delimiter IF delimiterchar="{" OR delimiterchar="}" THEN MSGBOX "INVALID DELIMITER" EXIT FUNCTION END IF modifiedinput=RIGHT$(originalinput, LEN(originalinput)-1) ' remove delimiter from start of command line IF RIGHT$(originalinput,1)=delimiterchar THEN modifiedinput=LEFT$(modifiedinput, LEN(modifiedinput)-1) ' remove delimiter from end of command line if it is there END IF ' for texting 'msgbox modifiedinput+$CRLF+delimiterchar+$CRLF+str$(PARSECOUNT ( modifiedinput, delimiterchar)) IF PARSECOUNT ( modifiedinput, delimiterchar)<>4 THEN MSGBOX "INVALID DELIMITER STRUCTURE IN COMMAND LINE - NOT EQUAL TO 4 FIELDS" EXIT FUNCTION END IF 'window_name windowname=PARSE$(modifiedinput,delimiterchar,1) 'match_type matchtype=PARSE$(modifiedinput,delimiterchar,2) SELECT CASE UCASE$(matchtype) CASE "S" , "START" typeofmatch="PARTIALFRONT" CASE "A" , "ANYWHERE" , "ANY" typeofmatch="PARTIALMIDDLE" CASE "E" , "EXACT" typeofmatch="EXACT" CASE ELSE typeofmatch="EXACT" END SELECT 'keystroke_delay keystrokedelaystr=PARSE$(modifiedinput,delimiterchar,3) keystrokedelay=VAL(keystrokedelaystr) 'keystrokes keysequence=PARSE$(modifiedinput,delimiterchar,4) IF windowname<>"*" THEN ' change active window ------------------------------------------ EnumTry1.zCaption = windowname EnumTry1.hParent = 0 EnumWindows CODEPTR(EnumWindowProc), VARPTR(EnumTry1) 'If window exist EnumTry will be filled with window handle IF EnumTry1.hndl THEN ' if we have a matching window name hProc = EnumTry1.hndl SetForegroundWindow(hProc) ELSE MSGBOX "No matching window title" EXIT FUNCTION END IF ' ------------------------------------------------------------------------------- END IF keystrokes="" singlekey="" specialkeyflag="N" IF keystrokedelay>=0 THEN ' send keys one at a time 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 ' no keystroke delay, send all keystrokes as a block keystrokes=keysequence SendString keystrokes END IF END FUNCTION '______________________________________________________________________________ 'Retreive windows handle via caption FUNCTION EnumWindowProc(BYVAL hWindowProc AS LONG, BYVAL lParam AS LONG) AS LONG LOCAL zCaption AS ASCIIZ * %Max_Path LOCAL zClass AS ASCIIZ * %Max_Path LOCAL EnumTryPtr AS EnumType POINTER LOCAL LenClass AS LONG LOCAL LenCaption AS LONG 'Retrive a pointer to EnumTry so we can read and change it EnumTryPtr = lParam IF IsWindowVisible(hWindowProc) THEN 'Get only visible windows IF GetParent(hWindowProc) = @EnumTryPtr.hParent THEN 'Under desktop if 0 or under parent IF IsWindowEnabled(hWindowProc) THEN 'Get only enabled windows LenClass = GetClassName (hWindowProc, zClass, %MAX_PATH) 'Get the class of the dialog LenCaption = GetWindowText(hWindowProc, zCaption, %MAX_PATH) 'Get the caption of the dialog 'Did we found a dialog with our text? SELECT CASE typeofmatch CASE "EXACT" IF UCASE$(zCaption) = UCASE$(@EnumTryPtr.zCaption) THEN @EnumTryPtr.hndl = hWindowProc 'Put handle in EnumTry1 @EnumTryPtr.zCaption = zCaption 'Put caption in EnumTry1 @EnumTryPtr.zClass = zClass 'Put class in EnumTry1 FUNCTION = 0 : EXIT FUNCTION 'Stop enumeration and exit END IF CASE "PARTIALFRONT" ' UCASE$(@EnumTryPtr.zCaption) IS MY SPECIFIED TEXT IF LEFT$(UCASE$(zCaption),LEN(@EnumTryPtr.zCaption)) = UCASE$(@EnumTryPtr.zCaption) THEN @EnumTryPtr.hndl = hWindowProc 'Put handle in EnumTry1 @EnumTryPtr.zCaption = zCaption 'Put caption in EnumTry1 @EnumTryPtr.zClass = zClass 'Put class in EnumTry1 FUNCTION = 0 : EXIT FUNCTION 'Stop enumeration and exit END IF CASE "PARTIALMIDDLE" IF TALLY (UCASE$(zCaption), UCASE$(@EnumTryPtr.zCaption))>0 THEN @EnumTryPtr.hndl = hWindowProc 'Put handle in EnumTry1 @EnumTryPtr.zCaption = zCaption 'Put caption in EnumTry1 @EnumTryPtr.zClass = zClass 'Put class in EnumTry1 FUNCTION = 0 : EXIT FUNCTION 'Stop enumeration and exit END IF CASE ELSE END SELECT END IF END IF END IF FUNCTION = 1 'True, so Function will recall itself until last window found END FUNCTION '______________________________________________ '=============================================================================== ' 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. '---------------------------- ' '=============================================================================== '------------------------------------------------------------------------- '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 ' arrow keys 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 ' arrow keys - alternate syntax CASE "ARROWRIGHT" : bVkCode = %VK_RIGHT : GOSUB bVkCode_Down : GOSUB bVkCode_Up CASE "ARROWLEFT" : bVkCode = %VK_LEFT : GOSUB bVkCode_Down : GOSUB bVkCode_Up CASE "ARROWUP" : bVkCode = %VK_UP : GOSUB bVkCode_Down : GOSUB bVkCode_Up CASE "ARROWDOWN" : 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 '-------------------------------------------------------------------------
Comment