Sign in to follow this  
Followers 0
Klaatu

Some useful UDFs

13 posts in this topic

I've written some UDFs that I personally find very useful and though others might too. If they were to be included into the standard UDF library, that'd be fine with me. These are written for the latest beta of AutoIt, but with slight modification could be used in the current release version as well.

This set expands on the _RunDOS() function in <Process.au3>:

#include <Process.au3>
#include <File.au3>
;===============================================================================
;
; Description:    Executes a DOS command in a hidden command window, capturing
;                  the output into a temporary file.
; Syntax:          _RunDOSo( $sCommand )
; Parameter(s):  $sCommand - Command to execute
; Requirement(s):   None
; Return Value(s):  Name of temporary file containing output
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        @Error is set to the exit code of the DOS command.
;
;===============================================================================
Func _RunDOSo($sCommand)
   Local $sTempFile
   
   $sTempFile = _TempFile()
   SetError(_RunDOS($sCommand & ' >' & _Quote($sTempFile)))
   Return $sTempFile
EndFunc  ;==>_RunDOSo

;===============================================================================
;
; Description:    Executes a DOS command in a hidden command window, taking
;                  its input from a file.
; Syntax:          _RunDOSi( $sCommand, [$sInput] )
; Parameter(s):  $sCommand - Command to execute
;                  $sInput - File to be piped into $sCommand
; Requirement(s):   None
; Return Value(s):  On Success - Returns the exit code of the command
;                  On Failure - Depends on RunErrorsFatal setting
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        None
;
;===============================================================================
Func _RunDOSi($sCommand, $sInput)
   Return _RunDOS($sCommand & ' <' & _Quote($sInput))
EndFunc  ;==>_RunDOSi

;===============================================================================
;
; Description:    Executes a DOS command in a hidden command window, taking
;                  its input from a file, and capturing the output into a
;                  temporary file.
; Syntax:          _RunDOSio( $sCommand, [$sInput] )
; Parameter(s):  $sCommand - Command to execute
;                  $sInput - File to be piped into $sCommand
; Requirement(s):   None
; Return Value(s):  Name of temporary file containing output
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        @Error is set to the exit code of the DOS command.
;
;===============================================================================
Func _RunDOSio($sCommand, $sInput)
   Return _RunDOSo($sCommand & ' <' & _Quote($sInput))
EndFunc  ;==>_RunDOSio

;===============================================================================
;
; Description:    Executes a DOS command in a hidden command window, piping
;                  its output into the input of a second command, and capturing
;                  the output of the second DOS command into a temporary file.
; Syntax:          _PipeDOS( $sCommand1, $sCommand2 )
; Parameter(s):  $sCommand1 - First command to execute
;                  $sCommand2 - Second command to execute
; Requirement(s):   None
; Return Value(s):  Name of temporary file containing output of second command
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        @Error is set to exit code of the first DOS command.
;                  @Extended is set to the exit code of the second DOS command.
;
;===============================================================================
Func _PipeDOS($sCommand1, $sCommand2)
   Local $sTempFile1, $sTempFile2, $Terror
   
   $sTempFile1 = _RunDOSo($sCommand1)
   $Terror = @Error
   $sTempFile2 = _RunDOSio($sCommand2, $sTempFile1)
   SetExtended(@Error)
   FileDelete($sTempFile1)
   SetError($Terror)
   Return $sTempFile2
EndFunc  ;==>_PipeDOS

The _Quote function listed above is very simple, and could go into some other module if desired, or easily edited out if seen as too simplistic. Here it is:

;===============================================================================
;
; Description:    Places quote marks around a string.
; Syntax:          _Quote( $sTheString, [$sQuoteChar] )
; Parameter(s):  $sTheString - the string to place quotes around
;                  $sQuoteChar - the quote character to use. Default is ".
; Requirement(s):   None
; Return Value(s):  The first parameter, with the second parameter added at
;                  the start and end.
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        None
;
;===============================================================================
Func _Quote($TheString, $QuoteChar = '"')
   Return ($QuoteChar & $TheString & $QuoteChar)
