Jump to content

Recommended Posts

Hello everyone,

I've created a UDF for basic communication with SSH servers. I know there is already such a UDF, but I wasn't satisfied with it for my purpose, so I created a new one.

This UDF also acts as a wrapper for the plink executable. Its essential functions are _SSHConnect, _SSHSend, _SSHRecv and _SSHCloseSocket.

It does support multiple simultaneous connections and aims to be pretty robust. Feel free to share your opinions :)

Two of the included examples use a slightly modified version of Vintage Terminal by @Chimp

Spoiler
#include-once

#include <Constants.au3>
#include <Timers.au3>
#include <Array.au3>

OnAutoItExitRegister("__internal_closeAllSockets")

; #INDEX# =======================================================================================================================
; Title .........: SSH_UDF <ssh.au3>
; Udf Version....: 1.0.1 (10-2021)
; AutoIt Version : 3.3.14.5
; Language ......: English
; Description ...: Provides basic functionality to connect and communicate with SSH servers
; Author(s) .....: p4sCh
; Exe(s) ........: plink.exe; plink.exe is distributed under MIT License. See LICENSE.txt
; ===============================================================================================================================

; #PUBLIC FUNCTIONS# ============================================================================================================
; _SSHConnect       <Essential>
; _SSHLogin         <Optional>
; _SSHSend          <Essential>
; _SSHRecv          <Essential>
; _SSHCloseSocket   <Essential>
; _SSHSetConfig     <Optional>
; _SSHGetConfig     <Optional>
; _SSHSetPS1        <Misc>
; ===============================================================================================================================

; #INTERNAL FUNCTIONS# ==========================================================================================================
; __internal_SSHRecv
; __internal_applyInputEcho
; __internal_sanitizeOutput
; __internal_addSocket
; __internal_removeSocket
; __internal_isSocket
; __internal_getSocketIdx
; __internal_closeSocketAndReturn
; __internal_closeAllSockets
; hostToIP
; isValidIPv4
; isValidIPv6
; ===============================================================================================================================

; #CONSTANTS# ===================================================================================================================
Global Const $SSH_HOST_KEY_AUTO_ACCEPT = 0, $SSH_HOST_KEY_AUTO_ACCEPT_AND_STORE = 1, _
    $SSH_HOST_KEY_ABORT_IF_UNKNOWN = 2, $SSH_HOST_KEY_INTERACTIVE = 3
; ===============================================================================================================================

; #VARIABLES# ===================================================================================================================
Global $__plinkFolder = @ScriptDir & "\"
Global $__plinkExe = "plink.exe"
Global $__plinkFullPath = $__plinkFolder & $__plinkExe

Global $__echoLoginEnabled=True, $__stripBracketedPasteModeANSICodes=True, _
       $__stripPS1BeforeBell=True, $__stripANSIColorCodes=False, $__sanitizePlinkStderr=False

