Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

CMDret - AHK functions


  • Please log in to reply
26 replies to this topic
corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
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

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
...

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
It only returns the first line of the output.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
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

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
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.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

- 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 :)

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Hey! Your last post was your 1000th one. How should we celebrate it?

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

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

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

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

- "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)


AHKnow*
  • Guests
  • Last active:
  • Joined: --
Congratulations Corrupt on your 1000th post. Also, great work.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

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) .

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
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

robiandi
  • Guests
  • Last active:
  • Joined: --
@corrupt: I get all the lines of the output. Many thanks for your script.
(I also congratulate on the 1000th post)

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
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


toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
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.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.