EndFunc

Now, this one I use all the time when I want to wait for a certain window to exist and send keys to it immediately after.

;===============================================================================
;
; Description:    Wait for a window to exist and make it active.
; Syntax:          _WinWait( $sTitle, [$sText], [$iTimeOut] )
; Parameter(s):  $sTitle - the title of the window
;                  $sText - text to look for in the window
;                  $iTimeOut - how long to wait for the window to exist
; Requirement(s):   None
; Return Value(s):  True if the window exists and we were able to make it active
;                  False if the window did not exist within the timeout period
; Author(s):        Dale (Klaatu) Thompson
; Note(s):        None
;
;===============================================================================
Func _WinWait($sTitle, $sText = '', $iTimeOut = 0)
   If $iTimeOut <= 0 Then
      $iTimeOut = 1000000
   EndIf
   If Not WinWait($sTitle, $sText, $iTimeOut) Then Return False
   Do
      If Not WinActive($sTitle, $sText) Then WinActivate($sTitle, $sText)
   Until WinWaitActive($sTitle, $sText, 1)
   Return True
EndFunc  ;==>_WinWait

I Hope others find these as useful as I have.


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites



yes i think i will.

thanks!

Share this post


Link to post
Share on other sites

I like the quote UDF ;)

Keep it up.


FootbaG

Share this post


Link to post
Share on other sites

Std? Sexually Transmitted Disease? No, that doesn't work. ;)

I don't understand. Could you expand on your statement please? You can't be speaking of ConsoleRead and ConsoleWrite, because those don't work with these. Maybe you could give an example of what you mean? I'd be very interested in your method, if I knew more about what you are speaking of.


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites

I'd be very interested in your method, if I knew more about what you are speaking of.

Hey, Klaatu.

