Jump to content

Command Prompt automation


PhilHibbs
 Share

Recommended Posts

Here is my "Cmd.au3" include file, maybe this could become an official UDF library at some point. It has three functions - one to get the window handle from a process ID (GetProcessWin doesn't work for command prompts), the other pair both copy and searche through a Command Prompt's text buffer.

I'm not entirely happy about the $aText mechanism in the _CmdWaitList function, which takes a list of values to wait for and returns the index of the first one found. I don't like having to construct the array of strings. Also I'm not sure about setting SendKeepActive() inside a UDF. Maybe I should leave that to be the caller's responsibility.

#include-Once

; #INDEX# =======================================================================================================================
; Title .........: Cmd
; AutoIt Version : 3.3.6++
; Language ......: English
; Description ...: Functions for manipulating command prompt windows.
; Author(s) .....: PhilHibbs
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
;_CmdGetWindow
;_CmdWaitFor
;_CmdWaitList
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdGetWindow
; Description ...: Locates the window handle for a given Command Prompt process.
; Syntax.........: _CmdGetWindow($pCmd)
; Parameters ....: $pCmd  - Process id of the Command Prommpt application
; Return values .: Success - Window handle
;                  Failure - -1, sets @error
;                  |1 - Process $pCmd not found
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdGetWindow( $pCmd )
    Local $WinList, $i
    $WinList = WinList()
    For $i = 1 to $WinList[0][0]
        If $WinList[$i][0] <> "" And WinGetProcess( $WinList[$i][1] ) = $pCmd Then
            Return $WinList[$i][1]
        EndIf
    Next
    Return SetError(1, 0, -1)
EndFunc   ;==>_CmdGetWindow

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitFor
; Description ...: Waits for a particular string to be found in a Command Prompt window
; Syntax.........: _CmdWaitFor($hWin, $text, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $text    - String to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - True
;                  Failure - False
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix, e.g.
;                     Send( "echo :cmd123:;ls -l{Enter}" )
;                     _CmdWaitFor( $hTelnet, $User & "@", -1, ":cmd123:" )
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitFor( $hWin, $text, $timeout = Default, $period = Default, $prefix = "" )
    Local $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    SendKeepActive( $hWin )

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        Send( "! es{Enter}" )
        $con = ClipGet()
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        If StringInStr( $con, $text ) > 0 Then
            Return True
        EndIf
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    Return False
EndFunc   ;==>_CmdWaitFor

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitList
; Description ...: Waits for one of a set of strings to be found in a Command Prompt window
; Syntax.........: _CmdWaitList($hWin, $aText, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $aText   - Array of strings to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - Element number found
;                  Failure - -1, sets @error
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitList( $hWin, ByRef $aText, $timeout = Default, $period = Default, $prefix = "" )
    Local $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    SendKeepActive( $hWin )

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        Send( "! es{Enter}" )
        $con = ClipGet()
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        For $i = 0 To UBound( $aText ) - 1
            If StringInStr( $con, $aText[$i] ) > 0 Then
                Return $i
            EndIf
        Next
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    If Not(WinExists( $hWin )) Then Return SetError(2, 0, -1)
    Return SetError(1, 0, -1)
EndFunc   ;==>_CmdWaitList

Demonstration:

#include "Cmd.au3"

$pCmd = Run( "cmd.exe" )
Sleep( 1000 )
$hCmd = _CmdGetWindow( $pCmd )
SendKeepActive( $hCmd )
If _CmdWaitFor( $hCmd, "Microsoft Corp", 1000 ) Then
    ; Clipboard will contain the contents of the cmd buffer
    $cmdtext = ClipGet()
    ; Pick up the last line, this will be the prompt string for future use
    $cmdprompt = StringMid( $cmdtext, StringInStr( $cmdtext, @LF, False, -1 ) + 1)
    Send( "dir{Enter}" )
    If _CmdWaitFor( $hCmd, "bytes free" ) Then
        $cmdtext = ClipGet()
        $bytesfree = StringStripWS(StringMid( $cmdtext, StringInStr( $cmdtext, "Dir(s)" ) + 7, 16 ), 3)
        MsgBox( 0, "DIR", "You have " & $bytesfree & " bytes free" )
    EndIf
EndIf
Edited by PhilHibbs
Link to comment
Share on other sites

What happened to STDIO?

Nothing, it's still there. This is just another way of doing something similar, for instance if you have a Command Prompt that is launched separately from AutoIt. Or, more specifically in my case, when you want to leave the Command Prompt application running for the user to carry on interacting with at the end of the AutoIt script.

*Edit:* Also this will work for text-gui applications like Edit, Xtree, or old applications with text menus.

Edited by PhilHibbs
Link to comment
Share on other sites

Nothing, it's still there. This is just another way of doing something similar, for instance if you have a Command Prompt that is launched separately from AutoIt. Or, more specifically in my case, when you want to leave the Command Prompt application running for the user to carry on interacting with at the end of the AutoIt script.

Valid point.

Cheers,

Brett

Link to comment
Share on other sites

Check out these functions, though I'm not sure if they will work for external applications.

http://msdn.microsoft.com/en-us/library/ms682073(VS.85).aspx

Yes, I saw those after finding the _WinAPI_WriteConsole function. ReadConsoleOutput looks interesting, but I have no idea whether it's even possible to get the console handle.
Link to comment
Share on other sites

New test script, using the && command separator in order to demonstrate the $prefix mechanism:

#include "Cmd.au3"

$pCmd = Run( "cmd.exe" )
$hCmd = _CmdGetWindow( $pCmd )
SendKeepActive( $hCmd )
If _CmdWaitFor( $hCmd, "Microsoft Corp", 1000 ) Then
    ; Clipboard will contain the contents of the cmd buffer
    $cmdtext = ClipGet()
    ; Pick up the last line, this will be the prompt string for future use
    $cmdprompt = StringMid( $cmdtext, StringInStr( $cmdtext, @LF, False, -1 ) + 1)
    Send( "echo :cmd001:&&dir{Enter}" )
    If _CmdWaitFor( $hCmd, $cmdprompt, Default, Default, ":cmd001:" ) Then
        $cmdtext = ClipGet()
        $bytesfree = StringStripWS(StringMid( $cmdtext, StringInStr( $cmdtext, "Dir(s)" ) + 7, 16 ), 3)
        MsgBox( 0, "DIR", "You have " & $bytesfree & " bytes free" )
    EndIf
EndIf

*Edit:* Of course, that $cmdprompt mechanism will fail if you have some kind of funky prompt like "$t $p$g".

Edited by PhilHibbs
Link to comment
Share on other sites

  • 4 weeks later...

Totally new version of Cmd.Au3, this uses proper API calls rather than stuffing the Select All keys and reading the clipboard. The _CmdAttachConsole returns a "handle" which needs to be passed to the other _Cmd functions, the _CmdWaitFor and _CmdWaitList also still need the window handle so that they can check if the prompt has disappeared (although the script seems to die if the window is closed).

The script using this library must be run outside of SciTE, but does not need to be compiled.

#include-Once

#include <WinAPI.au3>
#Include <Misc.au3>

; #INDEX# =======================================================================================================================
; Title .........: Cmd
; AutoIt Version : 3.3.6++
; Language ......: English
; Description ...: Functions for manipulating command prompt windows.
; Author(s) .....: PhilHibbs
;                  Valik
; ===============================================================================================================================

; #CONSTANTS# ===================================================================================================================
Global Const $STD_INPUT_HANDLE = -10
Global Const $STD_OUTPUT_HANDLE = -11
Global Const $STD_ERROR_HANDLE = -12
Global Const $_CONSOLE_SCREEN_BUFFER_INFO = _
             "short dwSizeX;" & _
             "short dwSizeY;" & _
             "short dwCursorPositionX;" & _
             "short dwCursorPositionY;" & _
             "short wAttributes;" & _
             "short Left;" & _
             "short Top;" & _
             "short Right;" & _
             "short Bottom;" & _
             "short dwMaximumWindowSizeX;" & _
             "short dwMaximumWindowSizeY"
Global Const $_COORD = _
             "short X;" & _
             "short Y"
Global Const $_CHAR_INFO = _
             "wchar UnicodeChar;" & _
             "short Attributes"
Global Const $_SMALL_RECT = _
             "short Left;" & _
             "short Top;" & _
             "short Right;" & _
             "short Bottom"
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
;_CmdGetWindow
;_CmdAttachConsole
;_CmdWaitFor
;_CmdWaitList
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdGetWindow
; Description ...: Locates the window handle for a given Command Prompt process.
; Syntax.........: _CmdGetWindow($pCmd)
; Parameters ....: $pCmd  - Process id of the Command Prommpt application
; Return values .: Success - Window handle
;                  Failure - -1, sets @error
;                  |1 - Process $pCmd not found
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdGetWindow( $pCmd )
    Local $WinList, $i
    While True
        $WinList = WinList()
        For $i = 1 to $WinList[0][0]
            If $WinList[$i][0] <> "" And WinGetProcess( $WinList[$i][1] ) = $pCmd Then
                Return $WinList[$i][1]
            EndIf
        Next
    WEnd
EndFunc   ;==>_CmdGetWindow

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdAttachConsole
; Description ...: Locates the console handle for a given Command Prompt process.
; Syntax.........: _CmdAttachConsole($pCmd)
; Parameters ....: $pCmd  - Process id of the Command Prommpt application
; Return values .: Success - Window handle structure
;                  Failure - -1, sets @error
;                  |1 - Unable to attach console
;                  |2 - Unable to create file handle
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: Thanks to Valik for original Screen_Scrape.Au3
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdAttachConsole($nPid)
    ; Try to attach to the console of the PID.
    Local $aRet = DllCall("kernel32.dll", "int", "AttachConsole", "dword", $nPid)
    If @error Then Return SetError(@error, @extended, False)
    If $aRet[0] Then
        ; The user should treat this as an opaque handle, but internally it contains a handle
        ; and some structures.
        Local $vHandle[2]
        $vHandle[0] = _CmdGetStdHandle($STD_OUTPUT_HANDLE)  ; STDOUT Handle
        $vHandle[1] = DllStructCreate($_CONSOLE_SCREEN_BUFFER_INFO) ; Screen Buffer structure

        ; Return the handle on success.
        Return $vHandle
    EndIf

    ; Return 0 on failure.
    Return 0
EndFunc ; _CmdAttachConsole()

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdGetText
; Description ...: Gets all the text in a Command Prompt window
; Syntax.........: _CmdGetText($vHandle, $bAll )
; Parameters ....: $hConsole - Console handle
;                  $bAll     - True = all text up to bottom of visible area, False = just visible area
; Return values .: Success - Text contents of window
;                  Failure - "", sets @error
;                  |1 - Invalid handle structure
;                  |2 - Screen Buffer API call failed
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: Thanks to Valik for original Screen_Scrape.Au3
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdGetText(ByRef $vHandle, $bAll = False)
    ; Basic sanity check to validate the handle.
    If UBound($vHandle) = 2 Then
        ; Create some variables for convenience.
        Local Const $hStdOut = $vHandle[0]
        Local Const $pConsoleScreenBufferInfo = $vHandle[1]

        ; Try to get the screen buffer information.
        If _GetConsoleScreenBufferInfo($hStdOut, $pConsoleScreenBufferInfo) Then
            ; Load the SMALL_RECT with the projected text position.
            Local $iLeft = DllStructGetData( $pConsoleScreenBufferInfo, "Left")
            Local $iRight = DllStructGetData( $pConsoleScreenBufferInfo, "Right")
            Local $iTop = DllStructGetData( $pConsoleScreenBufferInfo, "Top")
            Local $iBottom = DllStructGetData( $pConsoleScreenBufferInfo, "Bottom")

            Local $iWidth = $iRight - $iLeft + 1

            ; Set up the coordinate structures.
            Local $coordBufferCoord = _CmdWinAPI_MakeDWord(0, 0)
            Local $coordBufferSize = _CmdWinAPI_MakeDWord($iWidth, 1)
;MsgBox( 0, "coords", "L=" & $iLeft & "T=" & $iTop & "R=" & $iRight & "B=" & $iBottom )

            Local $pBuffer = DllStructCreate("dword[" & $iWidth & "]")

            Local Const $pRect = DllStructCreate($_SMALL_RECT)
            ; This variable holds the output string.
            Local $sText = ""
            For $j = _IIf( $bAll, 0, $iTop ) To $iBottom
                Local $sLine = ""
                DllStructSetData( $pRect, "Left", $iLeft )
                DllStructSetData( $pRect, "Right", $iRight )
                DllStructSetData( $pRect, "Top", $j )
                DllStructSetData( $pRect, "Bottom", $j )

                ; Read the console output.
                If _CmdReadConsoleOutput($hStdOut, $pBuffer, $coordBufferSize, $coordBufferCoord, $pRect) Then

                    Local $pPtr = DllStructGetPtr($pBuffer)

                    For $i = 0 To $iWidth - 1
                        ; We offset the buffer each iteration by 4 bytes because that is the size of the CHAR_INFO
                        ; structure.  We do this so we can read each individual character.
                        Local $pCharInfo = DllStructCreate($_CHAR_INFO, $pPtr)
                        $pPtr += 4

                        ; Append the character.
                        $sLine &= DllStructGetData($pCharInfo, "UnicodeChar")
                    Next
                    $sText &= StringStripWS( $sLine, 2 ) & @CRLF
                EndIf
            Next
            $sText = StringStripWS( $sText, 2 )

            Return $sText
        EndIf
        Return SetError( 2, 0, "" )
    EndIf

    Return SetError( 1, 0, "" )
EndFunc   ;==>_CmdGetText

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitFor
; Description ...: Waits for a particular string to be found in a Command Prompt window
; Syntax.........: _CmdWaitFor( $hWin, $vHandle, $text, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $text    - String to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - True
;                  Failure - False
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix, e.g.
;                     Send( "echo :cmd123:;ls -l{Enter}" )
;                     _CmdWaitFor( $hTelnet, $wTelnet, $User & "@", -1, ":cmd123:" )
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitFor( $hWin, ByRef $vHandle, $text, $timeout = Default, $period = Default, $prefix = "" )
    Local $bScrInfo, $bScrContent, $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        $con = _CmdGetText( $vHandle )
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        If StringInStr( $con, $text ) > 0 Then
            Return True
        EndIf
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    Return False
EndFunc   ;==>_CmdWaitFor

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitList
; Description ...: Waits for one of a set of strings to be found in a Command Prompt window
; Syntax.........: _CmdWaitList($hWin, $vHandle, $aText, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $aText   - Array of strings to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - Element number found
;                  Failure - -1, sets @error
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitList( $hWin, ByRef $vHandle, ByRef $aText, $timeout = Default, $period = Default, $prefix = "" )
    Local $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    SendKeepActive( $hWin )

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        $con = _CmdGetText( $vHandle )
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        For $i = 0 To UBound( $aText ) - 1
            If StringInStr( $con, $aText[$i] ) > 0 Then
                Return $i
            EndIf
        Next
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    If Not(WinExists( $hWin )) Then Return SetError(2, 0, -1)
    Return SetError(1, 0, -1)
EndFunc   ;==>_CmdWaitList

Func _CmdGetStdHandle($nHandle)
    Local $aRet = DllCall("kernel32.dll", "hwnd", "GetStdHandle", "dword", $nHandle)
    If @error Then Return SetError(@error, @extended, $INVALID_HANDLE_VALUE)
    Return $aRet[0]
EndFunc ; _CmdGetStdHandle()

Func _GetConsoleScreenBufferInfo($hConsoleOutput, $pConsoleScreenBufferInfo)
    Local $aRet = DllCall("kernel32.dll", "int", "GetConsoleScreenBufferInfo", "hwnd", $hConsoleOutput, _
        "ptr", _CmdSafeGetPtr($pConsoleScreenBufferInfo))
    If @error Then Return SetError(@error, @extended, False)
    Return $aRet[0]
EndFunc ; _GetConsoleScreenBufferInfo()

Func _CmdReadConsoleOutput($hConsoleOutput, $pBuffer, $coordBufferSize, $coordBufferCoord, $pRect)
    ; We lie about the types for the COORD structures.  Since they are the size of an int we expect a packed
    ; int.  Otherwise we may crash or just pass garbage.
    Local $aRet = DllCall("kernel32.dll", "int", "ReadConsoleOutputW", "ptr", $hConsoleOutput, _
        "ptr", _CmdSafeGetPtr($pBuffer), "int", $coordBufferSize, "int", $coordBufferCoord, _
        "ptr", _CmdSafeGetPtr($pRect))
    If @error Then SetError(@error, @extended, False)
    Return $aRet[0]
EndFunc ; _CmdReadConsoleOutput()

Func _CmdSafeGetPtr(Const ByRef $ptr)
    Local $_ptr = DllStructGetPtr($ptr)
    If @error Then $_ptr = $ptr
    Return $_ptr
EndFunc ; _CmdSafeGetPtr()

Func _CmdWinAPI_MakeDWord($LoWORD, $HiWORD)
    Local $tDWord = DllStructCreate("dword")
    Local $tWords = DllStructCreate("word;word", DllStructGetPtr($tDWord))
    DllStructSetData($tWords, 1, $LoWORD)
    DllStructSetData($tWords, 2, $HiWORD)
    Return DllStructGetData($tDWord, 1)
EndFunc   ;==>_CmdWinAPI_MakeDWord

Simple demo script:

#include <WinAPI.au3>
#include "Cmd.au3"


$pCmd = Run( "cmd.exe" )
Sleep(1000)
$hCmd = _CmdGetWindow( $pCmd )
$hCon = _CmdAttachConsole( $pCmd )
If _CmdWaitFor( $hCmd, $hCon, "Microsoft Corp", 100 ) Then
    $cmdtext = _CmdGetText( $hCon, True )
;MsgBox(0,"text",$cmdtext)
    ; Pick up the last line, this will be the prompt string for future use
    $cmdprompt = StringMid( $cmdtext, StringInStr( $cmdtext, @LF, False, -1 ) + 1)
    Send( "dir{Enter}" )
    If _CmdWaitFor( $hCmd, $hCon, $cmdprompt, 10000 ) Then
        $cmdtext = _CmdGetText( $hCon, True )
;MsgBox(0,"text",$cmdtext)
        $bytesfree = StringStripWS(StringMid( $cmdtext, StringInStr( $cmdtext, "Dir(s)" ) + 7, 16 ), 3)
        MsgBox( 0, "DIR", "You have " & $bytesfree & " bytes free" )
    EndIf
EndIf
Edited by PhilHibbs
Link to comment
Share on other sites

; #CURRENT# =====================================================================================================================

;_CmdGetWindow

;_CmdAttachConsole

;_CmdWaitFor

;_CmdWaitList

; ===============================================================================================================================

You are missing the get text function from that list :idea:

Link to comment
Share on other sites

You are missing the get text function from that list :idea:

Thanks. Fixed that in my version.

One thing I'm not entirely sure about is how to determine how much of the buffer to fetch. There could be thousands of lines un-used in the buffer, it would be wasteful to fetch it all. At present, it stops reading when it reaches the bottom of the visible window, so you lose sight of stuff if you scroll up. I have a development version that uses the current cursor position instead of the bottom line of the screen, that's ok but I guess the cursor can move under some circumstances. When you do Select All, it only highlights the part of the screen that has been used, so on a fresh command prompt only the first 4 lines are highlighed. Any idea how I replicate that? Keep reading until the $pRect (lpReadRegion) is altered to indicate that nothing was read? Is that what would happen if I try to read past the end of the used area of the buffer?

Edited by PhilHibbs
Link to comment
Share on other sites

I lost the will to live with this, but I just dug it out of one of my old backups and figured that you would would be the guy to make it into a proper UDF. Right now its only functions, and not much works - but its a start, and with a bit of work can be turned into a very useful set of functions.

http://code.google.com/p/m-a-t/downloads/detail?name=Console.au3

Unfortunately I've lost the msdn parser I wrote to do most of the work.

Mat

Edited by Mat
Link to comment
Share on other sites

  • 1 month later...

Here is my "Cmd.au3" include file, maybe this could become an official UDF library at some point. It has three functions - one to get the window handle from a process ID (GetProcessWin doesn't work for command prompts), the other pair both copy and searche through a Command Prompt's text buffer.

I'm not entirely happy about the $aText mechanism in the _CmdWaitList function, which takes a list of values to wait for and returns the index of the first one found. I don't like having to construct the array of strings. Also I'm not sure about setting SendKeepActive() inside a UDF. Maybe I should leave that to be the caller's responsibility.

#include-Once

; #INDEX# =======================================================================================================================
; Title .........: Cmd
; AutoIt Version : 3.3.6++
; Language ......: English
; Description ...: Functions for manipulating command prompt windows.
; Author(s) .....: PhilHibbs
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
;_CmdGetWindow
;_CmdWaitFor
;_CmdWaitList
; ===============================================================================================================================

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdGetWindow
; Description ...: Locates the window handle for a given Command Prompt process.
; Syntax.........: _CmdGetWindow($pCmd)
; Parameters ....: $pCmd  - Process id of the Command Prommpt application
; Return values .: Success - Window handle
;                  Failure - -1, sets @error
;                  |1 - Process $pCmd not found
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdGetWindow( $pCmd )
    Local $WinList, $i
    $WinList = WinList()
    For $i = 1 to $WinList[0][0]
        If $WinList[$i][0] <> "" And WinGetProcess( $WinList[$i][1] ) = $pCmd Then
            Return $WinList[$i][1]
        EndIf
    Next
    Return SetError(1, 0, -1)
EndFunc   ;==>_CmdGetWindow

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitFor
; Description ...: Waits for a particular string to be found in a Command Prompt window
; Syntax.........: _CmdWaitFor($hWin, $text, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $text    - String to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - True
;                  Failure - False
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix, e.g.
;                     Send( "echo :cmd123:;ls -l{Enter}" )
;                     _CmdWaitFor( $hTelnet, $User & "@", -1, ":cmd123:" )
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitFor( $hWin, $text, $timeout = Default, $period = Default, $prefix = "" )
    Local $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    SendKeepActive( $hWin )

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        Send( "! es{Enter}" )
        $con = ClipGet()
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        If StringInStr( $con, $text ) > 0 Then
            Return True
        EndIf
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    Return False
EndFunc   ;==>_CmdWaitFor

; #FUNCTION# ====================================================================================================================
; Name...........: _CmdWaitList
; Description ...: Waits for one of a set of strings to be found in a Command Prompt window
; Syntax.........: _CmdWaitList($hWin, $aText, $timeout = -1, $period, $prefix = "" )
; Parameters ....: $hWin    - Window handle
;                  $aText   - Array of strings to search for
;                  $timeout - How long to wait for in ms, 0 = look once and return, -1 = keep looking for ever
;                  $period  - How long to pause between each content grab
;                  $prefix  - Prefix string, anything prior to this prefix is discarded before searching for $text
; Return values .: Success - Element number found
;                  Failure - -1, sets @error
;                  |1 - Text is not found within the time limit
;                  |2 - Window does not exist
; Author ........: Phil Hibbs (phil at hibbs dot me dot uk)
; Modified.......:
; Remarks .......: The prefix is for searching for something that might occur multiple times, for instance if you issue a command
;                  and want to wait for the User@ prompt, the command itself should be the preifx. If you are issuing the same
;                  command multiple times, you could echo a unique string and use that as the prefix.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _CmdWaitList( $hWin, ByRef $aText, $timeout = Default, $period = Default, $prefix = "" )
    Local $timer, $con, $i

    If $timeout = Default Then $timeout = -1
    If $period = Default Then $period = 1000

    SendKeepActive( $hWin )

    $timer = TimerInit()
    While ($timeout <= 0 Or TimerDiff($timer) < $timeout) And WinExists( $hWin )
        Send( "! es{Enter}" )
        $con = ClipGet()
        If $prefix <> "" Then
            $con = StringMid( $con, StringInStr( $con, $prefix, False, -1 ) + StringLen( $prefix ) )
        EndIf
        For $i = 0 To UBound( $aText ) - 1
            If StringInStr( $con, $aText[$i] ) > 0 Then
                Return $i
            EndIf
        Next
        If $timeout = 0 Then ExitLoop
        Sleep($period)
    WEnd
    If Not(WinExists( $hWin )) Then Return SetError(2, 0, -1)
    Return SetError(1, 0, -1)
EndFunc   ;==>_CmdWaitList

Demonstration:

#include "Cmd.au3"

$pCmd = Run( "cmd.exe" )
Sleep( 1000 )
$hCmd = _CmdGetWindow( $pCmd )
SendKeepActive( $hCmd )
If _CmdWaitFor( $hCmd, "Microsoft Corp", 1000 ) Then
    ; Clipboard will contain the contents of the cmd buffer
    $cmdtext = ClipGet()
    ; Pick up the last line, this will be the prompt string for future use
    $cmdprompt = StringMid( $cmdtext, StringInStr( $cmdtext, @LF, False, -1 ) + 1)
    Send( "dir{Enter}" )
    If _CmdWaitFor( $hCmd, "bytes free" ) Then
        $cmdtext = ClipGet()
        $bytesfree = StringStripWS(StringMid( $cmdtext, StringInStr( $cmdtext, "Dir(s)" ) + 7, 16 ), 3)
        MsgBox( 0, "DIR", "You have " & $bytesfree & " bytes free" )
    EndIf
EndIf

Hi,

Do you have a sample to *WRITE* to a command line, like using the function WriteConsoleOutput?

Link to comment
Share on other sites

  • 6 years later...

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...