About the SendKeysToOS method #150
-
|
It looks like you are running a vbs file, are you affected by the vbs deprecation? |
Beta Was this translation helpful? Give feedback.
Replies: 6 comments
-
|
Yes, of course that will be a problem. That method is one of several that are likely the reason why the VBA version of SeleniumVBA is flagged as a false-positive by some AV programs. |
Beta Was this translation helpful? Give feedback.
-
|
What about getting rid of the "SendKeysToOS" method? |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for your answer. |
Beta Was this translation helpful? Give feedback.
-
|
Thanks @GHRyunosuke - if it is not a requirement, then we will consider dropping it in future. Unfortunately, it is not the only functionality that AV does not like so dropping it might reduce the chances of false positives but will not eliminate them. But appreciate your feedback! @hanamichi77777 - it is not dangerous to use it if you need it. We implemented it to solve execution-blocking popups. But to some AV programs, it looks like we are doing something suspicious. As you can see from the code, it is not nefarious in any way. First use the standard Best, Mike |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for your answer. VBA.SendKeys ”hoge”, True '「VBA.」 aims to avoid name duplication |
Beta Was this translation helpful? Give feedback.
-
|
Since we are deprecating SendKeysToOS in SeleniumVBA v6.7, I'm including the method and associated support code here, in case anyone wants to continue using until MS removes VBScript from VBA. Recall that the original impetus for this method was to handle non-webpage execution blocking dialogs that could not be accessed by WebDriver's SendKeys method. '@Description("DEPRECATED - Sends a key sequence to the active or specified OS window - see WebKeyboard class for how to send special keys and chords. This method can optionally run on a delayed separate thread to interact with inline execution-blocking windows")
Public Sub SendKeysToOS(ByVal keyString As String, Optional ByVal timeDelayMS As Long = 0, Optional ByVal windowTitle As String = vbNullString, Optional ByVal runOnSeparateThread As Boolean = False, Optional ByVal waitForWindow As Boolean = False, Optional ByVal maxTimeToWaitMS As Long = 10000)
'WARNING: this could crash host app with MWB AV real-time protection
Dim i As Long
Dim modKeys As Collection
Dim endMatchPos As Long
Dim startMatchPos As Long
Dim matchLength As Long
Dim endMatchPosLast As Long
Dim startTime As Long
Dim winActivated As Boolean
Dim sendPackage As New Collection
'replace possible escaped special key strings with single (wide) character equivalents
keyString = unEscapeUnicode(keyString)
'check if we have special keys
If Not (keyString Like "*[" & ChrW$(&HE000&) & "-" & ChrW$(&HE05D&) & "]*") Then
'no special keys or chords
sendPackage.Add keyString
Else
'search for chords, parse, and then map the input key sequence(s) to VBA/VBScript SendKeys format
Dim oRegExp As New RegExp
Dim oMatches As MatchCollection
Dim oMatch As match
Dim modKeyString As String
oRegExp.Global = True
oRegExp.Pattern = "(([" & ChrW$(&HE008&) & "-" & ChrW$(&HE00A&) & "])+)(.*?)" & ChrW$(&HE000&)
Set oMatches = oRegExp.execute(keyString)
'check if we have cords
If oMatches.Count > 0 Then
'process cords and keys between cords
endMatchPosLast = 0
For Each oMatch In oMatches
startMatchPos = oMatch.FirstIndex + 1 'regexp is zero-based so add 1
matchLength = oMatch.Length
endMatchPos = startMatchPos + matchLength - 1
If startMatchPos - 1 > endMatchPosLast Then
'non-cord action
sendPackage.Add osKeyMapper(Mid$(keyString, endMatchPosLast + 1, startMatchPos - endMatchPosLast - 1))
End If
'now do the cord action
'handle more than one modifier keys
Set modKeys = splitKeyString(oMatch.SubMatches(0))
modKeyString = vbNullString
'press the modifiers down
For i = 1 To modKeys.Count
modKeyString = modKeyString & osKeyMapper(modKeys(i))
Next i
sendPackage.Add modKeyString & "(" & osKeyMapper(oMatch.SubMatches(2)) & ")"
endMatchPosLast = endMatchPos
Next oMatch
If Len(keyString) > endMatchPosLast Then
'last non-cord action
sendPackage.Add osKeyMapper(Mid$(keyString, endMatchPosLast + 1, Len(keyString) - endMatchPosLast))
End If
Else 'no cords so send keys in one call
sendPackage.Add osKeyMapper(keyString)
End If
End If
If runOnSeparateThread Then
'create the vbs script file to run on a separate thread
Dim vbs As String
vbs = "Set WshShell = WScript.CreateObject(""WScript.Shell"")" & vbCrLf
'wait specified amount of time before executing script
If timeDelayMS > 0 Then vbs = vbs & "WScript.Sleep " & timeDelayMS & vbCrLf
If windowTitle <> vbNullString Then
'user is after a particular window
If waitForWindow Then
vbs = vbs & "winActivated = WshShell.AppActivate(""" & windowTitle & """, False)" & vbCrLf
vbs = vbs & "startTime = Timer()" & vbCrLf
vbs = vbs & "Do Until winActivated" & vbCrLf
vbs = vbs & vbTab & "WScript.Sleep 100" & vbCrLf
vbs = vbs & vbTab & "If 1000*(Timer()-startTime) > " & maxTimeToWaitMS & " Then Exit Do" & vbCrLf
vbs = vbs & vbTab & "winActivated = WshShell.AppActivate(""" & windowTitle & """, False)" & vbCrLf
vbs = vbs & "Loop" & vbCrLf
Else
vbs = vbs & "winActivated = WshShell.AppActivate(""" & windowTitle & """, False)" & vbCrLf
End If
vbs = vbs & "If winActivated Then" & vbCrLf
For i = 1 To sendPackage.Count
vbs = vbs & vbTab & "WshShell.SendKeys """ & sendPackage(i) & """, False" & vbCrLf
Next i
vbs = vbs & "End If" & vbCrLf
Else
'no window specified so send keys to active window
'unfortunately, there seems to be no means in VBScript of making sure we are not sending to the VBIDE
'in this case so hopefully user is not in debug mode
For i = 1 To sendPackage.Count
vbs = vbs & "WshShell.SendKeys """ & sendPackage(i) & """, False" & vbCrLf
Next i
End If
'save the vbs string to user's temp file
Dim filePath As String
filePath = ResolvePath("%TEMP%\seleniumVBAtmp" & Timer() & ".vbs", False)
SaveStringToFile vbs, filePath
'launch the script!
Shell "WScript """ & filePath & """ , vbNormalFocus"
'remember to clean up later on WebDriver terminate event
cleanupTempFiles = True
Else
'not running on separate thread, so use native VBA instead of VBSript
If timeDelayMS > 0 Then WebShared.sleep timeDelayMS
If windowTitle <> vbNullString Then
If waitForWindow Then
winActivated = WebShared.AppActivate(windowTitle, False) 'try to activate the window
startTime = VBA.Timer()
Do Until winActivated
WebShared.sleep 100
If 1000 * (Timer() - startTime) > maxTimeToWaitMS Then Exit Do
winActivated = WebShared.AppActivate(windowTitle, False) 'try to activate the window
Loop
Else
winActivated = WebShared.AppActivate(windowTitle, False) 'try to activate the window
End If
If winActivated Then
For i = 1 To sendPackage.Count
VBA.SendKeys sendPackage(i), False 'send the keys
Next i
End If
Else
'no window specified so send keys to active window
'but make sure we are not sending to the IDE if debugging!!!!
If Not IsActiveWindowVBIDE Then
For i = 1 To sendPackage.Count
VBA.SendKeys sendPackage(i), False 'send the keys
Next i
End If
End If
End If
End Sub
Private Function osKeyMapper(ByVal keyString As String) As String
Dim oRegExp As New RegExp
Dim mappedValue As String
Dim i As Long
Dim key As String
Dim keyStringSave As String
'make map array static
Static map(0 To 93) As String
Static initMap As Boolean
If Not initMap Then
'create lookup array where index corresponds to Hex value of Selenium special key
map(0) = vbNullChar
map(1) = "{BREAK}" 'Cancel key - OS-specific keystroke sequence that performs a cancel action ^break
map(2) = "{HELP}"
map(3) = "{BACKSPACE}"
map(4) = "{TAB}"
map(5) = "{CLEAR}"
map(6) = "{ENTER}" 'Return
map(7) = "{ENTER}"
map(8) = "+" 'Shift
map(9) = "^" 'Ctrl
map(10) = "%" 'Alt
map(11) = "" 'The pause key
map(12) = "{ESCAPE}"
map(13) = Chr$(32) 'Space key
map(14) = "{PGUP}"
map(15) = "{PGDN}"
map(16) = "{END}"
map(17) = "{HOME}"
map(18) = "{LEFT} "
map(19) = "{UP} "
map(20) = "{RIGHT} "
map(21) = "{DOWN} "
map(22) = "{INSERT}"
map(23) = "{DELETE}"
map(24) = ";"
map(25) = "="
map(26) = "0"
map(27) = "1"
map(28) = "2"
map(29) = "3"
map(30) = "4"
map(31) = "5"
map(32) = "6"
map(33) = "7"
map(34) = "8"
map(35) = "9"
map(36) = "*"
map(37) = "+"
map(38) = ""
map(39) = "-"
map(40) = "."
map(41) = "/"
map(49) = "{F1}"
map(50) = "{F2}"
map(51) = "{F3}"
map(52) = "{F4}"
map(53) = "{F5}"
map(54) = "{F6}"
map(55) = "{F7}"
map(56) = "{F8}"
map(57) = "{F9}"
map(58) = "{F10}"
map(59) = "{F11}"
map(60) = "{F12}"
map(61) = "^{ESCAPE}" 'Meta or command key
map(64) = vbNullString ' ZenkakuHankaku Key
map(80) = "+"
map(81) = "^"
map(82) = "%"
map(83) = "^{ESCAPE}" 'Meta or Command key
map(84) = "{PGUP}"
map(85) = "{PGDN}"
map(86) = "{END}"
map(87) = "{HOME}"
map(88) = "{LEFT} "
map(89) = "{UP} "
map(90) = "{RIGHT} "
map(91) = "{DOWN} "
map(92) = "{INSERT}"
map(93) = "{DELETE}"
initMap = True
End If
'replace possible escaped special key strings with single (wide) character equivalents
keyString = unEscapeUnicode(keyString)
'escape keys corresponding to special VBA SendKeys chars with brackets {}
oRegExp.Global = True
oRegExp.Pattern = "([+^%~{}\[\]()])"
keyString = oRegExp.Replace(keyString, "{$1}")
'map potential Selenium special keys to VBA/VBScript Sendkeys counter-parts
keyStringSave = keyString
For i = 1 To Len(keyStringSave)
key = Mid$(keyStringSave, i, 1)
If AscWL(key) >= &HE000& And AscWL(key) <= &HE05D& Then
'we have a special key
mappedValue = map(AscWL(key) - &HE000&)
If mappedValue = vbNullString Then Err.Raise 1, , "Error in using SendKeysToOS - key " & "\u" & VBA.Right$("0000" & VBA.Hex$(AscW(key)), 4) & " not supported"
'replace Selenium special key occurrence with mapped VBA/VBScript version
keyString = Replace$(keyString, key, mappedValue, , 1)
End If
Next i
osKeyMapper = keyString
End Function
Public Function IsActiveWindowVBIDE() As Boolean
'determines whether the VBDIDE is the active window or not
Dim winTitle As String
winTitle = String$(200, vbNullChar)
GetWindowText GetForegroundWindow(), StrPtr(winTitle), 200
IsActiveWindowVBIDE = Left$(winTitle, InStr(winTitle, vbNullChar) - 1) Like "Microsoft Visual Basic for Applications -*"
End FunctionWebDriver SendKeysToOS Method. For inline execution-blocking windows such as "Select Certificate" popup, If running on a separate thread, there are two ways of delaying the If running on a separate thread and the window title is not known, then specify an explicit amount of time via the The following code example shows how to handle an execution-blocking input box that has a known title. For an example of an execution-blocking window of unknown title, see the Select Certificate discussion. Dim driver As New WebDriver
Dim keys As New WebKeyboard
driver.StartChrome
driver.OpenBrowser
'Sends input to the future InputBox via separate thread
driver.SendKeysToOS _
keyString:="Here is my input to InputBox!" & keys.EnterKey, _
windowTitle:="InputBox Title", _
runOnSeparateThread:=True, _
waitForWindow:=True, _
maxTimeToWaitMS:=2000, _
timeDelayMS:=0
driver.NavigateTo "https://example.com/"
driver.Wait 1000
'this InputBox dialog will block execution flow until it receives input
'so must send input keys from separate thread launched earlier
ThisWorkbook.Activate
'InputBox returns the user input keys
Debug.Print InputBox("Please enter input keys:", "InputBox Title")
driver.Wait 1000
driver.CloseBrowser
driver.Shutdown |
Beta Was this translation helpful? Give feedback.
Thanks @GHRyunosuke - if it is not a requirement, then we will consider dropping it in future. Unfortunately, it is not the only functionality that AV does not like so dropping it might reduce the chances of false positives but will not eliminate them. But appreciate your feedback!
@hanamichi77777 - it is not dangerous to use it if you need it. We implemented it to solve execution-blocking popups. But to some AV programs, it looks like we are doing something suspicious. As you can see from the code, it is not nefarious in any way. First use the standard
SendKeyswhich works 95% of the time.Best, Mike