w0uter is speaking of the StdoutRead, StderrRead and StdinWrite functions that are in the beta version of AutoIt. They're a lot more low-level than your own functions; yours have the huge benefit of the command interpreter managing the input/output so you don't have to worry about the process choking because a buffer got written full (and w0uter's _RunSTD function takes measures to prevent this, as well).

The other benefit of your implementation is that you could run it using the release version of AutoIt, which would help people in a situation where policy prevents them from using a beta release product...


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

Well, this is what I came up with after taking w0uter's suggestion to look into the StdInWrite and StdOutRead functions in the beta:

;**********************************************************************
; Run a command without showing any window.
; Returns the output of the command.
; Code is same as w0uter's, just variable names changed.
;**********************************************************************
Func _RunSTD($sCommand, $sWorkDir = @WorkingDir, $iShowMode = @SW_HIDE)
   Local $sResult = '', $iPID = Run($sCommand, $sWorkDir, $iShowMode, 2)
   While Not @Error
      $sResult &= StdoutRead($iPID)
   Wend
   Return $sResult
EndFunc

;**********************************************************************
; Run a command without showing any window.
; Input to the command is supplied by the second argument.
; Returns the output of the command.
;**********************************************************************
Func _RunIO($sCommand, $sInput, $sWorkDir = @WorkingDir, $iShowMode = @SW_HIDE)
   Local $sResult = '', $iPID = Run($sCommand, $sWorkDir, $iShowMode, 3)
   If Not @Error Then
      StdinWrite($iPID, $sInput)
      StdinWrite($iPID)
      While Not @Error
         $sResult &= StdoutRead($iPID)
      Wend
   EndIf
   Return $sResult
EndFunc;==>_RunIO

;**********************************************************************
; Run a "DOS" command without showing any window.
; Returns the output of the command.
;**********************************************************************
Func _RunDOSo($sCommand)
   Return _RunSTD(@ComSpec & ' /C' & $sCommand)
EndFunc  ;==>_RunDOSo

;**********************************************************************
; Run a "DOS" command, taking it's input from a file
; Input to the command is supplied by the second argument.
; Returns the output of the command.
;**********************************************************************
Func _RunDOSio($sCommand, $sInput)
   Return _RunIO(@ComSpec & ' /C' & $sCommand, $sInput)
EndFunc  ;==>_RunDOSio

;**********************************************************************
; Run a "DOS" command, taking it's input from another "DOS" command
; Returns the output of the second command.
;**********************************************************************
Func _PipeDOS($sCommand1, $sCommand2)
   Return _RunDOSio($sCommand2, _RunDOSo($sCommand1))
EndFunc  ;==>_PipeDOS

The problem lies in that it doesn't seem as stable as the old-fashioned way of using temp files. Now, either I've got some bug in this rather simple code somewhere, or the StdOutRead function isn't returning an error when it's done with its output, or something. The code below calls _PipeDOS, which calls _RunDOSo, which calls _RunSTD. But it's hanging up in the While loop of RunSTD for some reason.

$vLines = _PipeDOS('net view','find /I "\\W-S"')

Basically it's supposed to return a subset of computers in the domain. When I used temp files I had no problem, but using the new functions I do. FYI, RunAsSet is not being used.

Does anyone see a bug in my code that could cause it?


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites

After further testing, it looks like it might have to do with the size of the strings being used, but not sure why. The string I'm getting returned from _RunSTD is roughly 345,000 characters. That is being piped into a second process, so I'm using StdinWrite with a string that is 345,000 characters long. I see no documentation stating a limit to this string. The only thing I see is in the description of StdoutRead, which returns a string of up to 64k in a single call.

It does appear that the problem may not lie in _RunSTD after all, although AutoIt's Tooltip indicated that this was where it was hanging. It looks to me now like it might be in StdinWrite being passed such a large string.

BTW, there appears to also be a problem with AutoIt's MsgBox function. Passing a 345k string to MsgBox pegs the CPU out at 100% for a second or so, and no message box is ever shown.


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites

More testing. And while using the Std* routines may be a good idea in theory, in practice, and at this moment in time with the beta, it appears that, at least StdInWrite anyway, they are just too buggy to be relied on.

Maybe when more testing is done they'll be better, but for now the old method of using temporary files works 100%, while the same UDFs using the Std* routines are iffy at best, and at worst totally fail.


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

And while using the Std* routines may be a good idea in theory, in practice, and at this moment in time with the beta, it appears that, at least StdInWrite anyway, they are just too buggy to be relied on.

Mmph. I'm sure you mean that in the nicest possible way. Bring on the Pepsi Challenge.

Regarding _PipeDOS, from your function defs it looks like the call stack is _RunDOSio and _RunIO, rather than _RunDOSo and _RunSTD as you cite above, so I'll leave it to you to debug your own code. And if you're not going to use AutoIt to transform the output from your first command, why don't you just pipe it on the command line rather than having a _PipeDOS function?

Personally, I don't see how it's that much more complicated to use the StdxxxYyy functions as they are in your script rather trying to write a wrapper for every occasion. Here's a terse version:

; Dim our variables
Dim $ourNetview, $ourOutput, $ourFind, $ourWSs
$ourNetview = Run("net view", @SystemDir, @SW_HIDE, 2)
While 1
 $ourOutput &= StdoutRead($ourNetview)
 If @error = -1 Then ExitLoop
WEnd
$ourFind = Run('find /I "\\"', @SystemDir, @SW_HIDE, 3)
StdinWrite($ourFind, $ourOutput)
StdinWrite($ourFind)
While 1
 $ourWSs &= StdoutRead($ourFind)
 If @error = -1 Then ExitLoop
WEnd
MsgBox(0, "Debug", $ourWSs)

...and a nice sexy commented version, courtesy of SciTE's HTML export:

; Dim our variables

Dim $ourNetview, $ourOutput, $ourFind, $ourWSs

; Run "NET.EXE view" and provide a pipe to the STDOUT of the child process

; The final param of 2 means we only care about the output pipe.

$ourNetview = Run("net view", @SystemDir, @SW_HIDE, 2)

; Read the child process's STDOUT until it returns -1, meaning EOF,

; and capture in the $ourOutput variable

While 1

$ourOutput &= StdoutRead($ourNetview)

If @error = -1 Then ExitLoop

WEnd

; Run a FIND command, 3 in the final param means we're providing pipes to

; STDOUT and the standard input, as well. Though the process will spawn

; and we'll get a PID, FIND will sit idle until we close the STDIN pipe,

; so we can't read from it, but we can write as many times as we might need to...

$ourFind = Run('find /I "\\"', @SystemDir, @SW_HIDE, 3)

; Write the data we read from the NET command above to the FIND process

StdinWrite($ourFind, $ourOutput)

; Calling StdinWrite with no data to write closes the STDIN pipe, thereafter

; we can start reading from the FIND process.

StdinWrite($ourFind)

; Read the child process's STDOUT until it returns -1

; and capture in the $ourWSs variable

While 1

$ourWSs &= StdoutRead($ourFind)

If @error = -1 Then ExitLoop

WEnd

; Show the output

MsgBox(0, "Debug", $ourWSs)

[Edit: Had to fix a format error, in the terse code, believe-it-or-not...]

Edited by DaveF

Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


Link to post
Share on other sites

Regarding _PipeDOS, from your function defs it looks like the call stack is _RunDOSio and _RunIO, rather than _RunDOSo and _RunSTD as you cite above, so I'll leave it to you to debug your own code.

While it's true that _RunDOSio calls _RunIO, you're wrong. The second parameter to _RunDOSio in _PipeDOS is a call to _RunDOSo which calls _RunSTD, like I stated. Now whose debugging skills are in question?

And if you're not going to use AutoIt to transform the output from your first command, why don't you just pipe it on the command line rather than having a _PipeDOS function?

Because the functions to do the work are already written maybe?

Personally, I don't see how it's that much more complicated to use the StdxxxYyy functions as they are in your script rather trying to write a wrapper for every occasion.

No one said it was "more complicated". In fact, if it worked it would be less so (though not by much. It's all pretty simple code either way). And they're general functions so more complicated code doesn't have to be written "for every occasion". Duh.

...and a nice sexy commented version, courtesy of SciTE's HTML export:

(snipped for brevity)

That's a sexy version? Except for the coloring, it sucks. What happened to the indenting?

Regardless, did you actually try your code? I mean, in more than just a superficial way? If you've done any testing of StdinWrite, you would have noticed the Runned process hanging after about 256k sent to it's STDIN, with the AutoIt script hanging right there along with it when further calls to StdinWrite hang the script as well. Try for instance sending just a random 1 million character string in the call to StdinWrite instead of the output of a 'net view' (which is variable length, depending on the complexity of your network or domain) and see what happens.

Frankly, I don't see the point to your post at all. Apparently it was just to rag on me for pointing out that one of AutoIt's functions in the beta version needs more testing. Smells a bit fanboyish, don't you think? I would love to be able to eliminate the use of temp files and maybe speed things up a bit while I'm at it, but if it doesn't work well enough in its current form, I'm going to use what works 100%. When the new functions work more reliably then I'll look at changing the routines. Feel free to use the new stuff if it makes you happy; I'll wait until they're in better shape myself.


My Projects:DebugIt - Debug your AutoIt scripts with DebugIt!

Share this post


Link to post
Share on other sites

Smells a bit fanboyish, don't you think?

Oh, it's way worse than that. ;)

Saw your bug report post, I'll look at it in the morning.


Yes yes yes, there it was. Youth must go, ah yes. But youth is only being in a way like it might be an animal. No, it is not just being an animal so much as being like one of these malenky toys you viddy being sold in the streets, like little chellovecks made out of tin and with a spring inside and then a winding handle on the outside and you wind it up grrr grrr grrr and off it itties, like walking, O my brothers. But it itties in a straight line and bangs straight into things bang bang and it cannot help what it is doing. Being young is like being like one of these malenky machines.

Share this post


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
Sign in to follow this  
Followers 0