Jump to content


Photo

CMDret - AHK functions


  • Please log in to reply
26 replies to this topic

#1 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 12:12 AM

CMDret-AHK functions

Note: Development on this version has currently been discontinued. The current version of CMDret can be found here (dll version).

Stream - version 0.03 beta
RunReturn - version 1.10 beta

Here you will find AutoHotkey code based on cmdret.dll, provided as functions :!: instead of having to include the cmdret.dll file.

CMDret can be used to retrieve and store output from console programs in a variable without displaying the console window.

Although it's not directly included in AutoHotkey yet, the functionaility in cmdret.dll can be accomplished with AHK code. As time permits, I'll be porting other functions in cmdret.dll to AHK functions also. If anyone would like to contribute, please feel free to add, modify, make suggestions, report bugs, etc... :)
Please be aware: If you use these functions you do so at your own risk.

As changes are made I will continue to update the code in this post with the current versions. As updates can sometimes produce undesirable results, a zip file can be downloaded here that contains the current versions, previous versions and a changelog. A brief changelog will also be added at the end of this post.

Here's the current version of the CMDret_Stream Function. Either save as a separate file and use #Include or add to the end of an existing script. This function has been designed to be able to retrieve and display data as it would become available in a CMD window instead of having to wait until the command's process has completed.
; ******************************************************************
; CMDret-AHK functions by corrupt
;
; CMDret_Stream
; version 0.03 beta
; Updated: Feb 19, 2007
; 
; CMDret code modifications and/or contributions have been made by:
; Laszlo, shimanov, toralf, Wdb
; ******************************************************************
; Usage:
; CMDin - command to execute
; CMDname - type of output to process (Optional)
; WorkingDir - full path to working directory (Optional)
; ******************************************************************
; Known Issues:
; - If using dir be sure to specify a path (example: cmd /c dir c:\)
; or specify a working directory
; - Running 16 bit console applications may not produce output. Use
; a 32 bit application to start the 16 bit process to receive output
; ******************************************************************
; Additional requirements:
; - Your script must also contain a CMDret_Output function
;
; CMDret_Output(CMDout, CMDname="")
; Usage:
; CMDout - each line of output returned (1 line each time)
; CMDname - type of output to process (Optional)
; ******************************************************************
; Code Start
; ******************************************************************

