I know that the Journal Playback Hook is the proper way to send keys to
an application, but I have a lot of existing VB code that used their SendKeys
command. So I built an include file to emulate this command. It will
support all of the VB's sendkeys codes, plus I added several more codes to
do more functions that the VB sendkeys cant. (like mouse movements, sleep, etc)
I have tried to throughly test it, and it seems to work fine.
If fact it even send keys to a DOS window, which I had problems with VB SendKeys.
ADDED: AppActivate function (similar to VBs AppActivate)
ADDED: {LMOUSECLICK X Y} to give basic mouse functions
ADDED: {CHR 123} to send a ascii code to the window example {CHR 65} would send A
------------------
"I haven't lost my mind... its backed up on tape... I think??"
[This message has been edited by William Burns (edited March 23, 2004).]
an application, but I have a lot of existing VB code that used their SendKeys
command. So I built an include file to emulate this command. It will
support all of the VB's sendkeys codes, plus I added several more codes to
do more functions that the VB sendkeys cant. (like mouse movements, sleep, etc)
I have tried to throughly test it, and it seems to work fine.
If fact it even send keys to a DOS window, which I had problems with VB SendKeys.
ADDED: AppActivate function (similar to VBs AppActivate)
ADDED: {LMOUSECLICK X Y} to give basic mouse functions
ADDED: {CHR 123} to send a ascii code to the window example {CHR 65} would send A
Code:
'=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ ' SendKeys.inc - by William Burns revised 03/19/2004 ' ' Made with PowerBasic for Windows 7.0 ' ' This SendKeys function should emulate most of the same rules as the VisualBasic sendkeys ' ' Example usage: SendKeys "This is my text to send.{ENTER}" ' ' You can send these codes: {ENTER}{ESC}{UP}{DOWN}{LEFT}{RIGHT} ' {BACKSPACE}or{BS}{HOME}{END}{PGUP}{PGDN}{TAB}{DEL}or{DELETE} ' {INS}or{INSERT}{WIN}{PRTSC}(CAPSLOCK}{NUMLOCK}{SCROLLLOCK}{HELP} ' {PAUSEKEY} {BREAK}{LCONTROL}{RCONTROL}{LSHIFT}{RSHIFT}{ATTN} ' {LMENU}{RMENU} ' {F1}-{F16} {SLEEP 1000} <-- 1000 miliseconds ' {PAUSE 3.5} <-- Pauses 3.5 seconds (while processing event messages) ' {AppActivate WindowName} <-- switches to this window ' {LMOUSECLICK X Y} <-- Left button mouse click at x/y screen position ' {RMOUSECLICK X Y} <-- Right button mouse click at x/y screen position ' {MMOUSECLICK X Y} <-- Middle button mouse click at x/y screen position ' {CHR 123} <-- Send an ascii code 123 (same as pressing Alt-123 example: {CHR 65} would be A ' ' Procede characters with + for Shift, ^ for Ctrl, % for Alt example: ^p would be Ctrl-p ' You can also group characters for shift/ctrl/alt with parentheses () example: %(fx) would be Alt-F Alt-X ' To send these special characters {+^%~( as plain characters enclose them in braces example: {{} would just send { ' To send a key multiple times inclose it in braces with a number like {h 3} example: {DEL 41) would send DELETE key 41 times ' ' I also included a routine called SendPlainText to make it easier to send plain text without ' worrying about enclosing the special characters in braces (this one will not process codes) ' ' Note: the VB sendkeys has an optional 2nd parameter for pausing the program, but it is rarely used and ' since PowerBasic cant do optional parms, I left this parm off and gave you a seperate sub called ' WaitForNoKeys <-- just call this sub to see if the user has let go of all the keys, then call sendkeys ' ' ADDED: The code {AppActivate WindowName} will switch focus to the WindowName you specify ' or you can call this function direct: Result = AppActivate("Untitled - Notepad") ' '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ #If Not %Def(%WINAPI) #Include "win32api.inc" 'add win32api if not already added #EndIf '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Declare Sub SendKeys(ByVal sText As String) 'main sub (should work just like VBs SendKeys command Declare Sub WaitForNoKeys 'call this to wait until user lets go of all keys Declare Sub SendPlainText(ByVal sText As String) 'Similar to SendKeys except doesnt process codes or shifted states Declare Function AppActivate(ByVal sWinName As String) As Long 'sets focus to windowname Declare Sub SendEachKey(ByVal sChar As String, ByVal iShiftedState As Long) 'used internaly Declare Function SwitchToWin(ByVal iHandle As Long, ByVal iFlag As Long) As Long 'used internaly by AppActivate function '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub SendKeys(ByVal sText As String) Local iCount As Long Local iCount2 As Long Local iShiftedState As Long Local sSingleChar As String If Len(sText) < 1 Then Exit Sub For iCount = 1 To Len(sText) sSingleChar = Mid$(sText,iCount,1) If sSingleChar = "+" And IsFalse(iShiftedState And 1) Then 'Shift iShiftedState = iShiftedState Or 1 keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), 0, 0 Iterate For 'get next char End If If sSingleChar = "^" And IsFalse(iShiftedState And 2) Then 'Ctrl iShiftedState = iShiftedState Or 2 keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), 0, 0 Iterate For 'get next char End If If sSingleChar = "%" And IsFalse(iShiftedState And 4) Then 'Alt iShiftedState = iShiftedState Or 4 keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), 0, 0 Iterate For 'get next char End If If sSingleChar = "(" And iShiftedState Then 'group of shifted keys For iCount2 = iCount + 1 To (InStr(iCount + 2,sText,")") -1) 'process shift/ctrl/alt grouping sSingleChar = Mid$(sText,iCount2,1) If sSingleChar = "{" Then 'process code Call SendEachKey(Mid$(sText, iCount2, ((InStr(iCount2 + 2,sText,"}")-iCount2)+1)), iShiftedState) 'process codes iCount2 = InStr(iCount2 + 2,sText,"}") 'set next reg char to after the bracket Else 'not a code so process each char Call SendEachKey(sSingleChar, iShiftedState) End If Next iCount2 'next grouped key iCount = InStr(iCount + 2,sText,")") Else 'not grouped, so process each code or char If sSingleChar = "{" Then 'process code Call SendEachKey(Mid$(sText, iCount, ((InStr(iCount + 2,sText,"}")-iCount)+1)), iShiftedState) 'process codes iCount = InStr(iCount + 2,sText,"}") 'set next reg char to after the bracket Else 'not a code so process each char Call SendEachKey(sSingleChar, iShiftedState) End If End If If (iShiftedState And 1) Then keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg If (iShiftedState And 2) Then keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg If (iShiftedState And 4) Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg iShiftedState = 0 Next iCount End Sub '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub MouseClick(ByVal sButton As String, ByVal iXpos As Long, ByVal iYpos As Long) 'internal use If iXpos < 0 Or iyPos < 0 Then Exit Sub 'mouse_event %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 'move mouse to correct position SetCursorPos iXpos, iYpos 'move mouse Select Case sButton Case "L" 'left mouse button mouse_event %MOUSEEVENTF_LEFTDOWN Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 mouse_event %MOUSEEVENTF_LEFTUP Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 Case "R" 'right mouse button mouse_event %MOUSEEVENTF_RIGHTDOWN Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 mouse_event %MOUSEEVENTF_RIGHTUP Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 Case "M" 'middle mouse button mouse_event %MOUSEEVENTF_MIDDLEDOWN Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 mouse_event %MOUSEEVENTF_MIDDLEUP Or %MOUSEEVENTF_ABSOLUTE, iXpos, iYpos, 0, 0 End Select End Sub '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub SendChrCode(ByVal sChar As String, ByVal iShiftedState As Long) 'internal use Local iRet As Long Local iNumPad As Long Local iCounter As Long If (iShiftedState And 4) <> 4 Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), 0, 0 'press Alt For iCounter = 5 To Len(sChar) -1 If Asc(sChar,iCounter) > 47 And Asc(sChar,iCounter) < 58 Then 'only process chars 0 - 9 iNumPad = Val(Mid$(sChar,iCounter,1)) + &H60 'convert each number to the correct keypad scan code keybd_event iNumPad, MapVirtualKey(iNumPad, 0), 0, 0 'press keypad keybd_event iNumPad, MapVirtualKey(iNumPad, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg End If Next iCounter If (iShiftedState And 4) <> 4 Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg End Sub '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub SendEachKey(ByVal sChar As String, ByVal iShiftedState As Long) 'internal use Local iCounter As Long Local iTime As Single Local iRet As Long Local iVKcode As Integer Local iTempShifted As Long Local iRepeat As Single If Len(sChar) < 1 Then Exit Sub iVKcode = 0 iRepeat = 1 If sChar = "~" Then iVKcode = %VK_RETURN ' ~ is enter key If Len(sChar) > 1 And Left$(sChar,1) = "{" Then If ParseCount(sChar," ") > 1 Then 'second parm is included If UCase$(Parse$(sChar," ",1)) = "{APPACTIVATE" Then 'switch window code Call AppActivate(Mid$(sChar, 14,(Len(sChar)-14))) Exit Sub End If If UCase$(Parse$(sChar," ",1)) = "{LMOUSECLICK" Or UCase$(Parse$(sChar," ",1)) = "{RMOUSECLICK" Or UCase$(Parse$(sChar," ",1)) = "{MMOUSECLICK" Then 'mouseclick Call MouseClick(UCase$(Mid$(sChar,2,1)),Val(Parse$(sChar," ",2)), Val(Parse$(sChar," ",3))) Exit Sub End If If UCase$(Parse$(sChar," ",1)) = "{CHR" Then 'CHR code use Alt numpad SendChrCode(sChar,iShiftedState) Exit Sub End If iRepeat = Val(Left$(Parse$(sChar," ",2),Len(Parse$(sChar," ",2))-1)) 'grab repeat number only sChar = Parse$(sChar," ",1) + "}"'now set the work string to be real code End If Select Case UCase$(sChar) Case "{ENTER}" iVKcode = %VK_RETURN Case "{TAB}" iVKcode = %VK_TAB Case "{LMOUSECLICK}" iVKcode = %VK_LBUTTON Case "{RMOUSECLICK}" iVKcode = %VK_RBUTTON Case "{BACKSPACE}", "{BS}" iVKcode = %VK_BACK Case "{RIGHT}" iVKcode = %VK_RIGHT Case "{LEFT}" iVKcode = %VK_LEFT Case "{UP}" iVKcode = %VK_UP Case "{DOWN}" iVKcode = %VK_DOWN Case "{HOME}" iVKcode = %VK_HOME Case "{END}" iVKcode = %VK_END Case "{PGDN}" iVKcode = %VK_PGDN Case "{PGUP}" iVKcode = %VK_PGUP Case "{INS}", "{INSERT}" iVKcode = %VK_INSERT Case "{DEL}", "{DELETE}" iVKcode = %VK_DELETE Case "{ESC}" iVKcode = %VK_ESCAPE Case "{F1}" iVKcode = %VK_F1 Case "{F2}" iVKcode = %VK_F2 Case "{F3}" iVKcode = %VK_F3 Case "{F4}" iVKcode = %VK_F4 Case "{F5}" iVKcode = %VK_F5 Case "{F6}" iVKcode = %VK_F6 Case "{F7}" iVKcode = %VK_F7 Case "{F8}" iVKcode = %VK_F8 Case "{F9}" iVKcode = %VK_F9 Case "{F10}" iVKcode = %VK_F10 Case "{F11}" iVKcode = %VK_F11 Case "{F12}" iVKcode = %VK_F12 Case "{F13}" iVKcode = %VK_F13 Case "{F14}" iVKcode = %VK_F14 Case "{F15}" iVKcode = %VK_F15 Case "{F16}" iVKcode = %VK_F16 Case "{PRTSC}" iVKcode = %VK_SNAPSHOT Case "{WIN}" iVKcode = %VK_LWIN Case "{CAPSLOCK}" iVKcode = %VK_CAPITAL Case "{NUMLOCK}" iVKcode = %VK_NUMLOCK Case "{SCROLLLOCK}" iVKcode = %VK_SCROLL Case "{LCONTROL}" iVKcode = %VK_LCONTROL Case "{RCONTROL}" iVKcode = %VK_RCONTROL Case "{LSHIFT}" iVKcode = %VK_LSHIFT Case "{RSHIFT}" iVKcode = %VK_RSHIFT Case "{LMENU}" iVKcode = %VK_LMENU Case "{RMENU}" iVKcode = %VK_RMENU Case "{ATTN}" iVKcode = %VK_ATTN Case "{BREAK}" iVKcode = %VK_CANCEL Case "{PAUSEKEY}" iVKcode = %VK_PAUSE Case "{NOTHING}" 'added so you can use Alt while typing nothing ex: %{NOTHING} Exit Sub Case "{HELP}" iVKcode = %VK_HELP Case "{SLEEP}" 'suggested by Eros Olmi Sleep iRepeat Exit Sub Case "{PAUSE}" 'sleeps nicely while processing events iTime = Timer Do While Timer < (iTime + iRepeat) Sleep 100 Dialog DoEvents Loop Exit Sub Case Else sChar = Mid$(sChar,2,1) 'special char to send as normal key End Select End If If iVKcode = 0 Then 'process normal keys iRet = VkKeyScan(Asc(sChar)) If (LoByt(iRet) = -1) And (HiByt(iRet) = -1) Then Exit Sub 'cant do this char so skip iVKcode = LoByt(iRet) If (iShiftedState And HiByt(iRet)) <> HiByt(iRet) Then iTempShifted = HiByt(iRet) - (iShiftedState And HiByt(iRet)) 'and temp shift state if needed for this key End If If (iTempShifted And 1) Then keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), 0, 0 'hold down shift If (iTempShifted And 2) Then keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), 0, 0 'hold down control If (iTempShifted And 4) Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), 0, 0 'hold down alt Do keybd_event iVKcode, MapVirtualKey(iVKcode, 0), 0, 0 :Sleep 0 'key_down msg keybd_event iVKcode, MapVirtualKey(iVKcode, 0), %KEYEVENTF_KEYUP, 0 :Sleep 0'key_up msg Decr iRepeat Loop While iRepeat > 0 If (iTempShifted And 1) Then keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg If (iTempShifted And 2) Then keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg If (iTempShifted And 4) Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_UP msg End Sub '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub WaitForNoKeys 'you can use this function to wait until user lets go of the ctrl/Alt/Shift and any other keys 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 300 KeyWasPressed = 0 For iKey = 19 To 255 '0-9 and a-z If (GetAsyncKeyState(iKey) And 1) 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 '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Function AppActivate(ByVal sWinName As String) As Long 'Switches focus to the named window using undocumented SwitchToThisWindow function Local zWinName As Asciiz * %MAX_PATH Local hUser32 As Dword Local dProcAddr As Dword Local iRet As Long Local hFindDlg As Long zWinName = sWinName hFindDlg = FindWindow(ByVal 0, zWinName) 'find window by name If hFindDlg = 0 Then Exit Function 'cant find window so exit hUser32 = GetModuleHandle("user32.dll") 'get handle for DLL already in mem If hUser32 = 0 Then Exit Function 'cant find dll so exit dProcAddr = GetProcAddress(hUser32, "SwitchToThisWindow") 'get subroutine address from dll If dProcAddr = 0 Then Exit Function 'cant find sub in dll Call Dword dProcAddr Using SwitchToWin(hFindDlg, %TRUE) To iRet 'call routine Function = iRet 'return results (1=success 0=failed) End Function '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~ Sub SendPlainText(ByVal sText As String) Local iRet As Long Local iCounter As Long Local iVKcode As Integer Local iTempShifted As Long For iCounter = 1 To Len(sText) iRet = VkKeyScan(Asc(sText,iCounter)) If (LoByt(iRet) = -1) And (HiByt(iRet) = -1) Then Iterate For 'cant do this char so skip iVKcode = LoByt(iRet) iTempShifted = HiByt(iRet) If (iTempShifted And 1) Then keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), 0, 0: Sleep 0 'hold down shift If (iTempShifted And 2) Then keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), 0, 0: Sleep 0 'hold down control If (iTempShifted And 4) Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), 0, 0: Sleep 0 'hold down alt keybd_event iVKcode, MapVirtualKey(iVKcode, 0), 0, 0: Sleep 0 'key_down msg keybd_event iVKcode, MapVirtualKey(iVKcode, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_up msg If (iTempShifted And 1) Then keybd_event %VK_SHIFT, MapVirtualKey(%VK_SHIFT, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_up msg If (iTempShifted And 2) Then keybd_event %VK_CONTROL, MapVirtualKey(%VK_CONTROL, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_up msg If (iTempShifted And 4) Then keybd_event %VK_MENU, MapVirtualKey(%VK_MENU, 0), %KEYEVENTF_KEYUP, 0: Sleep 0 'key_up msg Next iCounter End Sub '=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
"I haven't lost my mind... its backed up on tape... I think??"
[This message has been edited by William Burns (edited March 23, 2004).]
Comment