Dim $__socket[1], $__echoStr[1]
_ArrayPop($__socket)
_ArrayPop($__echoStr)
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHConnect
; Description ...: Establishes a connection with a ssh server
; Parameters ....: $host        - IPv4, IPv6 or hostname
;                  $login       - username
;                  $passwd      - password
;                  $port        - ssh port (default 22)
;                  $sshKeyFingerprint - how should _SSHConnect react when ssh key fingerprint of server is unknown? options are:
;                      $SSH_HOST_KEY_AUTO_ACCEPT          : accept ssh host key (do not store key in cache) (default)
;                      $SSH_HOST_KEY_AUTO_ACCEPT_AND_STORE: accept ssh host key and store in cache (registry) for future
;                      $SSH_HOST_KEY_ABORT_IF_UNKNOWN     : reject ssh host key if unknown (and disconnect)
;                      $SSH_HOST_KEY_INTERACTIVE          : don't answer when prompted, let user decide
; Return values .: Success      - Returns socket id greater than zero
;                               - Returns zero when connection was established and then canceled
;                                 (because ssh key fingerprint was unknown and $SSH_HOST_KEY_ABORT_IF_UNKNOWN was used)
;                  Failure      - Returns error code less than zero
; Author ........: p4sCh
; Modified.......:
; Remarks .......: Login(username) and password don't have to be passed to this function. You can also login when prompted.
;                  You can use _SSHLogin for logging in when prompted.
; Related .......: _SSHCloseSocket, _SSHLogin
; ===============================================================================================================================
Func _SSHConnect($host, $login="", $passwd="", $port=22, $sshKeyFingerprint=$SSH_HOST_KEY_AUTO_ACCEPT)
    $host = hostToIP($host)
    If $host == "" Then Return -1
    If Not FileExists($__plinkFullPath) Then
        ConsoleWriteError("SSH UDF: Path to plink executable is not valid! Path: "&$__plinkFullPath&@LF)
        Return -2
    EndIf

    Local $plinkCmd = $__plinkFullPath & " -P " & $port
    If $passwd <> "" Then $plinkCmd &= ' -pw "' & $passwd & '"'
    If Not StringIsSpace($login) Then
        $plinkCmd &= " " & $login & "@" & $host
    Else
        $plinkCmd &= " " & $host
    EndIf

    Local $socket = Run($plinkCmd, $__plinkFolder, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD + $STDERR_CHILD)
    If @error Then Return -3

    Local $sIdx = __internal_addSocket($socket)
    If $sshKeyFingerprint <> $SSH_HOST_KEY_INTERACTIVE Then
        Local $totalTimeout = _Timer_Init()
        Local $recvTimeout = -1
        Local $recv = ""
        Do
            $recv = __internal_SSHRecv($socket, False)
            If @error > 1 Then Return __internal_closeSocketAndReturn($socket, -4)
            If $recv <> "" Then
                $recvTimeout = _Timer_Init()
                If $__echoLoginEnabled Then $__echoStr[$sIdx] &= $recv
                If StringInStr($recv, "store key in cache?") Then
                    Local $success, $answer
                    If $sshKeyFingerprint = $SSH_HOST_KEY_AUTO_ACCEPT_AND_STORE Then
                        $answer = "y"&@CR
                    ElseIf $sshKeyFingerprint == $SSH_HOST_KEY_AUTO_ACCEPT Then
                        $answer = "n"&@CR
                    ElseIf $sshKeyFingerprint == $SSH_HOST_KEY_ABORT_IF_UNKNOWN Then
                        $answer = @CR
                    Else
                        Return __internal_closeSocketAndReturn($socket, -5)
                    EndIf
                    If $__echoLoginEnabled Then $__echoStr[$sIdx] &= $answer & @LF
                    $success = _SSHSend($socket, $answer)
                    If Not $success Then Return __internal_closeSocketAndReturn($socket, -6)
                    If $sshKeyFingerprint == $SSH_HOST_KEY_ABORT_IF_UNKNOWN Then Return __internal_closeSocketAndReturn($socket, 0)
                    ExitLoop
                EndIf
            Else
                Sleep(15)
            EndIf
        Until ($recvTimeout <> -1 And _Timer_Diff($recvTimeout) > 1000) Or _Timer_Diff($totalTimeout) > 5500
    EndIf

    Return $socket
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHLogin
; Description ...: If login or password was not passed to _SSHConnect, then _SSHLogin can be used
;                  to pass login information to interactive prompt
; Parameters ....: $host        - ssh socket
;                  $login       - username
;                  $passwd      - password
; Return values .: Success      - Returns True
;                  Failure      - Returns False
; Author ........: p4sCh
; Modified.......:
; Remarks .......: This function can be used when prompted login and/or password.
;                  This function might not work correctly in every case.
; Related .......: _SSHConnect
; ===============================================================================================================================
Func _SSHLogin($socket, $login="", $passwd="", $timeoutMs=5500)
    Local $sIdx = __internal_getSocketIdx($socket)
    If $sIdx < 0 Then Return False
    Local $loginEntered = False, $passwdEntered = False, $success = False
    Local $timer = _Timer_Init()
    Local $i = 0
    Do
        Local $recv
        If $i == 0 Then
            $recv = $__echoStr[$sIdx]
        Else
            $recv = __internal_SSHRecv($socket, False)
            If @error > 1 Then Return False
        EndIf
        If StringInStr($recv,"login as:") Then
            If $login == "" Or $loginEntered Then Return False
            If $__echoLoginEnabled Then
                If $i <> 0 Then $__echoStr[$sIdx] &= $recv
                $__echoStr[$sIdx] &= $login & @LF
            EndIf
            Local $ret = _SSHSend($socket, $login & @LF)
            If Not $ret Then Return False
            $loginEntered = True
        ElseIf StringInStr($recv,"password:") Then
            If $passwd == "" Or $passwdEntered Then Return False
            If $__echoLoginEnabled Then
                If $i <> 0 Then $__echoStr[$sIdx] &= $recv
                $__echoStr[$sIdx] &= "****" & @LF
            EndIf
            Local $ret = _SSHSend($socket, $passwd & @LF)
            If Not $ret Then Return False
            $passwdEntered = True
        Else
            If $i == 0 Then
                $i = 1
                ContinueLoop
            EndIf
            $__echoStr[$sIdx] &= $recv
            If $recv <> "" And ($loginEntered Or $passwdEntered) Then
                $success = True
                ExitLoop
            EndIf
        EndIf
        Sleep(10)
        $i += 1
    Until _Timer_Diff($timer) > $timeoutMs
    Return $success
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHSend
; Description ...: Send data to the ssh session.
; Parameters ....: $socket      - ssh socket
;                  $data        - string to send
; Return values .: Success      - Returns True
;                  Failure      - Returns False
; Author ........: p4sCh
; Modified.......:
; Remarks .......: When communicating with plink directly (e.g. ssh host key prompt) enter commands by appending @CR.
;                  When communicating with ssh server (everything else) often both @CR and @LF work for entering commands.
; Related .......: _SSHRecv
; ===============================================================================================================================
Func _SSHSend($socket, $data)
    If Not __internal_isSocket($socket) Then Return False
    StdinWrite($socket, $data)
    If @error Then Return False
    Return True
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHRecv
; Description ...: Receive data returned from plink (data source might be plink itself or ssh session)
; Parameters ....: $socket      - ssh socket
; Return values .: Success      - Returns data read from stdout
;                  Failure      - Sets @error greater than zero.
;                      @error=1:  Returns data read from stderr (can be important)
;                      @error=2:  Returns empty string. Socket invalid
;                      @error=3:  Returns empty string. Socket invalid OR stdout and stderr not available OR other error
;                                 Probably means you got disconnected from the ssh session
; Author ........: p4sCh
; Modified.......:
; Remarks .......:
; Related .......: _SSHSend
; ===============================================================================================================================
Func _SSHRecv($socket)
    Local $recv = __internal_SSHRecv($socket)
    SetError(@error)
    Return $recv
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHCloseSocket
; Description ...: Close a ssh socket returned by _SSHConnect
; Parameters ....: $socket      - ssh socket (ByRef)
; Return values .: N/A
; Author ........: p4sCh
; Modified.......:
; Remarks .......: This function sets passed ssh socket to zero
; Related .......: _SSHConnect
; ===============================================================================================================================
Func _SSHCloseSocket(ByRef $socket)
    If Not __internal_isSocket($socket) Then Return
    StdioClose($socket)
    ProcessClose($socket)
    __internal_removeSocket($socket)
    $socket = 0
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHSetConfig
; Description ...: Set ssh configuration values
; Parameters ....: $variableName    - name of the variable to be changed
;                  $value           - new value for that variable
; Return values .: Success          - Returns True. New value set
;                  Failure          - Returns False. Either non-existent variable name or wrong value type
; Author ........: p4sCh
; Modified.......:
; Remarks .......: Valid variableName:[value-type] pairs are:
;                  "plinkFolder":[String]         - Path where plink executable is located (default: @ScriptDir & "\")
;                  "plinkExe":[String]            - plink executable name (default: plink.exe)
;                  "echoLoginEnabled":[Bool]      - Receive full login procedure via _SSHRecv (default: True)
;                  "stripBracketedPasteModeANSICodes":[Bool] - Remove Bracketed-Paste-Mode ANSI Codes from ssh output
;                                                              These Codes are ESC[?2004h and ESC[?2004l (default: True)
;                  "stripPS1BeforeBell":[Bool]    - If found ESC[0; ANSI Code and then Bell-Character in one line of ssh output,
;                                                   these characters and everything in between get removed (default: True)
;                  "stripANSIColorCodes":[Bool]   - Removes ANSI Color Codes and ANSI SGR Codes from _SSHRecv (default: False)
;                  "sanitizePlinkStderr":[Bool]   - Applies sanitize functions (stripBracketedPasteModeANSICodes,
;                                                   stripPS1BeforeBell, stripANSIColorCodes) also to plinks stderr
;                                                   instead of only to its stdout (default: False)
; Related .......: _SSHGetConfig
; ===============================================================================================================================
Func _SSHSetConfig($variableName, $value)
    If $variableName == "" Or $value == "" Then Return False
    If StringLeft($variableName, 1) == "$" Then $variableName = StringRight($variableName, StringLen($variableName) - 1)

    Switch $variableName
        Case "plinkFolder", "plinkExe"
            If Not IsString($value) Then
                ConsoleWriteError("SSH Config error: Could not change $"&$variableName&". Wrong value type, String expected!"&@LF)
                Return False
            ElseIf $variableName == "plinkFolder" Then
                If StringRight($value, 1) <> "\" And StringRight($value, 1) <> "/" Then $value &= "\"
                $__plinkFullPath = $value & $__plinkExe
            ElseIf $variableName == "plinkExe" Then
                $__plinkFullPath = $__plinkFolder & $value
            EndIf
        Case "echoLoginEnabled", "stripBracketedPasteModeANSICodes", "stripPS1BeforeBell", "stripANSIColorCodes", _
             "sanitizePlinkStderr"
            If Not IsBool($value) Then
                ConsoleWriteError("SSH Config error: Could not change $"&$variableName&". Wrong value type, Bool expected!"&@LF)
                Return False
            EndIf
        Case "socket", "echoStr"
            Return False
    EndSwitch
    If Not Assign("__" & $variableName, $value, $ASSIGN_EXISTFAIL) Then
        ConsoleWriteError("SSH Config error: Variable $"&$variableName&" does not exist!"&@LF)
        Return False
    EndIf
    Return True
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHGetConfig
; Description ...: Get ssh configuration values
; Parameters ....: $variableName    - name of the variable to be returned
; Return values .: Success          - Returns value of the requested variable
;                  Failure          - Returns empty string and sets @error to non-zero
; Author ........: p4sCh
; Modified.......:
; Remarks .......: Names of returnable variables are: "plinkFolder", "plinkExe", "echoLoginEnabled",
;                  "stripBracketedPasteModeANSICodes", "stripPS1BeforeBell", "stripANSIColorCodes", "sanitizePlinkStderr"
;                  For explanation of these variables see _SSHSetConfig
; Related .......: _SSHSetConfig
; ===============================================================================================================================
Func _SSHGetConfig($variableName)
    If StringLeft($variableName, 1) == "$" Then $variableName = StringRight($variableName, StringLen($variableName) - 1)
    Local $value = Eval("__" & $variableName)
    Return SetError(@error, 0, $value)
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _SSHSetPS1
; Description ...: Changes the prompt string on the ssh server
; Parameters ....: $socket      - ssh socket
;                  $PS1         - new prompt string
; Return values .: Success      - Returns True
;                  Failure      - Returns False (might return True if 'export PS1="x"' command does not work on ssh server)
; Author ........: p4sCh
; Modified.......:
; Remarks .......: Works by executing the command 'export PS1="x"' where x is the string ($PS1) passed to the function
; Related .......: N/A
; ===============================================================================================================================
Func _SSHSetPS1($socket, $PS1 = "SSH: ")
    Return _SSHSend($socket, 'export PS1="' & $PS1 & '"' & @CR)
EndFunc

; #INTERNAL FUNCTIONS# ==========================================================================================================
; Author ........: p4sCh (except for isValidIPv4 and isValidIPv6)
; ..............->
Func __internal_SSHRecv($socket, $addEcho=True, $peek=False)
    Local $output = ""
    Local $sIdx = __internal_getSocketIdx($socket)
    If $sIdx < 0 Then Return SetError(2, 0, $output)
    Local $stdoutErr = False
    $output = StdoutRead($socket, $peek)
    If $output == "" Then
        If @error Then $stdoutErr = True
        If $addEcho Then __internal_applyInputEcho($output, $sIdx)
        $output &= StderrRead($socket, $peek)
        If $output <> "" Then
            If $__sanitizePlinkStderr Then __internal_sanitizeOutput($output)
            Return SetError(1, 0, $output)
        ElseIf @error And $stdoutErr == True Then
            Return SetError(3, 0, $output)
            EndIf
    Else
        __internal_sanitizeOutput($output)
        If $addEcho Then __internal_applyInputEcho($output, $sIdx)
    EndIf
    Return $output
EndFunc

Func __internal_applyInputEcho(ByRef $str, $idx)
    $str = $__echoStr[$idx] & $str
    $__echoStr[$idx] = ""
EndFunc

Func __internal_sanitizeOutput(ByRef $str)
    If $__stripBracketedPasteModeANSICodes Then
        $str = StringReplace($str,"�[?2004h","")
        $str = StringReplace($str,"�[?2004l"&@CR,"")
    EndIf
    If $__stripPS1BeforeBell Then
        If StringInStr($str, "�") Then
            Local $lines = StringSplit($str, @CRLF)
            Local $p1, $p2
            For $i=1 To $lines[0]
                $p1 = StringInStr($str,"�]0;")
                If $p1 <> 0 Then
                    $p2 = StringInStr($str, "�", 1, 1, $p1)
                    If $p2 <> 0 Then
                        $str = StringLeft($str, $p1-1) & StringRight($str, StringLen($str) - $p2)
                    EndIf
                EndIf
            Next
        EndIf
    EndIf
    If $__stripANSIColorCodes Then
        ; Matching Pattern: ESC\[[0-107][;0-107][;0-255]m               ; leading zeros are matched
        ; Matches ANSI Codes: SGR, 3-bit color, 4-bit color and 8-bit color
        $str = StringRegExpReplace($str,"�\[([0-1]0[0-7]|\d?\d)?(;[0-1]0[0-7]|;\d?\d)?(;[0-1]?\d?\d|;(0|2)[0-5][0-5])?m", "")
        ; Matching Pattern: ESC\[(38|48);2;[0-255];[0-255];[0-255]m     ; leading zeros are matched
        ; Matches ANSI Codes: True Color (24-bit)
        $str = StringRegExpReplace($str,"�\[(38|48);2;([0-1]?\d?\d|(0|2)[0-5][0-5]);([0-1]?\d?\d|(0|2)[0-5][0-5]);([0-1]?\d?\d|(0|2)[0-5][0-5])m","")
    EndIf
EndFunc

Func __internal_addSocket($socket, $echoStr = "")
    ReDim $__socket[UBound($__socket) + 1]
    ReDim $__echoStr[UBound($__echoStr) + 1]
    $__socket[UBound($__socket) - 1] = $socket
    $__echoStr[UBound($__echoStr) - 1] = $echoStr
    Return UBound($__socket) - 1
EndFunc

Func __internal_removeSocket($socket)
    Local $idx = _ArraySearch($__socket, $socket)
    If $idx >= 0 Then
        If _ArrayDelete($__socket, $idx) >= 0 Then
            _ArrayDelete($__echoStr, $idx)
            Return True
        EndIf
    EndIf
    Return False
EndFunc

Func __internal_isSocket($socket)
    If _ArraySearch($__socket, $socket) >= 0 Then Return True
    Return False
EndFunc

Func __internal_getSocketIdx($socket)
    Return _ArraySearch($__socket, $socket)
EndFunc

Func __internal_closeSocketAndReturn($socket, $retValue)
    _SSHCloseSocket($socket)
    Return $retValue
EndFunc

Func __internal_closeAllSockets()
    Local $i
    For $i=0 To UBound($__socket) -1
        StdioClose($__socket[$i])
        ProcessClose($__socket[$i])
    Next
    ReDim $__socket[1]
    ReDim $__echoStr[1]
    _ArrayPop($__socket)
    _ArrayPop($__echoStr)
EndFunc

Func hostToIP($host)
    If StringIsSpace($host) Then
        Return ""
    ElseIf isValidIPv4($host) Or isValidIPv6($host) Then
        Return $host
    Else
        TCPStartup()
        Local $IP = TCPNameToIP($host)
        If @error Then ConsoleWriteError("SSH UDF: Could not resolve IP/Domain: " & $host & @LF)
        TCPShutdown()
        Return $IP
    EndIf
EndFunc

; Source: jchd https://www.autoitscript.com/forum/topic/163160-regular-expression-to-confirm-an-ipv4-address/?do=findComment&comment=1188014
Func isValidIPv4($ipv4)
    Return StringRegExp($ipv4, "^(?:[1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])){3}$", 0)
EndFunc

; Source: James https://www.autoitscript.com/forum/topic/144168-validate-ipv6-address/
Func isValidIPv6($ipv6)
    Return StringRegExp($ipv6, "^(([\da-f]{0,4}:{0,2}){1,8})$", 0)
EndFunc
; ===============================================================================================================================

 

Download

The download includes ssh.au3 (UDF), plink.exe (necessary), vintage terminal and code examples:

Spoiler

SSH UDF.zip (version 1.0)

Version 1.0.1

- fixed rare _SSHConnect bug where "ssh-host-key prompt" was not answered

SSH UDF 1.0.1.zip

 

Edited by p4sCh
added version 1.0.1
Link to post
Share on other sites
On 9/30/2021 at 3:07 PM, gcriaco said:

It works fine. Many thanks and compliments!

Thank you 😊

On 10/2/2021 at 9:45 AM, Chimp said:

Hi @p4sCh, good job!

thanks for sharing :)

Thanks for providing the vintage terminal. It really took the examples to another level!

You might want to consider to update the original vintage terminal thread with the modified version from this UDF.

It does have some bug fixes, new features and should have noticeable better performance.

On 10/1/2021 at 5:11 AM, JiBe said:

this UDF SSH - AutoIt Example Scripts - AutoIt Forums (autoitscript.com) support multiple simultaneous connections.

 

I know :). And it supports many plink settings which can not be adjusted in this UDF. I had different reasons to make another SSH UDF.

Link to post
Share on other sites

Hi,

Thanks for sharing but ...

I wonder why you use an external app to create an SSH cleient.

While W10 has this out of the box using the SSH command

usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]
           [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
           [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]
           [-i identity_file] [-J [user@]host[:port]] [-L address]
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
           [-Q query_option] [-R address] [-S ctl_path] [-W host:port]
           [-w local_tun[:remote_tun]] destination [command]

Which also integrates in Windows Terminal 

https://docs.microsoft.com/en-us/windows/terminal/tutorials/ssh

 

Link to post
Share on other sites
13 hours ago, ptrex said:

Hi,

Thanks for sharing but ...

I wonder why you use an external app to create an SSH cleient.

While W10 has this out of the box using the SSH command

Just because I didn't knew about this feature 😅
Very interesting, thank you. At the moment I have little time but I consider changing the UDF to use this method primarily or as alternative to plink. Would be nice to drop the additional executable.

Edit: Looks like this command is only available from Win10 (Version 1709+). So on an older OS you would have to use plink instead.

Edited by p4sCh
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Kanashius
      This UDF can be used to draw at the Desktop Wallpaper (Windows 8+) with GDI+ without using files.
      The example runs with 60-120fps at my machine, so the speed is acceptable.
      When the script exits, the original wallpaper is restored.
      Have fun :).
      PS: The UDF uses the window between the background and the icons, so it could be used to add an own child window at the desktop. Just look at the begin of __Wallpaper_Startup ($hWorkerW).
       
      Example:
       
      WallpaperUDF.au3 WallpaperUDF Example.au3
    • By sandgre
      Wondering if you can help a teacher out.  For the last few years of remote, hybrid, and in-person teaching I've been using an AutoHotKey script that creates an inking toolbar to be used during a PowerPoint presentation.  (https://www.autohotkey.com/boards/viewtopic.php?t=79163) The annotation tools available through Ppt are woefully inadequate, and, on my machine at least, jump around all over the place - sometimes on the bottom, sometimes on the left, sometimes on the right.  It's a scavenger hunt every single time.  The inking tool is always available, and recent updates allowed for the addition of lots more colors, which I used for modeling phenomena with students. One tragic day I arrived to school to find that my district IT dep't took away access to AutoHotKey.  No more tool bar. 
      I've been trying to resurrect some programming skills from a past life. I'm working through various tutorials and help files, with the goal of recreating the toolbar in AutoIt.  I'm really needing some help to move things along though.  I've been going through your PowerPoint UDF and all of your functions seem to just relate to editing the slides. Can you point me in the direction of how to create a toolbar that accesses the annotation tools that are available during the slideshow itself? 
       
    • By kurtykurtyboy
      GuiFlatButton is a UDF to easily create regular buttons with different colors for background, foreground, border, hover, focus, etc..
      This started as an effort to change the background color of a button and eventually grew into a full UDF.
      If you've looked around forums for changing button background colors, you have probably noticed that each proposed workaround has its own set of issues/side-effects. The answers usually circle back to 'use ownerdrawn buttons' and 'not worth it'. Well, now it is possible for anyone to easily create ownerdrawn buttons - totally worth it!
      Some issues with other workarounds such as drawing with GDI+ or using a colored label as a 'button':
      Not 'real' buttons so you lose built-in functionality that windows gives to buttons Messy / inefficient code in the main while loop to check for mouse position Slow to respond to click, paint, etc... Having to deal with GUIRegisterMsg messages Not straight-forward to implement GuiFlatButton is not a workaround; it is a technique to respond to Windows' built-in owner-drawn button events.
      With minimal effort, we can now create true simple colored buttons.
      The idea is to create an owner-drawn button using GUICtrlCreateButton then subclass the GUI and controls to handle the button-specific events to paint it however we want.
      This UDF magically does all of this for us! No need to worry about event handling or main while loop logic.
       
      How to use
      It couldn't be any easier! Simply create a new button using the familiar syntax. This creates an ownerdrawn button with default colors.
      $mybutton1 = GuiFlatButton_Create("Button 1", 78, 20, 120, 40) If you want to change the background and text colors:
      GuiFlatButton_SetBkColor(-1, 0x5555FF) GuiFlatButton_SetColor(-1, 0xFFFFFF) Advanced Usage
      Set background/text/border all at once
      GuiFlatButton_SetColors(-1, 0x0000FF, 0xFFFFFF, 0x9999FF) Set ALL colors for ALL button states! (normal, focus, hover, selected)
      Local $aColorsEx = [0x0000FF, 0xFFFFFF, -2, 0x4444FF, 0xFFFFFF, 0xAAAAFF, 0x6666FF, 0xFFFFFF, 0xCCCCFF, 0x0000EE, 0xFFFFFF, 0x7777EE] GuiFlatButton_SetColorsEx(-1, $aColorsEx) Set default colors to apply to any future buttons
      ;set colors GuiFlatButton_SetDefaultColors(0x0000FF, 0xFFFFFF, 0x9999FF) ;create buttons $mybutton1 = GuiFlatButton_Create("Button 1", 12, 20, 120, 40) $mybutton2 = GuiFlatButton_Create("Button 2", 143, 20, 120, 40) Set ALL color defaults
      ;set colors Local $aColorsEx = [0x0000FF, 0xFFFFFF, -2, 0x4444FF, 0xFFFFFF, 0xAAAAFF, 0x6666FF, 0xFFFFFF, 0xCCCCFF, 0x0000EE, 0xFFFFFF, 0x7777EE] GuiFlatButton_SetDefaultColorsEx($aColorsEx) ;create buttons $mybutton1 = GuiFlatButton_Create("Button 1", 12, 20, 120, 40) $mybutton2 = GuiFlatButton_Create("Button 2", 143, 20, 120, 40)  
      Available Functions
       
      Simple Example

      #include <GUIConstantsEx.au3> #include <MsgBoxConstants.au3> #include "GuiFlatButton.au3" Example() ;GUI with one button Func Example() Local $hGUI, $mybutton1 $hGUI = GUICreate("GuiFlatButton Ex0", 275, 120) GUISetBkColor(0x333333) $idLabel = GUICtrlCreateLabel("Click the button", 10, 100, 150, 30) GUICtrlSetColor(-1, 0xFFFFFF) ;create new button then set the background and foreground colors $mybutton1 = GuiFlatButton_Create("Button 1", 78, 20, 120, 40) GuiFlatButton_SetBkColor(-1, 0x5555FF) GuiFlatButton_SetColor(-1, 0xFFFFFF) GUISetState(@SW_SHOW, $hGUI) Local $i = 0 Local $iMsg While 1 $iMsg = GUIGetMsg() Switch $iMsg Case $GUI_EVENT_CLOSE ExitLoop Case $mybutton1 $i += 1 GUICtrlSetData($idLabel, $i) ConsoleWrite($i & @CRLF) EndSwitch Sleep(10) WEnd GUIDelete() EndFunc ;==>Example
      Menu/Toolbar Example

      #include <GUIConstantsEx.au3> #include <MsgBoxConstants.au3> #include "GuiFlatButton.au3" Example() ;Example GUI with toolbar Func Example() Local $hGUI, $idLabel, $aButtons, $iTbSize $hGUI = GUICreate("GuiFlatButton Ex2", 300, 200) GUISetBkColor(0x444444) $idLabel = GUICtrlCreateLabel("Click a button", 10, 180, 150, 30) GUICtrlSetColor(-1, 0xFFFFFF) $aButtons = createToolbar() $iTbSize = UBound($aButtons) GUISetState(@SW_SHOW, $hGUI) Local $i = 0 Local $iMsg While 1 $iMsg = GUIGetMsg() Switch $iMsg Case $GUI_EVENT_CLOSE ExitLoop Case $aButtons[0] To $aButtons[$iTbSize - 1] ConsoleWrite("1") GUICtrlSetData($idLabel, GuiFlatButton_Read($iMsg)) EndSwitch Sleep(10) WEnd GUIDelete() EndFunc ;==>Example Func createToolbar() Local $aButtons[6] Local $bkColor = 0x777777 Local $textColor = 0xFFFFFF Local $borderColor = 0x999999 Local $aBtnClrs[12] = [0x777777, 0xFFFFFF, $GUI_BKCOLOR_TRANSPARENT, 0x888888, 0xFFFFFF, $GUI_BKCOLOR_TRANSPARENT, 0x999999, 0xFFFFFF, $GUI_BKCOLOR_TRANSPARENT, 0x666666, 0xFFFFFF, $GUI_BKCOLOR_TRANSPARENT] For $i = 0 To UBound($aButtons) - 1 $aButtons[$i] = GuiFlatButton_Create("B" & $i, $i * 50, 0, 50, 17) GuiFlatButton_SetColorsEx($aButtons[$i], $aBtnClrs) Next Return $aButtons EndFunc ;==>createToolbar  
      Icon Example
      You can even easily add icons to your buttons -- just create a new button and send it an icon!

      #include <GDIPlus.au3> #include "GuiFlatButton.au3" Example() ;buttons with Icon images Func Example() ;get images for demonstration _GDIPlus_Startup() ;initialize GDI+ Local $hIcon = _WinAPI_ShellExtractIcon(@SystemDir & '\shell32.dll', 258, 24, 24) ;extract the 'Save' icon Local $hBitmap = _GDIPlus_BitmapCreateFromHICON($hIcon) ;Create Bitmap from Icon (for demonstration) Local $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) ;Create HBitmap from Bitmap _GDIPlus_BitmapDispose($hBitmap) ;dispose the bitmap _GDIPlus_Shutdown() ;done with GDI+ Local $hGUI = GUICreate("GuiFlatButton Ex5", 255, 400) GUISetBkColor(0xEEEEEE) ;set default colors of future buttons Local $aColorsEx = _ [0xE2E5E8, 0X000000, 0x888888, _ ; normal : Background, Text, Border 0xE2E5E8, 0X000000, 0x333333, _ ; focus : Background, Text, Border 0xE8E8E8, 0X000000, 0x666666, _ ; hover : Background, Text, Border 0xDDDDDD, 0X000000, 0xAAAAAA] ; selected : Background, Text, Border GuiFlatButton_SetDefaultColorsEx($aColorsEx) ;normal button with icon $label1 = GUICtrlCreateLabel( "$BS_TOOLBUTTON -->", 5, 10) GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT) Local $mybutton1 = GuiFlatButton_Create("Save", 130, 5, 50, 48, $BS_TOOLBUTTON) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybutton1), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align top Local $mybuttonT = GuiFlatButton_Create("Top", 5, 65, 120, 55, $BS_TOP) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonT), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align top-left Local $mybuttonTL = GuiFlatButton_Create("Top-Left", 5, 125, 120, 55, BITOR($BS_TOP, $BS_LEFT)) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonTL), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align top-right Local $mybuttonTR = GuiFlatButton_Create("Top-Right", 5, 185, 120, 55, BITOR($BS_TOP, $BS_RIGHT)) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonTR), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align left Local $mybuttonL = GuiFlatButton_Create("Left", 5, 245, 120, 55, $BS_LEFT) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonL), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align bottom Local $mybuttonB = GuiFlatButton_Create("Bottom", 130, 65, 120, 55, $BS_BOTTOM) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonB), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align bottom-left Local $mybuttonBL = GuiFlatButton_Create("Bottom-Left", 130, 125, 120, 55, BITOR($BS_BOTTOM, $BS_LEFT)) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonBL), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align bottom-right Local $mybuttonBR = GuiFlatButton_Create("Bottom-Right", 130, 185, 120, 55, BITOR($BS_BOTTOM, $BS_RIGHT)) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonBR), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) ;align right Local $mybuttonR = GuiFlatButton_Create("Right", 130, 245, 120, 55, $BS_RIGHT) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonR), $BM_SETIMAGE, $IMAGE_ICON, $hIcon)) GuiFlatButton_SetState($mybuttonR, $GUI_DISABLE ) ;disabled Local $mybuttonDisable = GuiFlatButton_Create("Disabled", 130, 310, 120, 55, $BS_TOOLBUTTON) _WinAPI_DeleteObject(_SendMessage(GUICtrlGetHandle($mybuttonDisable), $BM_SETIMAGE, $IMAGE_BITMAP, $hHBitmap)) GuiFlatButton_SetState($mybuttonDisable, $GUI_DISABLE ) ;clean up! _WinAPI_DestroyIcon( $hIcon ) _WinAPI_DeleteObject( $hHBitmap ) GUISetState(@SW_SHOW, $hGUI) Local $iMsg While 1 $iMsg = GUIGetMsg() Switch $iMsg Case $GUI_EVENT_CLOSE ExitLoop EndSwitch Sleep(10) WEnd GUIDelete() EndFunc ;==>Example  
      I'm sure there are some use-cases I've forgotten, so feedback is welcome!
       
      Download the latest UDF and several more examples:
      Update 2022-05-25
      GuiFlatButton_20220525.zip
      Fixed issue, buttons disappear when a GUI containing a child window with WS_EX_MDICHILD extended style is moved
      Update 2022-05-24
      Fixed issue releasing subclassing when GUI is deleted but program is not closed
      Fixed occasional white background flicker
      Added function GuiFlatButton_GetPos
      Update 2021-01-02
      Fixed bug, not drawing correctly after deleting GUI with GUIDelete()
      Fixed bug, changing default colors changed all buttons, even previously created buttons
      Made some internal functions more efficient
      Update 2019-04-14
      Fixed bug, not showing pressed down state when clicking rapidly
      Added Icon/Bitmap support!
      Added function GuiFlatButton_SetPos to change the position and/or size of a button
      Update 2019-02-09
      Added 2 new functions to set the button colors globally for all future buttons.
      GuiFlatButton_SetDefaultColors 
      GuiFlatButton_SetDefaultColorsEx

      Credits to:
      Melba23 (UDF template)
      LarsJ (general subclassing code)
      4ggr35510n (TrackMouseEvent example)
      binhnx (disable dragging with $WS_EX_CONTROLPARENT)
      GUIRegisterMsg in AutoIt Help (owner-draw button example)
      funkey (_WinAPI_DrawState example)
      GuiFlatButton_20190414.zip GuiFlatButton20210102.zip
      GuiFlatButton_20220524.zip
    • By Hermes
      Hi, I am struggling in setting the value of a textarea based on the value of clipboard (that contains a long web page source codes). If I use _WD_SetElementValue, it freezes after some time, or appears to be pressing tab and goes out of focus. I can also use send keys but i need the script to run in the background.
      Here is the full script:
      #Include "Chrome.au3" #Include "wd_core.au3" #Include "wd_helper.au3" #Include "WinHttp.au3" #include <MsgBoxConstants.au3> #include <WinAPIFiles.au3> #include <Array.au3> #include <AutoItConstants.au3> #include <WinAPIFiles.au3> #include <GDIPlus.au3> #include <Excel.au3> Local $sDesiredCapabilities, $sSession SetupChrome() _WD_Startup() $sSession = _WD_CreateSession($sDesiredCapabilities) _WD_LoadWait($sSession) _WD_Navigate($sSession, "http://demo.borland.com/testsite/stadyn_largepagewithimages.html") _WD_LoadWait($sSession) Global $sSource = _WD_GetSource($sSession) Local $Paste = ClipPut($sSource) Local $sData = ClipGet() Local $aArray = 0, _ $iOffset = 1 While 1 $aArray = StringRegExp($sData, '(?s)<p>.*</p>', $STR_REGEXPARRAYMATCH, $iOffset) If @error Then ExitLoop $iOffset = @extended For $i = 0 To UBound($aArray) - 1 Local $Paste = ClipPut($aArray[$i]) Local $sRegExData = ClipGet() ;MsgBox(0, "", "$sRegExData = " & $sRegExData) Next WEnd _WD_Navigate($sSession, "https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_textarea_placeholder") _WD_WaitElement($sSession, $_WD_LOCATOR_ByCSSSelector, "iframe#iframeResult") Local $sElement1 = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "iframe#iframeResult") _WD_FrameEnter($sSession, $sElement1) _WD_WaitElement($sSession, $_WD_LOCATOR_ByXPath, "//html/body/textarea") $textarea = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//html/body/textarea") _WD_ElementAction($sSession, $textarea, 'click') ;WD SetElementValue(SsSession, Stextarea, $sRegExData) <-- I can do this but the focus goes out, or the browser freezes _WD_FrameLeave($sSession) sleep(2000) Send("^v") _WD_LoadWait($sSession) _WD_Shutdown() Func SetupChrome() _WD_Option('Driver', 'chromedriver.exe') _WD_Option('Port', 9515) _WD_Option('DriverParams', '--log-path="' & @ScriptDir & '\chrome.log"') $sDesiredCapabilities = '{"capabilities": {"alwaysMatch": {"goog:chromeOptions": {"w3c": true, "args":["start-maximized","disable-infobars"]}}}}' EndFunc ;==>SetupChrome Can someone help me please, or re-direct me to the right path? TIA!
    • By Hermes
      Hi, I am trying to select elements in print page (dialog window) for a specific web page. But when trying to do so, it looks like it does not allow me to do it.
      So far, this is what I have:
      _WD_WaitElement($sSession, $_WD_LOCATOR_ByCSSSelector, "body") Local $mainpagebody = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "body") _WD_HighlightElement($sSession, $mainpagebody, 2) _WD_WaitElement($sSession, $_WD_LOCATOR_ByCSSSelector, "img.print-button") Local $printbutton = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "img.print-button") _WD_ElementAction($sSession, $printbutton, 'click') _WD_LoadWait($sSession) Sleep(3000) _WD_WaitElement($sSession, $_WD_LOCATOR_ByCSSSelector, "body") Local $printpagebody = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "body") _WD_HighlightElement($sSession, $printpagebody, 2) The script above highlights the main web page "body" element, then clicks a tag to print a specific part of the page, then it will open a print page window where i am trying to highlight the body of that print page - but it looks like it is dropping from the session because it opens up another chrome page chrome://print.
       
      Below is the output log:
      __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"body"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}} _WD_WaitElement ==> Success __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"body"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}} __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/execute/sync; $sData={"script":"arguments[0].style='background: #FFFF66; border-radius: 5px; padding-left: 3px;'; return true;", "args":[{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}]} __WD_Post: StatusCode=200; ResponseText={"value":true}... _WD_ExecuteScript: {"value":true}... __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"img.print-link"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"eb9e4673-4dec-4d4c-be6a-b7967743394b"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"eb9e4673-4dec-4d4c-be6a-b7967743394b"}} _WD_WaitElement ==> Success __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"img.print-link"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"eb9e4673-4dec-4d4c-be6a-b7967743394b"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"eb9e4673-4dec-4d4c-be6a-b7967743394b"}} __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element/eb9e4673-4dec-4d4c-be6a-b7967743394b/click; $sData={"id":"eb9e4673-4dec-4d4c-be6a-b7967743394b"} __WD_Post: StatusCode=200; ResponseText={"value":null}... _WD_ElementAction: {"value":null}... __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/execute/sync; $sData={"script":"arguments[0].style='background: #FFFF66; border-radius: 5px; padding-left: 3px;'; return true;", "args":[{"element-6066-11e4-a52e-4f735466cecf":"eb9e4673-4dec-4d4c-be6a-b7967743394b"}]} __WD_Post: StatusCode=200; ResponseText={"value":true}... _WD_ExecuteScript: {"value":true}... __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/execute/sync; $sData={"script":"return document.readyState", "args":[]} __WD_Post: StatusCode=500; ResponseText={"value":{"error":"script timeout","message":"script timeout\n (Session info: chrome=92.0.4515.107)... __WD_Post ==> Webdriver Exception: {"value":{"error":"script timeout","message":"script timeout\n (Session info: chrome=92.0.4515.107)","stacktrace":"Backtrace:\n\tOrdinal0 [0x00C63733+2504499]\n\tOrdinal0 [0x00BFC401+2081793]\n\tOrdinal0 [0x00B024F0+1058032]\n\tOrdinal0 [0x00B55685+1398405]\n\tOrdinal0 [0x00B45E83+1334915]\n\tOrdinal0 [0x00B54CDB+1395931]\n\tOrdinal0 [0x00B45D4B+1334603]\n\tOrdinal0 [0x00B222B4+1188532]\n\tOrdinal0 [0x00B23149+1192265]\n\tGetHandleVerifier [0x00DDFB8C+1512252]\n\tGetHandleVerifier [0x00E8B0DF+2214031]\n\tGetHandleVerifier [0x00CE4BC3+484211]\n\tGetHandleVerifier [0x00CE3E69+480793]\n\tOrdinal0 [0x00C0218D+2105741]\n\tOrdinal0 [0x00C066E8+2123496]\n\tOrdinal0 [0x00C06827+2123815]\n\tOrdinal0 [0x00C0FB73+2161523]\n\tBaseThreadInitThunk [0x75EB62C4+36]\n\tRtlSubscribeWnfStateChangeNotification [0x77C11B69+1081]\n\tRtlSubscribeWnfStateChangeNotification [0x77C11B34+1028]\n"}} _WD_ExecuteScript: {"value":{"error":"script timeout","message":"script timeout\n (Session info: chrome=92.0.4515.107)... _WD_ExecuteScript ==> Webdriver Exception: HTTP status = 500 _WD_LoadWait ==> Webdriver Exception __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"body"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}} _WD_WaitElement ==> Success __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/element; $sData={"using":"css selector","value":"body"} __WD_Post: StatusCode=200; ResponseText={"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}}... _WD_FindElement: {"value":{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}} __WD_Post: URL=HTTP://127.0.0.1:9515/session/cd08704233a965ccbaf9292e8692c3e4/execute/sync; $sData={"script":"arguments[0].style='background: #FFFF66; border-radius: 5px; padding-left: 3px;'; return true;", "args":[{"element-6066-11e4-a52e-4f735466cecf":"91a394f0-004c-480d-aedf-52e2b30233c6"}]} __WD_Post: StatusCode=200; ResponseText={"value":true}... _WD_ExecuteScript: {"value":true}...  
      Is it even possible to select elements in print page?

×
×
  • Create New...