PDA

View Full Version : Send Keystrokes Without Interrupting The User?? - There has to be a way



Abbylovedon
10-11-2014, 03:00 PM
Hello-

Background:
We have this ridiculous invoicing program at my company that management refuses to abandon. So, it drove me to learn some VBA and write this macro. Basically, this code loops through a column of our customers, sending a series of keystrokes to our invoicing program, windows explorer, and Foxit-PhantomPDF in order to produce the end-result invoice that is emailed to our 1500 customers. I'm proud to say that I've gotten it to a point where it can run completely unmanned, and produce an invoice for a customer every 50 seconds (Even though most programs can produce well more than 1500 invoices in probably less than 5 seconds).

Problem:
In any case, is there no way to send keystrokes without AppActivate'ing or SetForegroundWindow(hWnd)'ing or interfering with what the user is working on??? Postmessage, Sendmessage, Sendkeys, Keybd_event - can none of these do this? IT Dept. will not allow an idle account/computer set up just to run a macro twice a month. Any help toward figuring out a way I could run this or something similar while still working on other things would GREATLY be appreciated. Thank you in advance!

Code Notes:


Excel 2013 - Windows 8
Code will run in a workplace
My VBA skills are at the beginner level
My API declarations are in a different module (I'm not sure if I should've included it, but I can provide it if you need it)
I know Sendkeys is the worst possible way to send keystrokes - but there was an API I found online that works with SendKeys and it seems to be the only way I can send keystrokes to the invoicing program (the invoicing application runs through RDC)
Code provided down below!




Option Explicit
'********************************************
' Invoice Software LOOP
'
' This will loop through the A column and
' create invoices based on the supplied
' values
'
' Don: Updated 10-11-2014
'********************************************
Public Sub Software_Loop()
Dim hWnd As Long
Dim StatusTxt As Range
Dim BeginDate As String
Dim EndDate As String
Dim InvoiceDate As String
Dim DueDate As String
Dim SaveSpot As String
Dim FolderName As String
Dim InvoicePG1 As String
Dim InvoicePG2 As String
Dim MyMsg As String
Dim CtrlShiftTabKey As String
Dim LeftKey As String
Dim ReturnKey As String
Dim AltKey As String
Dim DownKey As String
Dim TabKey As String
Dim ShiftTabKey As String
Dim i As Integer
Dim SaveLength As Integer
Dim SaveLess As Integer
Dim OpenPos As Integer
Dim ClosePos As Integer