CMDret_Stream(CMDin, CMDname="", WorkingDir=0)
{
  Global cmdretPID
  tcWrk := WorkingDir=0 ? "Int" : "Str"
  idltm := A_TickCount + 20
  LivePos = 1
  VarSetCapacity(CMDout, 1, 32)
  VarSetCapacity(sui,68, 0)
  VarSetCapacity(pi, 16, 0)
  VarSetCapacity(pa, 12, 0)
  Loop, 4 {
    DllCall("RtlFillMemory", UInt,&pa+A_Index-1, UInt,1, UChar,12 >> 8*A_Index-8)
    DllCall("RtlFillMemory", UInt,&pa+8+A_Index-1, UInt,1, UChar,1 >> 8*A_Index-8)
  }
  IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, "UInt",&pa, "Int",0) <> 0) {
    Loop, 4
      DllCall("RtlFillMemory", UInt,&sui+A_Index-1, UInt,1, UChar,68 >> 8*A_Index-8)
    DllCall("GetStartupInfo", "UInt", &sui)
    Loop, 4 {
      DllCall("RtlFillMemory", UInt,&sui+44+A_Index-1, UInt,1, UChar,257 >> 8*A_Index-8)
      DllCall("RtlFillMemory", UInt,&sui+60+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
      DllCall("RtlFillMemory", UInt,&sui+64+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
      DllCall("RtlFillMemory", UInt,&sui+48+A_Index-1, UInt,1, UChar,0 >> 8*A_Index-8)
    }
    IF (DllCall("CreateProcess", Int,0, Str,CMDin, Int,0, Int,0, Int,1, "UInt",0, Int,0, tcWrk, WorkingDir, UInt,&sui, UInt,&pi) <> 0) {
      Loop, 4
        cmdretPID += *(&pi+8+A_Index-1) << 8*A_Index-8
      Loop {
        idltm2 := A_TickCount - idltm
        If (idltm2 < 15) {
          DllCall("Sleep", Int, 15)
          Continue
        }
        IF (DllCall("PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 ) <> 0 ) {
          Process, Exist, %cmdretPID%
          IF (ErrorLevel OR bSize > 0) {
            IF (bSize > 0) {
              VarSetCapacity(lpBuffer, bSize+1, 0)
              IF (DllCall("ReadFile", "UInt",hRead, "Str", lpBuffer, "Int",bSize, "UInt*",bRead, "Int",0) > 0) {
                IF (bRead > 0) {
                  IF (StrLen(lpBuffer) < bRead) {
                    VarSetCapacity(CMcpy, bRead, 32)
                    bRead2 = %bRead%
                    Loop {
                      DllCall("RtlZeroMemory", "UInt", &CMcpy, Int, bRead)
                      NULLptr := StrLen(lpBuffer)
                      cpsize := bread - NULLptr
                      DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", (&lpBuffer + NULLptr + 2), "Int", (cpsize - 1))
                      DllCall("RtlZeroMemory", "UInt", (&lpBuffer + NULLptr), Int, cpsize)
                      DllCall("RtlMoveMemory", "UInt", (&lpBuffer + NULLptr), "UInt", &CMcpy, "Int", cpsize)
                      bRead2 --
                      IF (StrLen(lpBuffer) > bRead2)
                        break
                    }
                  }
				  VarSetCapacity(lpBuffer, -1)
                  CMDout .= lpBuffer
                  bRead = 0
                }
              }
            }
          }
          ELSE
            break
        }
        ELSE
          break
        idltm := A_TickCount
        LiveFound := RegExMatch(CMDout, "m)^(.*)", LiveOut, LivePos)
        If (LiveFound)
          SetTimer, cmdretSTR, 5
      }
      cmdretPID=
      DllCall("CloseHandle", UInt, hWrite)
      DllCall("CloseHandle", UInt, hRead)
    }
  }
  StringTrimLeft, LiveRes, CMDout, %LivePos%
  If LiveRes <>
    Loop, Parse, LiveRes, `n
    {
      FileLine = %A_LoopField%
      StringTrimRight, FileLine, FileLine, 1
      CMDret_Output(FileLine, CMDname)
    }
  StringTrimLeft, CMDout, CMDout, 1
  cmdretPID = 0
  Return, CMDout
cmdretSTR:
SetTimer, cmdretSTR, Off
If (LivePosLast <> LiveFound) {
  FileLine = %LiveOut1%
  LivePos := LiveFound + StrLen(FileLine) + 1
  LivePosLast := LivePos
  CMDret_Output(FileLine, CMDname)
}
Return
}

Here's the current version of the CMDret_RunReturn function. Either save as a separate file and use #Include or add to the end of an existing script.
; ****************************************************************** 
; CMDret-AHK functions 
; version 1.10 beta 
; 
; Updated: Dec 5, 2006 
; by: corrupt 
; Code modifications and/or contributions made by: 
; Laszlo, shimanov, toralf, Wdb  
; ****************************************************************** 
; Usage: 
; CMDin - command to execute
; WorkingDir - full path to working directory (Optional) 
; ****************************************************************** 
; Known Issues: 
; - If using dir be sure to specify a path (example: cmd /c dir c:\)
; or specify a working directory    
; - Running 16 bit console applications may not produce output. Use 
; a 32 bit application to start the 16 bit process to receive output  
; ****************************************************************** 
; Additional requirements: 
; - none 
; ****************************************************************** 
; Code Start 
; ****************************************************************** 

CMDret_RunReturn(CMDin, WorkingDir=0) 
{ 
  Global cmdretPID
  tcWrk := WorkingDir=0 ? "Int" : "Str"
  idltm := A_TickCount + 20 
  CMsize = 1 
  VarSetCapacity(CMDout, 1, 32) 
  VarSetCapacity(sui,68, 0) 
  VarSetCapacity(pi, 16, 0) 
  VarSetCapacity(pa, 12, 0) 
  Loop, 4 { 
    DllCall("RtlFillMemory", UInt,&pa+A_Index-1, UInt,1, UChar,12 >> 8*A_Index-8) 
    DllCall("RtlFillMemory", UInt,&pa+8+A_Index-1, UInt,1, UChar,1 >> 8*A_Index-8) 
  } 
  IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, "UInt",&pa, "Int",0) <> 0) { 
    Loop, 4 
      DllCall("RtlFillMemory", UInt,&sui+A_Index-1, UInt,1, UChar,68 >> 8*A_Index-8) 
    DllCall("GetStartupInfo", "UInt", &sui) 
    Loop, 4 { 
      DllCall("RtlFillMemory", UInt,&sui+44+A_Index-1, UInt,1, UChar,257 >> 8*A_Index-8) 
      DllCall("RtlFillMemory", UInt,&sui+60+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8) 
      DllCall("RtlFillMemory", UInt,&sui+64+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8) 
      DllCall("RtlFillMemory", UInt,&sui+48+A_Index-1, UInt,1, UChar,0 >> 8*A_Index-8) 
    }
    IF (DllCall("CreateProcess", Int,0, Str,CMDin, Int,0, Int,0, Int,1, "UInt",0, Int,0, tcWrk, WorkingDir, UInt,&sui, UInt,&pi) <> 0) {
      Loop, 4 
        cmdretPID += *(&pi+8+A_Index-1) << 8*A_Index-8 
      Loop { 
        idltm2 := A_TickCount - idltm 
        If (idltm2 < 10) { 
          DllCall("Sleep", Int, 10) 
          Continue 
        } 
        IF (DllCall("PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 ) <> 0 ) { 
          Process, Exist, %cmdretPID% 
          IF (ErrorLevel OR bSize > 0) { 
            IF (bSize > 0) { 
              VarSetCapacity(lpBuffer, bSize+1) 
              IF (DllCall("ReadFile", "UInt",hRead, "Str", lpBuffer, "Int",bSize, "UInt*",bRead, "Int",0) > 0) { 
                IF (bRead > 0) { 
                  TRead += bRead 
                  VarSetCapacity(CMcpy, (bRead+CMsize+1), 0) 
                  CMcpy = a 
                  DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", &CMDout, "Int", CMsize) 
                  DllCall("RtlMoveMemory", "UInt", &CMcpy+CMsize, "UInt", &lpBuffer, "Int", bRead) 
                  CMsize += bRead 
                  VarSetCapacity(CMDout, (CMsize + 1), 0) 
                  CMDout=a    
                  DllCall("RtlMoveMemory", "UInt", &CMDout, "UInt", &CMcpy, "Int", CMsize) 
                  VarSetCapacity(CMDout, -1)   ; fix required by change in autohotkey v1.0.44.14 
                } 
              } 
            } 
          } 
          ELSE 
            break 
        } 
        ELSE 
          break 
        idltm := A_TickCount 
      } 
      cmdretPID= 
      DllCall("CloseHandle", UInt, hWrite) 
      DllCall("CloseHandle", UInt, hRead) 
    }
  } 
  IF (StrLen(CMDout) < TRead) { 
    VarSetCapacity(CMcpy, TRead, 32) 
    TRead2 = %TRead% 
    Loop { 
      DllCall("RtlZeroMemory", "UInt", &CMcpy, Int, TRead) 
      NULLptr := StrLen(CMDout) 
      cpsize := Tread - NULLptr 
      DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", (&CMDout + NULLptr + 2), "Int", (cpsize - 1)) 
      DllCall("RtlZeroMemory", "UInt", (&CMDout + NULLptr), Int, cpsize) 
      DllCall("RtlMoveMemory", "UInt", (&CMDout + NULLptr), "UInt", &CMcpy, "Int", cpsize) 
      TRead2 -- 
      IF (StrLen(CMDout) > TRead2) 
        break 
    } 
  } 
  StringTrimLeft, CMDout, CMDout, 1 
  Return, CMDout 
} 

Recent Changes:
Stream - Version 0.03 beta
- Initial release

RunReturn - Version 1.10 beta
- Added the ability to specify the working directory (optional)

RunReturn - Version 1.09 beta
- bumped version to 1.09 since a fix was necessary to maintain functionality (Thanks Wdb, Chris - for the fix and update to the code posted :) )
- A VarSetCapacity line was added after a DllCall line to make it compatible with AutoHotkey v1.0.44.14 and later

RunReturn - Version 1.08 beta
- Fixed truncated output when output contained null characters (Thanks evl)

Enjoy :D

#2 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 01:08 AM

...

#3 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 12 March 2006 - 02:26 AM

It only returns the first line of the output.

#4 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 12 March 2006 - 03:01 AM

It looks like black magic. I tried to decipher it, but found
- The "DllCall("CloseHandle" UInt, hWrite)" commands miss a comma, they return an ErrorLevel = -2, and don't do anything. Also, do you really want to close the handle inside the loop and also outside? I think the one inside the loop is superfluous.
- ExtractInteger is not called, need not be included.
- Setting several variables to the empty string does not do anything, these lines could just be deleted.
- "VarSetCapacity(lpBuffer, 1024)" need not be in the loop, either. Move it to the beginning of the function.
- "VarSetCapacity(x,c,0) initializes the memory with 0's, you don't need InsertInteger to write 0's in them.

With the corresponding modifications I ended up with the following, shorter variant of your script, which seems to be equivalent (still returning only the first line of the command output). This version might be easier to debug.

Current version: 0.1

; SetBatchLines -1   ; Different behavior with or w/o it
AutoTrim Off         ; needed for leading/trailing spaces returned
MsgBox 48, CMDret test - Environment Vars, % CMDret_RunReturn("cmd /c set"),0.1
MsgBox 48, CMDret test - Directory Listing,% CMDret_RunReturn("cmd /c dir c:\"),0.1
/* - for long stability test
Loop 1000
{
   If StrLen(CMDret_RunReturn("cmd /c set")) < 100
      MsgBox ERROR
   If StrLen(CMDret_RunReturn("cmd /c dir c:\")) < 100
      MsgBox ERROR
   TrayTip,,%A_Index%
}
*/
CMDret_RunReturn(CMDin)
{
  Global cmdretPID   ; for external abort
  dly := (A_BatchLines <> -1) * 50 ; needs longer delay when Batchlines <> -1

  VarSetCapacity(lpBuffer,1024)
  VarSetCapacity(sui,68, 0)
  VarSetCapacity(pi, 16, 0)
  VarSetCapacity(pa, 12, 0)
  InsertInteger( 12, pa, 0)
  InsertInteger( 1,  pa, 8)

  IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, UInt,&pa, Int,0) <> 0) {
    InsertInteger(68,    sui, 0 )
    DllCall("GetStartupInfo", "UInt", &sui)
    InsertInteger(0x101, sui, 44)
    InsertInteger(0,     sui, 48)
    InsertInteger(hWrite,sui, 60)
    InsertInteger(hWrite,sui, 64)

    IF (DllCall("CreateProcess",Int,0,Str,CMDin,Int,0,Int,0,Int,1,UInt,0,Int,0,Int,0,UInt,&sui,UInt,&pi)<>0) {
      cmdretPID := ExtractUInt(pi, 8)
      Loop {
        IF DllCall("PeekNamedPipe",uint,hRead, uint,0, uint,0, uint,0, "UInt*",bSize, uint,0) = 0
          break
        Process Exist, %cmdretPID%
        If (ErrorLevel = 0 and bsize = 0)
          break
        If (bsize = 0) {
          Sleep %dly% ; only sleep before early continue
          Continue
        }
        VarSetCapacity(lpBuffer, bSize, 0)
        IF (DllCall("ReadFile",UInt,hRead, Str,lpBuffer, Int,bSize, "UInt*",bRead, Int,0) > 0) {
          IFEqual bRead,0, Continue
          CMDout = %CMDout%%lpBuffer%
          }
      } ; Loop
      cmdretPID =
    } ; IF CreateProcess

    DllCall("CloseHandle", UInt, hWrite)
    DllCall("CloseHandle", UInt, hRead)
  } ; IF CreatePipe

  Return CMDout
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) {
   Loop %pSize%
      DllCall("RtlFillMemory", UInt,&pDest+pOffset+A_Index-1, UInt,1, UChar,pInteger >> 8*A_Index-8)
}

ExtractUInt(ByRef pSource, pOffset = 0, pSize = 4) {
   Loop %pSize%
      result += *(&pSource+pOffset+A_Index-1) << 8*A_Index-8
   Return result
}

If you like to experiment, here is the shorthand of Corrupt's 1.06 script. You can set the delay values, used when BatchLines = -1, or otherwise. I tried to make it as short as possible, which is also the fastest. (For Win Me/98/95 you need in 2 places the original RtlFillMemory loop, instead of RtlFillMemoryUlong.)
CMDret_RunReturn(CMDin, MinSleep = 0, MaxSleep = 10)
{
  Global cmdretPID   ; for external abort
  dly := MinSleep + (A_BatchLines <> -1) * MaxSleep

  VarSetCapacity(lpBuffer,1024)
  VarSetCapacity(sui,68, 0)
  VarSetCapacity(pi, 16, 0)
  VarSetCapacity(pa, 12, 0)
  DllCall("RtlFillMemory", UInt,&pa,  UInt,1, UChar,12)
  DllCall("RtlFillMemory", UInt,&pa+8,UInt,1, UChar,1)

  IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, UInt,&pa, Int,0) <> 0) {
    DllCall("RtlFillMemory", UInt,&sui,    UInt,1, UChar,68)
    DllCall("GetStartupInfo", "UInt", &sui)
    DllCall("RtlFillMemory", UInt,&sui+44, UInt,1, UChar,1)
    DllCall("RtlFillMemory", UInt,&sui+45, UInt,1, UChar,1)
    DllCall("RtlFillMemory", UInt,&sui+48, UInt,1, UChar,0)
    DllCall("ntoskrnl.exe\RtlFillMemoryUlong", Uint,&sui+60, Uint,4, Uint,hWrite)
    DllCall("ntoskrnl.exe\RtlFillMemoryUlong", Uint,&sui+64, Uint,4, Uint,hWrite)

    IF (DllCall("CreateProcess",Int,0,Str,CMDin,Int,0,Int,0,Int,1,UInt,0,Int,0,Int,0,UInt,&sui,UInt,&pi)<>0) {
      cmdretPID := *(&pi+8)   ; PID is 16 bit
      cmdretPID += *(&pi+9) << 8
      Loop {
        IF DllCall("PeekNamedPipe",uint,hRead, uint,0, uint,0, uint,0, "UInt*",bSize, uint,0) = 0
          break
        IfEqual bsize,0, {
          Process Exist, %cmdretPID%
          IfEqual ErrorLevel,0, break
          Sleep %dly%
          Continue
        }
        VarSetCapacity(lpBuffer, bSize, 0)
        IF (DllCall("ReadFile",UInt,hRead, Str,lpBuffer, Int,bSize, "UInt*",bRead, Int,0) > 0) {
          IFEqual bRead,0, Continue
          CMDout = %CMDout%%lpBuffer%
        }
      } ; Loop
    } ; IF CreateProcess

    DllCall("CloseHandle", UInt, hWrite)
    DllCall("CloseHandle", UInt, hRead)
  } ; IF CreatePipe

  cmdretPID =
  Return CMDout
}

Edit: 2006.03.14, different sleep times dependent on A_BatchLines
Edit: 2006.03.16, experimental shorthand version of 1.06, with variable Sleep times

#5 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 03:08 AM

Thanks for checking it out Laszlo :) . I've noticed a couple bugs and I'm trying to determine the source of the issue. The method used to determine when to stop reading doesn't seem to be reliable and there's an issue that a script will lock up completely when the process ends if the time to read from the pipe is extended.

#6 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 03:22 AM

- ExtractInteger is not called, need not be included

I had used it in a previously unreleased version and left it in since the next 3 functions will likely use it.

- Setting several variables to the empty string does not do anything, these lines could just be deleted.

There is a purpose to that. It's to set up the variables so that I can later use a pointer to the variables to add content via DllCall ;) . They may not all be necessary as the code may not be using pointers to all of them but I find it much easier/safer to set them all up near the start of the function when testing.

- "VarSetCapacity(lpBuffer, 1024)" need not be in the loop, either. Move it to the beginning of the function.

It is used to zero the contents after each read but might not be the best method...

- "VarSetCapacity(x,c,0) initializes the memory with 0's, you don't need InsertInteger to write 0's in them.

True. It was more to keep the significant parts of the structures clear in my head but they can be removed.

With the corresponding modifications I ended up with the following, shorter variant of your script, which seems to be equivalent (still returning only the first line of the command output). This version might be easier to debug.

I'll give it a try. Thanks :)

#7 Laszlo

Laszlo
  • Fellows
  • 4713 posts

Posted 12 March 2006 - 03:26 AM

Hey! Your last post was your 1000th one. How should we celebrate it?

#8 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 03:34 AM

Hey! Your last post was your 1000th one. How should we celebrate it?

:D Thanks for noticing :lol: . Cheers ;)

#9 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 03:52 AM

- "VarSetCapacity(x,c,0) initializes the memory with 0's, you don't need InsertInteger to write 0's in them.

True. It was more to keep the significant parts of the structures clear in my head but they can be removed.


Hmm... This one needs to be left in after the GetStartupInfo call to keep the console hidden as the GetStartupInfo call overwrites the value.
InsertInteger("0", sui, 48)


#10 AHKnow*

AHKnow*
  • Guests

Posted 12 March 2006 - 04:32 AM

Congratulations Corrupt on your 1000th post. Also, great work.

#11 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 04:56 AM

Congratulations Corrupt on your 1000th post. Also, great work.

Thanks :) . I'm not sure if that means I've contributed many times or that I never shut up... :lol: but it's all good 8) .

#12 corrupt

corrupt
  • Members
  • 2558 posts

Posted 12 March 2006 - 05:05 AM

Updated the version in the first post to hopefully fix a few nasty bugs. This version will wait for the process to finish before displaying any output. This may not work well with the other functions that are planned but has been changed in this version as a temporary fix.

Edit: Updated again to 1.02 beta

Edit: Updated to 1.03 beta
- Fix: Close Pipe Handles if process didn't start

#13 robiandi

robiandi
  • Guests

Posted 12 March 2006 - 07:00 AM

@corrupt: I get all the lines of the output. Many thanks for your script.
(I also congratulate on the 1000th post)

#14 shimanov

shimanov
  • Members
  • 610 posts

Posted 12 March 2006 - 08:11 AM

A robust method to read from a pipe:

pid := DecodeInteger( "uint4", &process_information, 8 )

text = |
break_when_pipe_empty := false
loop,
{
	Process, Exist, %pid%
	break_when_pipe_empty := !ErrorLevel

	if ( !DllCall( "PeekNamedPipe", "uint", h_pipe_read, "uint", 0, "uint", 0, "uint", 0, "uint*", pipe_size, "uint", 0 ) )
		break
	
	if ( pipe_size > 0 )
	{
		VarSetCapacity( buffer, pipe_size, 0 )
		if ( !DllCall( "ReadFile", "uint", h_pipe_read, "str", buffer, "uint", pipe_size, "uint*", buffer_actual, "uint", 0 ) )
			break

		text = %text%%buffer%		
	}
	else ifEqual, break_when_pipe_empty, 1, break
	
	Sleep, 10
}
StringTrimLeft, text, text, 1


#15 toralf

toralf
  • Fellows
  • 3948 posts

Posted 12 March 2006 - 10:36 AM

Just a question:
Why have you decided to give the length of the output back as a return value and the outout itself as a ByRef var? Are there design considerations that favour this setting?

I would prefer to get the output back as a return value. Thus
- I wouldn't need to to set the capacity of the var
- the parameter would just be the command to execute
- if I need the length of the output, I can still do the StrLen()

Anyway, Thanks for this great work. I'll use it. And congrats to your 1000th post.