'%%%%%%%%%%%% Set Objects %%%%%%%%%%%%%%
Set StatusTxt = Range("M4") 'Status
BeginDate = Range("E7").Value 'Begin Date Range
EndDate = Range("F7").Value 'End Date Range
InvoiceDate = Range("G7").Value 'Invoice Date
DueDate = Range("H7").Value 'Due Date
SaveSpot = Range("E12").Value 'Save Location
InvoicePG1 = "1"
InvoicePG2 = "1a"
SaveLength = Len(SaveSpot) 'Length
SaveLess = SaveLength - 1
ClosePos = InStrRev(SaveSpot, "\")
OpenPos = InStrRev(SaveSpot, "\", SaveLess)
FolderName = Mid(SaveSpot, OpenPos + 1, ClosePos - OpenPos - 1)
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

'Change status to invoicing
StatusTxt.Value = "Invoicing..."
StatusTxt.Font.Color = vbRed

'Begin the loop
Do
DoEvents
If IsEmpty(ActiveCell) Then
Exit Do
End If
ThisWorkbook.Save

'\\\\ Software \\\\\\'
'========================
' SEARCH THE GROUP
'========================
'Loop until the invoice management window is found
Do
hWnd = FindWindow(vbNullString, "Invoice Management (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
'Invoice window is open - Create new invoice
If hWnd Then
hWnd = SetForegroundWindow(hWnd)
CtrlShiftTabKey = "^(+{tab})"
SendCtrlShiftTabKey CtrlShiftTabKey
LeftKey = "{left}"
SendLeftKey LeftKey
ReturnKey = "{return}"
SendReturnKey ReturnKey
End If
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\

'Enter group name based on active cell in Excel
MyMsg = ActiveCell.Value
For i = 1 To Len(MyMsg)
SendAKey Mid(MyMsg, i, 1)
Debug.Print Asc(Mid(MyMsg, i, 1))
Next i
SendAKey vbCr
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ReturnKey = "{return}"
SendReturnKey ReturnKey

'&^&^& Check for the NOT FOUND window &^&^&
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, "Search Results (RDC.company.com)")
If hWnd Then 'Popup Occurs - Group can't be found
hWnd = SetForegroundWindow(hWnd)
'Exit the window
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ReturnKey = "{return}"
SendReturnKey ReturnKey
'Code group couldn't be found & move down to next group
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = "GROUP NOT FOUND" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
ActiveCell.Offset(1, 0).Select
GoTo LoopOver
End If

'========================
' CREATE A NEW INVOICE
'========================
'Press 'New Invoice' button
AltKey = "%" 'Alt
SendAltKey AltKey
DownKey = "{DOWN}" 'Down Arrow
SendDownKey DownKey
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey
'Next>>Next>>Next Buttons
Application.Wait (Now + TimeValue("0:00:03")) 'Wait \\\\\\\
TabKey = "{tab}" 'Tab
SendTabKey TabKey
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey
ShiftTabKey = "+({tab})" 'Shift+Tab
SendShiftTabKey ShiftTabKey
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey

'Enter invoice date Information
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
For i = 1 To Len(BeginDate)
SendAKey Mid(BeginDate, i, 1)
Debug.Print Asc(Mid(BeginDate, i, 1))
Next i
SendAKey vbCr
For i = 1 To Len(EndDate)
SendAKey Mid(EndDate, i, 1)
Debug.Print Asc(Mid(EndDate, i, 1))
Next i
SendAKey vbCr
For i = 1 To Len(InvoiceDate)
SendAKey Mid(InvoiceDate, i, 1)
Debug.Print Asc(Mid(InvoiceDate, i, 1))
Next i
SendAKey vbCr
For i = 1 To Len(DueDate)
SendAKey Mid(DueDate, i, 1)
Debug.Print Asc(Mid(DueDate, i, 1))
Next i
SendAKey vbCr
'Proceed to the save screen
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey

'=========================
' CHECK IF INVOICE EXISTS
'=========================
'&^&^& Check for the Invoice already created window &^&^&
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, _
"Invoice Creation Wizard (Fill in Invoice Dates:) (RDC.company.com)")
If hWnd Then 'Popup Occurs - Invoice exists error
hWnd = SetForegroundWindow(hWnd)
'Close the window
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ShiftTabKey = "+({tab})" 'Shift+Tab
SendShiftTabKey ShiftTabKey
ReturnKey = "{return}" 'Enter
SendReturnKey ReturnKey
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = "INVOICE EXISTS" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Selection.Offset(1, 0).Select
GoTo LoopOver
End If

'==================
' SAVE THE INVOICE
'==================
'// PAGE 1 \\
'Select the "Print Created Invoice" Output Option
DownKey = "{DOWN}" 'Down Arrow
SendDownKey DownKey
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
TabKey = "{tab}" 'Tab
SendTabKey TabKey
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
'&^&^& Check if the Select Printer window popped up &^&^&
Do
hWnd = FindWindow(vbNullString, "Print (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup Occurs - Print to Foxit PDF Printer
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
Else
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = "RESTART HERE1" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Exit Do
End If
'&^&^& Check if the "Print to PDF Document - Foxit Reader PDF Printer" window popped up &^&^&
Do
hWnd = FindWindow(vbNullString, _
"Print to PDF Document - Foxit Reader PDF Printer (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup Occurs - Print to Foxit Reader PDF Printer
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ShiftTabKey = "+({tab})" 'Shift+Tab
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
MyMsg = SaveSpot & InvoicePG1 'Save the first page
For i = 1 To Len(MyMsg)
SendAKey Mid(MyMsg, i, 1)
Debug.Print Asc(Mid(MyMsg, i, 1))
Next i
SendAKey vbCr
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
Else
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "RESTART HERE2" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Exit Do
End If

'&^&^& Check if "Confirm Save As" popped up - A front page was left/not renamed &^&^&
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, "Confirm Save As (RDC.company.com)")
If hWnd Then 'Popup Occurs - replace the file
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
TabKey = "{tab}" 'Tab
SendTabKey TabKey
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
'Code that a front page already existed
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "OVERWRITE1" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
End If

'&^&^& Close the Foxit Reader PDF Popups &^&^&
Do
hWnd = FindWindow(vbNullString, _
InvoicePG1 & ".pdf - Foxit Reader (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup open - Close the window
SendMessage hWnd, WM_SYSCOMMAND, SC_CLOSE, 0&
End If

'// PAGE 2 \\
'&^&^& Check if the Print window popped up 2nd time &^&^&
Do
hWnd = FindWindow(vbNullString, "Print (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup Occurs - Print to Foxit Reader PDF Printer
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
Else
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "RESTART HERE3" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Exit Do
End If

'&^&^& Check if the Print to PDF Document - Foxit Reader PDF Printer popped up 2nd time &^&^&
Do
hWnd = FindWindow(vbNullString, "Print to PDF Document - Foxit Reader PDF Printer (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup Occurs - Print to Foxit Reader PDF Printer
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ShiftTabKey = "+({tab})" 'Shift+Tab
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
SendShiftTabKey ShiftTabKey
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
MyMsg = SaveSpot & InvoicePG2 'Invoice detail page
For i = 1 To Len(MyMsg)
SendAKey Mid(MyMsg, i, 1)
Debug.Print Asc(Mid(MyMsg, i, 1))
Next i
SendAKey vbCr
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
Else
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "RESTART HERE4" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Exit Do
End If

'&^&^& Check if "Confirm Save As" popped up - A detail page was left/not renamed &^&^&
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, "Confirm Save As (RDC.company.com)")
If hWnd Then 'Popup Occurs - replace the file
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
TabKey = "{tab}" 'Tab
SendTabKey TabKey
ReturnKey = "{enter}" 'Enter
SendReturnKey ReturnKey
'Code that a front page already existed
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "OVERWRITE2" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
End If

'&^&^& Close the Foxit Reader PDF Popups &^&^&
Do
hWnd = FindWindow(vbNullString, _
InvoicePG2 & ".pdf - Foxit Reader (RDC.company.com)")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Popup open - Close the window
SendMessage hWnd, WM_SYSCOMMAND, SC_CLOSE, 0&
End If

'\\\\ Local PC \\\\\\'

'=====================
' COLLATE THE INVOICE
'=====================
'Open the InvoicePG2 file in Foxit
ShellExecute 0, "Open", SaveSpot & InvoicePG2 & ".pdf", "", "", vbNormalNoFocus
'&^&^& Check if the InvoicePG2 file opened in Foxit &^&^&
Do
hWnd = FindWindow(vbNullString, InvoicePG2 & ".pdf - Foxit PhantomPDF")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Insert the 1st page
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "%", True 'Alt
SendKeys "o", True 'O Key
SendKeys "f", True 'F Key
SendKeys "n", True 'N Key
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "f", True 'F Key
Else
'Code the error
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "PDF DID NOT OPEN" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
Exit Do
End If

'&^&^& Check if the Foxit Open window popped up &^&^&
Do
hWnd = FindWindow(vbNullString, "Open")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Select InvoicePG1 file
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
SendKeys SaveSpot & InvoicePG1, True 'File Location
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{enter}", True 'Enter
End If

'&^&^& Check if the Foxit Insert Files window popped up &^&^&
Do
hWnd = FindWindow(vbNullString, "Insert Files")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Combine the files together
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{tab}", True 'Tab
SendKeys "{tab}", True 'Tab
SendKeys "{enter}", True 'Enter
End If

'&^&^& Save & close while checking if invoice is blank &^&^&
Do
hWnd = FindWindow(vbNullString, InvoicePG2 & ".pdf * - Foxit PhantomPDF")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Foxit PhantomPDF is open - save
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "%", True 'Alt
SendKeys "F", True 'F Key
SendKeys "S", True 'S Key
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
SendKeys "%", True 'alt Key
SendKeys "d", True 'd Key
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "invoice", True 'invoice Key
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{enter}", True 'Enter Key
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, "Foxit PhantomPDF")
If hWnd Then 'Popup Occurs - Invoice word not found
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
SendKeys "{enter}", True 'Enter Key
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
ActiveCell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = ActiveCell.Value & "BLANK" '$$ERROR$$
ActiveCell.Font.Color = vbRed
ActiveCell.Offset(0, -1).Select
End If
End If
Do
hWnd = FindWindow(vbNullString, InvoicePG2 & ".pdf - Foxit PhantomPDF")
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Insert the 1st page
SendMessage hWnd, WM_CLOSE, 0, 0&
End If

'=================
' RENAME THE FILE
'=================
'Open the folder containing the invoice
Shell "C:\Windows\explorer.exe /select, " & SaveSpot & InvoicePG2 & ".pdf", _
vbNormalFocus

'Rename file based on Active Cell
Do
hWnd = FindWindow(vbNullString, FolderName)
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Rename the file
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
SendKeys "{f2}", True 'F2
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys ActiveCell.Value, True
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{enter}", True 'Enter
End If

'&^&^& Check for the confirm file name change &^&^&
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
hWnd = FindWindow(vbNullString, "Rename File")
If hWnd Then 'Window is open - Combine the files together
hWnd = SetForegroundWindow(hWnd)
SendKeys "{enter}", True 'Enter
Application.Wait (Now + TimeValue("0:00:02")) 'Wait \\\\\\\
End If

Do
hWnd = FindWindow(vbNullString, FolderName)
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Rename the file
SendMessage hWnd, WM_CLOSE, 0, 0&
End If

'Delete the InvoicePG2 file & close window
Shell "C:\Windows\explorer.exe /select, " & SaveSpot & InvoicePG1 & ".pdf", _
vbNormalFocus

Do
hWnd = FindWindow(vbNullString, FolderName)
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Delete the file
hWnd = SetForegroundWindow(hWnd)
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{del}", True 'Del
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
SendKeys "{enter}", True 'Enter
Application.Wait (Now + TimeValue("0:00:01")) 'Wait \\\\\\\
Do
hWnd = FindWindow(vbNullString, FolderName)
DoEvents
Sleep 200
Loop Until hWnd <> 0
If hWnd Then 'Window is open - Rename the file
SendMessage hWnd, WM_CLOSE, 0, 0&
End If
End If

'Move down one cell
Selection.Offset(1, 0).Select

LoopOver:
Loop Until IsEmpty(ActiveCell)

'Change status to done
StatusTxt.Value = "DONE"
StatusTxt.Font.Color = vbGreen
Exit Sub
End Sub

snb
10-12-2014, 03:14 AM
Doesn't he invoice program provide for any import facility ?

You could explore the program Auto Hotkey, in which you can concatenate an unlimited amount of keystrokes.

Kenneth Hobs
10-13-2014, 08:23 AM
First, speedup your range updates using this or parts of it. http://vbaexpress.com/kb/getarticle.php?kb_id=1035

When using a sendkeys approach, focus and timing are critical. As such, the user can not be using the computer while the program is running. Add a MsgBox() at the end or a MacroStatusPrompt() to tell the user the program has completed.

It is better to use loops when looking for a Window's handle or Window Caption rather than to add sleeps or waits.

When trying to find a Class name, or other Window data, I use spy program like the 2nd link from the bottom. http://patorjk.com/blog/software/


Not that this will help but if you want to try an API method to sendkeys, this below might give you an idea.

Class ClsVKKeys:

'--------- Class Name: clsKeys
'Some code, ~50%, similar to clsKeyboard found in next two links.
'zip with class, http://www.freevbcode.com/ShowCode.asp?ID=340
'same but code, http://www.visualbasic.happycodings.com/API_and_Miscellaneous/code38.html


'vk_keys, http://msdn.microsoft.com/en-us/library/ms927178.aspx
'Kenneth Hobson, http://www.mrexcel.com/forum/showthread.php?p=2872719


Option Explicit


Private Declare Function MapVirtualKey Lib "user32" Alias _
"MapVirtualKeyA" (ByVal wCode As Long, _
ByVal wMapType As Long) As Long


Private Declare Function VkKeyScan Lib "user32" Alias "VkKeyScanA" (ByVal _
cChar As Byte) As Integer

Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


Private Declare Function GetKeyState Lib "user32" _
(ByVal nVirtKey As Long) As Integer


Private Const KEYEVENTF_EXTENDEDKEY = &H1
Private Const KEYEVENTF_KEYUP = &H2


Public Enum vk
vMouse_Left_Button = 1
vMouse_Rigt_Button = 2
vCancel = 3
vMouse_Middle_Button = 4
vMouse_X1_Button = 5
vMouse_X2_Button = 6
'undefined
vBackspace = 8
vTab = 9
'Reserved
'Reserved
vClear = 12
vEnter = 13
'Undefined
'Undefined
vShift = 16
vCtrl = 17
valt = 18 'Menu
vPause = 19
vCapsLock = 20 'Capital
vIME = 21 'Input Method Editor (IME) Kana mode, IME Hanguel mode (maintained for compatibility; use VK_HANGUL), IME Hangul mode
'Undefined
vIME_Junja_Mode = 23
vIME_Final_Mode = 24
vIME_Hanja_Mode = 25
'Undefined
vESC = 27
vIME_Convert = 28
vIME_NonConvert = 29
vIME_Accept = 30
vIME_ModeChange = 31
vSpace = 32 'Spacebar
vPage_Up = 33 'Prior
vPage_Down = 34 'Next
vEnd = 35
vHome = 36
vLeft = 37
vUp = 38
vRight = 39
vDown = 40
vSelect = 41
vPrint = 42
vExecute = 43
vPrintScreen = 44 'Snapshot
vInsert = 45
vDelete = 46
vHelp = 47
vKey0 = 48
vKey1 = 49
vKey2 = 50
vKey3 = 51
vKey4 = 52
vKey5 = 53
vKey6 = 54
vKey7 = 55
vKey8 = 56
vKey9 = 57
'Undefined
'Undefined
'Undefined
'Undefined
'Undefined
'Undefined
'Undefined
vKeyA = 65
vKeyB = 66
vKeyC = 67
vKeyD = 68
vKeyE = 69
vKeyF = 70
vKeyG = 71
vKeyH = 72
vKeyI = 73
vKeyJ = 74
vKeyK = 75
vKeyL = 76
vKeyM = 77
vKeyN = 78
vKeyO = 79
vKeyP = 80
vKeyQ = 81
vKeyR = 82
vKeyS = 83
vkeyt = 84
vKeyU = 85
vKeyV = 86
vKeyW = 87
vKeyX = 88
vKeyY = 89
vKeyZ = 90
vWindowsLeft = 91
vWindowsRight = 92
vApplications = 93 'Natural keyboard
'Reserved
vSleep = 95
vNumpad0 = 96
vNumpad1 = 97
vNumpad2 = 98
vNumpad3 = 99
vNumpad4 = 100
vNumpad5 = 101
vNumpad6 = 102
vNumpad7 = 103
vNumpad8 = 104
vNumpad9 = 105
vMultiply = 106
vAdd = 107
vSeparator = 108
vSubtract = 109
vDecimal = 110
vDivide = 111
vF1 = 112
vF2 = 113
vF3 = 114
vF4 = 115
vF5 = 116
vF6 = 117
vF7 = 118
vF8 = 119
vF9 = 120
vF10 = 121
vF11 = 122
vF12 = 123
vF13 = 124
vF14 = 125
vF15 = 126
vF16 = 127
vF17 = 128
vF18 = 129
vF19 = 130
vF20 = 131
vF21 = 132
vF22 = 133
vF23 = 134
vF24 = 135
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
vNumLock = 144
vScrollLock = 145
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
'Unassigned
vLeft_Shift = 160
vRight_Shift = 161
vLeft_Control = 162
vRight_Control = 163
vLeft_Menu = 164
vRight_Menu = 165
vBrowser_Back = 166
vBrowser_Forward = 167
vBrowser_Refresh = 168
vBrowser_Stop = 169
vBrowser_Search = 170
vBrowser_Favorites = 171
vBrowser_Home = 172
vVolume_Mute = 173
vVolume_Down = 174
vVolume_Up = 175
vMedia_Next_Track = 176
vMedia_Previous_Track = 177
vMedia_Stop = 178
vMedia_Play_Pause = 179
vLaunch_Mail = 180
vLaunch_Media_Select = 181
vLaunch_App1 = 182
vLaunch_App2 = 183
'Reserved
'Reserved
vOEM_1 = 186
vOEM_Plus = 187
vOEM_Comma = 188
vOEM_Minus = 189
vOEM_Period = 190
vOEM_2 = 191
vOEM_3 = 192
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Reserved
'Unassigned
'Unassigned
'Unassigned
vOEM_4 = 219
vOEM_5 = 220
vOEM_6 = 221
vOEM_7 = 222
'
'Reserved
'
vOEM_102 'Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard
'OEM Specific
'OEM Specific
vProcessKey = 229
'OEM Specific
vPacket = 231 'Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP
'Unassigned
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'OEM Specific
'vAttn=246
vSelectCursor = 247
vSelectExtended = 248
vErase_EOF = 249
vPlay = 250
vZoom = 251
vNoname = 252 'Reserved for future use
vPA1 = 253
vOEM_Clear = 254
End Enum


Public Sub SleepMS(ms As Long)
Sleep ms
End Sub


'Loop through a string and calls PressKey for each character (Does not
' parse strings like SendKeys)
Public Sub PressString(ByVal sString As String, Optional bDoEvents As Boolean = True)
Do While sString <> ""
PressKey Mid(sString, 1, 1)

Sleep 20
If bDoEvents Then
DoEvents
End If

sString = Mid(sString, 2)
Loop
End Sub


'Presses a specific key (this is used for keys that don't have a
' ascii equilivant)
Public Sub PressKeyVK(keyPress As vk, Optional bHold As Boolean, _
Optional bRelease As Boolean, Optional bCompatible As Boolean)

Dim nScan As Long
Dim nExtended As Long

nScan = MapVirtualKey(keyPress, 2)
nExtended = 0
If nScan = 0 Then
nExtended = KEYEVENTF_EXTENDEDKEY
End If
nScan = MapVirtualKey(keyPress, 0)

If bCompatible Then
nExtended = 0
End If

If Not bRelease Then
keybd_event keyPress, nScan, nExtended, 0
End If

If Not bHold Then
keybd_event keyPress, nScan, KEYEVENTF_KEYUP Or nExtended, 0
End If
End Sub


'Presses the single key represented by sKey
Public Sub PressKey(sKey As String, Optional bHold As Boolean, Optional bRelease As Boolean)


Dim nVK As Long
nVK = VkKeyScan(Asc(sKey))

If nVK = 0 Then
Exit Sub
End If

Dim nScan As Long
Dim nExtended As Long

nScan = MapVirtualKey(nVK, 2)
nExtended = 0
If nScan = 0 Then
nExtended = KEYEVENTF_EXTENDEDKEY
End If
nScan = MapVirtualKey(nVK, 0)

Dim bShift As Boolean
Dim bCtrl As Boolean
Dim bAlt As Boolean

bShift = (nVK And &H100)
bCtrl = (nVK And &H200)
bAlt = (nVK And &H400)

nVK = (nVK And &HFF)

If Not bRelease Then
If bShift Then
keybd_event vk.vShift, 0, 0, 0
End If
If bCtrl Then
keybd_event vk.vCtrl, 0, 0, 0
End If
If bAlt Then
keybd_event vk.valt, 0, 0, 0
End If

keybd_event nVK, nScan, nExtended, 0
End If

If Not bHold Then
keybd_event nVK, nScan, KEYEVENTF_KEYUP Or nExtended, 0

If bShift Then
keybd_event vk.vShift, 0, KEYEVENTF_KEYUP, 0
End If
If bCtrl Then
keybd_event vk.vCtrl, 0, KEYEVENTF_KEYUP, 0
End If
If bAlt Then
keybd_event vk.valt, 0, KEYEVENTF_KEYUP, 0
End If
End If
End Sub


' Use this to send key command key plus a command key. e.g. Shift+Tab
Sub KeyPlusKey(str1 As Variant, str2 As Variant)
KeyDown str1
Key str2
KeyUp str1
End Sub


Sub KeyPlusKeyPlusKey(str1 As Variant, str2 As Variant, str3 As Variant)
keybd_event str1, MapVirtualKey(str1, 0), 0, 0
keybd_event str2, MapVirtualKey(str2, 0), 0, 0
keybd_event str3, MapVirtualKey(str3, 0), 0, 0

keybd_event str3, MapVirtualKey(str3, 0), 2, 0
keybd_event str2, MapVirtualKey(str2, 0), 2, 0
keybd_event str1, MapVirtualKey(str1, 0), 2, 0

'KeyDown str1
'KeyDown str2
'KeyDown str3
'KeyUp str3
'KeyUp str2
'KeyUp str1
End Sub


Sub KeyPlusKeyPlusKeyPlusKey(str1 As Variant, str2 As Variant, str3 As Variant, str4 As Variant)
'keybd_event str1, MapVirtualKey(str1, 0), 0, 0
'keybd_event str2, MapVirtualKey(str2, 0), 0, 0
'keybd_event str3, MapVirtualKey(str3, 0), 0, 0
'keybd_event str4, MapVirtualKey(str4, 0), 0, 0

'keybd_event str4, MapVirtualKey(str4, 0), 2, 0
'keybd_event str3, MapVirtualKey(str3, 0), 2, 0
'keybd_event str2, MapVirtualKey(str2, 0), 2, 0
'keybd_event str1, MapVirtualKey(str1, 0), 2, 0

KeyDown str1
KeyDown str2
KeyDown str3
Key str4
KeyUp str3
KeyUp str2
KeyUp str1
End Sub


' Use this to send key command plus a key combination. e.g. Ctrl+O
Sub KeyPlusChar(str1 As Variant, str2 As Variant)
KeyDown str1
Key Asc(str2)
KeyUp str1
End Sub


' KeyDown() and KeyUp() for each character string in str.
Sub Keys(str As Variant)
Dim i As Integer, s As String, v As Variant
For i = 1 To Len(str)
s = Mid(str, i, 1)
DoEvents
PressKey s
Next i
End Sub


' Release a key
Sub KeyUp(str As Variant)
'keybd_event str, &H9D, 2, 0
keybd_event str, MapVirtualKey(str, 0), 2, 0
End Sub


' Press a key
Sub KeyDown(str As Variant)
'keybd_event str, &H9D, 0, 0
keybd_event str, MapVirtualKey(str, 0), 0, 0
End Sub


' Press and release a key
Sub Key(str As Variant)
KeyDown str
KeyUp str
End Sub


'Key Lock Routines
'Returns (in the boolean variables) the status of the various Lock keys
Public Sub GetLockKeyStatus(bCapsLock As Boolean, bNumLock As Boolean, _
bScrollLock As Boolean)
bCapsLock = CBool(GetKeyState(vk.vCapsLock))
bNumLock = CBool(GetKeyState(vk.vNumLock))
bScrollLock = CBool(GetKeyState(vk.vScrollLock))
End Sub


Public Sub GetLockKeysStatus(ByRef bCapsLock As Boolean, ByRef bNumLock As Boolean, _
ByRef bScrollLock As Boolean)
bCapsLock = CBool(GetKeyState(vk.vCapsLock))
bNumLock = CBool(GetKeyState(vk.vNumLock))
bScrollLock = CBool(GetKeyState(vk.vScrollLock))
End Sub


Sub ShowAllLockKeyStatus()
Dim bCaps As Boolean, bNum As Boolean, bScroll As Boolean
bCaps = True
bNum = True
bScroll = True
GetLockKeysStatus bCaps, bNum, bScroll
MsgBox "Caps: " & vbTab & bCaps & vbLf & "Num: " & vbTab & bNum & vbTab & vbLf & _
"Scroll: " & vbTab & bScroll, Title:="Lock Button's Active Status"
End Sub




Sub KeyLock(myKey As vk, State As Boolean)
'State=True means to press key if state is off
'myKey must be: Num, Scroll, or Caps as String type.

Select Case True
Case myKey = vk.vNumLock
If State <> CBool(GetKeyState(vk.vNumLock)) Then PressKey (vk.vNumLock)
Case myKey = vScrollLock
If State <> CBool(GetKeyState(vk.vScrollLock)) Then PressKey (vk.vScrollLock)
Case myKey = vCapsLock
If State <> CBool(GetKeyState(vbKeyCapital)) Then PressKey (vk.vCapsLock)
Case Else
'Nothing to do
End Select
End Sub


Sub PressKeySimple(theKey As vk)
keybd_event theKey, 0, 0, 0 'press key
keybd_event theKey, 0, &H2, 0 'release key
End Sub


Sub NumsOn()
KeyLock vk.vNumLock, True
End Sub


Sub NumsOff()
KeyLock vk.vNumLock, False
End Sub


Sub CapsOn()
KeyLock vk.vCapsLock, True
End Sub


Sub CapsOff()
KeyLock vk.vCapsLock, False
End Sub


Sub ScrollOn()
KeyLock vk.vScrollLock, True
End Sub


Sub ScrollOff()
KeyLock vk.vScrollLock, False
End Sub


Module Test:

Sub Test_Key_F5()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
'Presses F5
kb.Key vk.vF5
End Sub


Sub Test_KeyPlusKey_CtrlPlusT()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
'Ctrl+T
'kb.KeyPlusKey vk.vCtrl, Asc("T")
'or
kb.KeyPlusKey vk.vCtrl, vk.vkeyt
End Sub


Sub Test_KeyPlusChar_CtrlPlusT()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
'Ctrl+T
kb.KeyPlusChar vk.vCtrl, "T"
End Sub


Sub Test_KeyPlusKeyPlusKey_AltPlusCtrlPlusDel()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
'AltCtrl+Delete, Famous three finger solute: Logoff Dialog
kb.KeyPlusKeyPlusKey vk.valt, vk.vCtrl, vk.vDelete
End Sub


Sub Test_PressString_HelloWorld()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
kb.PressString "Hello World!"
End Sub


Sub Test_Keys_HelloWorld()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
kb.Keys "Hello World!"
End Sub


Sub Test_ShowLockKeysState()
Dim kb As clsVKKeys
Set kb = New clsVKKeys
kb.ShowAllLockKeyStatus
End Sub


Sub Test_GetLockKeyStatus_ScrollLock()
Dim kb As clsVKKeys, bScroll As Boolean
Set kb = New clsVKKeys
bScroll = True
kb.GetLockKeyStatus False, False, bScroll
MsgBox "Scroll: " & vbTab & bScroll
End Sub


Private Sub Test_PressKeyVK() 'Needs work.
Dim kb As clsVKKeys
Set kb = New clsVKKeys
'kb.PressKeyVK keyF5
Range("A1").Select
With kb
.SleepMS 20
.PressKeyVK vCtrl, True 'Press Ctrl key and hold down
.PressKey "T", True, False
.PressKey "T", False
.PressKeyVK vCtrl, False, True 'Release Ctrl key
End With
End Sub

GTO
10-13-2014, 10:39 PM
@Kenneth Hobs:

Hey Ken - here's another freebie win spy if of interest.

http://www.catch22.net/software/winspy-17

Mark