Sign in to follow this  
Followers 0
KaFu

CreateProcess with STDIO [Solved]

16 posts in this topic

#1 ·  Posted (edited)

Hiho,

trying to figure out how to get my example Retrieve Unicode results from Console working on x64 I took a look at the $tagSTARTUPINFO. Seems to work fine on x86.

This is the current definition:

Global Const $tagSTARTUPINFO = "int Size;ptr Reserved1;ptr Desktop;ptr Title;int X;int Y;int XSize;int YSize;int XCountChars;" & _
        "int YCountChars;int FillAttribute;int Flags;short ShowWindow;short Reserved2;ptr Reserved3;int StdInput;" & _
        "int StdOutput;int StdError"

MSDN defines the last three elements as handles. This clearly busts the current definition (int).

typedef struct _STARTUPINFO {

DWORD cb;

LPTSTR lpReserved;

LPTSTR lpDesktop;

LPTSTR lpTitle;

DWORD dwX;

DWORD dwY;

DWORD dwXSize;

DWORD dwYSize;

DWORD dwXCountChars;

DWORD dwYCountChars;

DWORD dwFillAttribute;

DWORD dwFlags;

WORD wShowWindow;

WORD cbReserved2;

LPBYTE lpReserved2;

HANDLE hStdInput;

HANDLE hStdOutput;

HANDLE hStdError;

} STARTUPINFO, *LPSTARTUPINFO;

LPTSTR equals ptr? I don't know ;). Anyone willing to take a look. Here's my current suggestion:

Global Const $tagSTARTUPINFO = "int Size;ptr Reserved1;ptr Desktop;ptr Title;int X;int Y;int XSize;int YSize;int XCountChars;" & _
        "int YCountChars;int FillAttribute;int Flags;short ShowWindow;short Reserved2;ptr Reserved3;handle StdInput;" & _
        "handle StdOutput;handle StdError"
Edited by KaFu

Share this post


Link to post
Share on other sites



This is the current definition:

No, it's not.

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

;) According to the UDF help-file... which is not up-to-date... :) ...

The current StructureConstants.au3 defines it as:

Global Const $tagSTARTUPINFO = "dword Size;ptr Reserved1;ptr Desktop;ptr Title;dword X;dword Y;dword XSize;dword YSize;dword XCountChars;" & _
        "dword YCountChars;dword FillAttribute;dword Flags;word ShowWindow;word Reserved2;ptr Reserved3;handle StdInput;" & _
        "handle StdOutput;handle StdError"

LPTSTR = ptr?

LPBYTE = ptr?

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

MSDN says the following about LPTSTR: "The letter "T" in a type definition, for example, TCHAR or LPTSTR, designates a generic type that can be compiled for either Windows code pages or Unicode." Also has this to say: "An LPWSTR if UNICODE is defined, an LPSTR otherwise." So maybe a wstr* or just a wstr?

Edited by jaberwocky6669

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Replacing LPTSTR = ptr with LPTSTR = wstr or LPTSTR = wstr* makes the function fail on x86 too. Thanks for the link to MSDN Windows Data Types definitions, something to bookmark , knew it, but didn't remembered it ;).

Edit:

Here's a working x86 example, fails on x64.

#AutoIt3Wrapper_UseX64=n
#include <NamedPipes.au3>
#include <WinAPI.au3>

Global Const $tagSTARTUPINFO_New = "dword Size;ptr Reserved1;ptr Desktop;ptr Title;dword X;dword Y;dword XSize;dword YSize;dword XCountChars;" & _
        "dword YCountChars;dword FillAttribute;dword Flags;word ShowWindow;word Reserved2;ptr Reserved3;handle StdInput;" & _
        "handle StdOutput;handle StdError"

; Example Start
#include <array.au3> ; for example only
$timer = TimerInit()
$aFiles = _FileListToArrayEx(@ProgramFilesDir, "*.*", 0, -1, True, True)
ConsoleWrite(TimerDiff($timer) & @CRLF & $aFiles[0] & " files read" & @CRLF)
_ArrayDisplay($aFiles)
; Example End

;===============================================================================
;
; Description:    lists all or preferred files and or folders in a specified path (Similar to using Dir with the /B Switch)
; Syntax:          _FileListToArrayEx($sPath, $sFilter = '*.*', $iFlag = 0, $sExclude = '')
; Parameter(s):     $sPath = Path to generate filelist for
;                   $sFilter = The filter to use. Search the Autoit3 manual for the word "WildCards" For details, support now for multiple searches
;                           Example *.exe; *.txt will find all .exe and .txt files
;                  $iFlag = determines weather to return file or folders or both.
;                   $sExclude = exclude a file from the list by all or part of its name
;                           Example: Unins* will remove all files/folders that start with Unins
;                       $iFlag=0(Default) Return both files and folders
;                      $iFlag=1 Return files Only
;                       $iFlag=2 Return Folders Only
;
; Requirement(s):   None
; Return Value(s):  On Success - Returns an array containing the list of files and folders in the specified path
;                       On Failure - Returns the an empty string "" if no files are found and sets @Error on errors
;                       @Error or @extended = 1 Path not found or invalid
;                       @Error or @extended = 2 Invalid $sFilter or Invalid $sExclude
;                      @Error or @extended = 3 Invalid $iFlag
;                       @Error or @extended = 4 No File(s) Found
;
; Author(s):        SmOke_N, modified by mfecteau, Ascend4nt & KaFu
;                   http://www.autoitscript.com/forum/index.php?showtopic=33930&view=findpost&p=799369
; Note(s):          The array returned is one-dimensional and is made up as follows:
;                   $array[0] = Number of Files\Folders returned
;                   $array[1] = 1st File\Folder
;                   $array[2] = 2nd File\Folder
;                   $array[3] = 3rd File\Folder
;                   $array[n] = nth File\Folder
;
;                   All files are written to a "reserved" .tmp file (Thanks to gafrost) for the example
;                   The Reserved file is then read into an array, then deleted
;===============================================================================
Func _FileListToArrayEx($s_path, $s_mask = "*.*", $i_flag = 0, $s_exclude = -1, $f_recurse = False, $f_full_path = False)

If FileExists($s_path) = 0 Then Return SetError(1, 1, 0)

    ; Strip trailing backslash, and add one after to make sure there's only one
    $s_path = StringRegExpReplace($s_path, "[\\/]+\z", "") & "\"

    ; Set all defaults
    If $s_mask = -1 Or $s_mask = Default Then $s_mask = "*.*"
    If $i_flag = -1 Or $i_flag = Default Then $i_flag = 0
    If $s_exclude = -1 Or $s_exclude = Default Then $s_exclude = ""

    ; Look for bad chars
    If StringRegExp($s_mask, "[/:><\|]") Or StringRegExp($s_exclude, "[/:><\|]") Then
        Return SetError(2, 2, 0)
    EndIf

    ; Strip leading spaces between semi colon delimiter
    $s_mask = StringRegExpReplace($s_mask, "\s*;\s*", ";")
    If $s_exclude Then $s_exclude = StringRegExpReplace($s_exclude, "\s*;\s*", ";")

    ; Confirm mask has something in it
    If StringStripWS($s_mask, 8) = "" Then Return SetError(2, 2, 0)
    If $i_flag < 0 Or $i_flag > 2 Then Return SetError(3, 3, 0)

    ; Validate and create path + mask params
    Local $a_split = StringSplit($s_mask, ";"), $s_hold_split = ""
    For $i = 1 To $a_split[0]
        If StringStripWS($a_split[$i], 8) = "" Then ContinueLoop
        If StringRegExp($a_split[$i], "^\..*?\..*?\z") Then
            $a_split[$i] &= "*" & $a_split[$i]
        EndIf
        $s_hold_split &= '"' & $s_path & $a_split[$i] & '" '
    Next
    $s_hold_split = StringTrimRight($s_hold_split, 1)
    If $s_hold_split = "" Then $s_hold_split = '"' & $s_path & '*.*"'

    Local $i_pid, $s_stdout, $s_hold_out, $s_dir_file_only = "", $s_recurse = "/s "
    If $i_flag = 1 Then $s_dir_file_only = ":-d"
    If $i_flag = 2 Then $s_dir_file_only = ":D"
    If Not $f_recurse Then $s_recurse = ""

    $command = @ComSpec & " /u /c dir /b " & $s_recurse & "/a" & $s_dir_file_only & " " & $s_hold_split
    $s_hold_out = _RunWaitStdOut($command, "", @SW_HIDE)
    ; ConsoleWrite($command & @crlf & $s_hold_out & @crlf & @extended & @crlf)

    $s_hold_out = StringRegExpReplace($s_hold_out, "\v+\z", "")
    If Not $s_hold_out Then Return SetError(4, 4, 0)

    ; Parse data and find matches based on flags
    Local $a_fsplit = StringSplit(StringStripCR($s_hold_out), @LF), $s_hold_ret
    $s_hold_out = ""

    If $s_exclude Then $s_exclude = StringReplace(StringReplace($s_exclude, "*", ".*?"), ";", "|")

    For $i = 1 To $a_fsplit[0]
        If $s_exclude And StringRegExp(StringRegExpReplace( _
                $a_fsplit[$i], "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\Q" & $s_exclude & "\E") Then ContinueLoop
        If StringRegExp($a_fsplit[$i], "^\w:[\\/]+") = 0 Then $a_fsplit[$i] = $s_path & $a_fsplit[$i]
        If $f_full_path Then
            $s_hold_ret &= $a_fsplit[$i] & Chr(1)
        Else
            $s_hold_ret &= StringRegExpReplace($a_fsplit[$i], "((?:.*?[\\/]+)*)(.*?\z)", "$2") & Chr(1)
        EndIf
    Next

    $s_hold_ret = StringTrimRight($s_hold_ret, 1)
    If $s_hold_ret = "" Then Return SetError(5, 5, 0)

    Return StringSplit($s_hold_ret, Chr(1))
EndFunc   ;==>_FileListToArrayEx

; ====================================================================================================
; Execute a command and display the results
; ====================================================================================================
; Paul Campbell (PaulIA), ProgAndy, modified by KaFu
; http://www.autoitscript.com/forum/index.php?showtopic=76607&view=findpost&p=555091

Func _RunWaitStdOut($sCmd, $sWorkingDir = "", $state = @SW_SHOW)

    Local $iBytes, $sData, $hReadPipe, $hWritePipe, $tBuffer, $tProcess, $tSecurity, $tStartup
    Local $STILL_ACTIVE = 0x103
    Local Const $STARTF_USESHOWWINDOW = 0x1
    Local Const $STARTF_USESTDHANDLES = 0x100

    ; Set up security attributes
    $tSecurity = DllStructCreate($tagSECURITY_ATTRIBUTES)
    DllStructSetData($tSecurity, "Length", DllStructGetSize($tSecurity))
    DllStructSetData($tSecurity, "InheritHandle", True)

    ; Create a pipe for the child process's STDOUT
    _NamedPipes_CreatePipe($hReadPipe, $hWritePipe, $tSecurity)

    ; Create child process
    $tProcess = DllStructCreate($tagPROCESS_INFORMATION)
    $tStartup = DllStructCreate($tagSTARTUPINFO_New)
    DllStructSetData($tStartup, "Size", DllStructGetSize($tStartup))
    DllStructSetData($tStartup, "Flags", BitOR($STARTF_USESTDHANDLES, $STARTF_USESHOWWINDOW))
    DllStructSetData($tStartup, "StdOutput", $hWritePipe)
    DllStructSetData($tStartup, "StdError", $hWritePipe)
    DllStructSetData($tStartup, "ShowWindow", $state)
    _WinAPI_CreateProcess("", $sCmd, 0, 0, True, 0, 0, $sWorkingDir, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess))
    Local $handle = DllStructGetData($tProcess, "hProcess"), $exitCode
    _WinAPI_CloseHandle(DllStructGetData($tProcess, "hThread"))

    Do
        $exitCode = DllCall("kernel32.dll", "long", "GetExitCodeProcess", "hwnd", $handle, "dword*", 0)
    Until $exitCode[0] <> $STILL_ACTIVE
    $exitCode = $exitCode[2]
    ; Close the write end of the pipe before reading from the read end of the pipe
    _WinAPI_CloseHandle($handle)
    _WinAPI_CloseHandle($hWritePipe)

    ; Read data from the child process
    $tBuffer = DllStructCreate("wchar Text[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    While 1
        _WinAPI_ReadFile($hReadPipe,$pBuffer , 4096, $iBytes)
        If $iBytes = 0 Then ExitLoop
        $sData &= StringLeft(DllStructGetData($tBuffer, "Text"), $iBytes / 2)
    WEnd
    _WinAPI_CloseHandle($hReadPipe)
    SetExtended($exitCode)
    Return $sData
EndFunc   ;==>_RunWaitStdOut

$tagSECURITY_ATTRIBUTES or $tagPROCESS_INFORMATION might be another reason why the function fails, but those look quite simple (and correct imho) compared to $tagSTARTUPINFO.

Edited by KaFu

Share this post


Link to post
Share on other sites

Replacing LPTSTR = ptr with LPTSTR = wstr or LPTSTR = wstr* makes the function fail on x86 too. Thanks for the link to MSDN Windows Data Types definitions, something to bookmark , knew it, but didn't remembered it :).

Edit:

Here's a working x86 example, fails on x64.

#AutoIt3Wrapper_UseX64=n
#include <NamedPipes.au3>
#include <WinAPI.au3>

Global Const $tagSTARTUPINFO_New = "dword Size;ptr Reserved1;ptr Desktop;ptr Title;dword X;dword Y;dword XSize;dword YSize;dword XCountChars;" & _
        "dword YCountChars;dword FillAttribute;dword Flags;word ShowWindow;word Reserved2;ptr Reserved3;handle StdInput;" & _
        "handle StdOutput;handle StdError"


; Example Start
#include <array.au3> ; for example only
$timer = TimerInit()
$aFiles = _FileListToArrayEx(@ProgramFilesDir, "*.*", 0, -1, True, True)
ConsoleWrite(TimerDiff($timer) & @CRLF & $aFiles[0] & " files read" & @CRLF)
;_ArrayDisplay($aFiles)
; Example End

Func _FileListToArrayEx($s_path, $s_mask = "*.*", $i_flag = 0, $s_exclude = -1, $f_recurse = False, $f_full_path = False)
    ; By mfecteau & Ascend4nt, modified by KaFu
    ; http://www.autoitscript.com/forum/index.php?showtopic=33930&st=60&p=799369&#entry799369

    If FileExists($s_path) = 0 Then Return SetError(1, 1, 0)

    ; Strip trailing backslash, and add one after to make sure there's only one
    $s_path = StringRegExpReplace($s_path, "[\\/]+\z", "") & "\"

    ; Set all defaults
    If $s_mask = -1 Or $s_mask = Default Then $s_mask = "*.*"
    If $i_flag = -1 Or $i_flag = Default Then $i_flag = 0
    If $s_exclude = -1 Or $s_exclude = Default Then $s_exclude = ""

    ; Look for bad chars
    If StringRegExp($s_mask, "[/:><\|]") Or StringRegExp($s_exclude, "[/:><\|]") Then
        Return SetError(2, 2, 0)
    EndIf

    ; Strip leading spaces between semi colon delimiter
    $s_mask = StringRegExpReplace($s_mask, "\s*;\s*", ";")
    If $s_exclude Then $s_exclude = StringRegExpReplace($s_exclude, "\s*;\s*", ";")

    ; Confirm mask has something in it
    If StringStripWS($s_mask, 8) = "" Then Return SetError(2, 2, 0)
    If $i_flag < 0 Or $i_flag > 2 Then Return SetError(3, 3, 0)

    ; Validate and create path + mask params
    Local $a_split = StringSplit($s_mask, ";"), $s_hold_split = ""
    For $i = 1 To $a_split[0]
        If StringStripWS($a_split[$i], 8) = "" Then ContinueLoop
        If StringRegExp($a_split[$i], "^\..*?\..*?\z") Then
            $a_split[$i] &= "*" & $a_split[$i]
        EndIf
        $s_hold_split &= '"' & $s_path & $a_split[$i] & '" '
    Next
    $s_hold_split = StringTrimRight($s_hold_split, 1)
    If $s_hold_split = "" Then $s_hold_split = '"' & $s_path & '*.*"'

    Local $i_pid, $s_stdout, $s_hold_out, $s_dir_file_only = "", $s_recurse = "/s "
    If $i_flag = 1 Then $s_dir_file_only = ":-d"
    If $i_flag = 2 Then $s_dir_file_only = ":D"
    If Not $f_recurse Then $s_recurse = ""

    $command = @ComSpec & " /u /c dir /b " & $s_recurse & "/a" & $s_dir_file_only & " " & $s_hold_split
    $s_hold_out = _RunWaitStdOut($command, "", @SW_HIDE)
    ; ConsoleWrite($command & @crlf & $s_hold_out & @crlf & @extended & @crlf)

    $s_hold_out = StringRegExpReplace($s_hold_out, "\v+\z", "")
    If Not $s_hold_out Then Return SetError(4, 4, 0)

    ; Parse data and find matches based on flags
    Local $a_fsplit = StringSplit(StringStripCR($s_hold_out), @LF), $s_hold_ret
    $s_hold_out = ""

    If $s_exclude Then $s_exclude = StringReplace(StringReplace($s_exclude, "*", ".*?"), ";", "|")

    For $i = 1 To $a_fsplit[0]
        If $s_exclude And StringRegExp(StringRegExpReplace( _
                $a_fsplit[$i], "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\Q" & $s_exclude & "\E") Then ContinueLoop
        If StringRegExp($a_fsplit[$i], "^\w:[\\/]+") = 0 Then $a_fsplit[$i] = $s_path & $a_fsplit[$i]
        If $f_full_path Then
            $s_hold_ret &= $a_fsplit[$i] & Chr(1)
        Else
            $s_hold_ret &= StringRegExpReplace($a_fsplit[$i], "((?:.*?[\\/]+)*)(.*?\z)", "$2") & Chr(1)
        EndIf
    Next

    $s_hold_ret = StringTrimRight($s_hold_ret, 1)
    If $s_hold_ret = "" Then Return SetError(5, 5, 0)

    Return StringSplit($s_hold_ret, Chr(1))
EndFunc   ;==>_FileListToArrayEx

; ====================================================================================================
; Execute a command and display the results
; ====================================================================================================
; Paul Campbell (PaulIA), ProgAndy, modified by KaFu
; http://www.autoitscript.com/forum/index.php?showtopic=76607&view=findpost&p=555091

Func _RunWaitStdOut($sCmd, $sWorkingDir = "", $state = @SW_SHOW)

    Local $iBytes, $sData, $hReadPipe, $hWritePipe, $tBuffer, $tProcess, $tSecurity, $tStartup
    Local $STILL_ACTIVE = 0x103
    Local Const $STARTF_USESHOWWINDOW = 0x1
    Local Const $STARTF_USESTDHANDLES = 0x100

    ; Set up security attributes
    $tSecurity = DllStructCreate($tagSECURITY_ATTRIBUTES)
    DllStructSetData($tSecurity, "Length", DllStructGetSize($tSecurity))
    DllStructSetData($tSecurity, "InheritHandle", True)

    ; Create a pipe for the child process's STDOUT
    _NamedPipes_CreatePipe($hReadPipe, $hWritePipe, $tSecurity)

    $tProcess = DllStructCreate($tagPROCESS_INFORMATION)
    $tStartup = DllStructCreate($tagSTARTUPINFO_New)
    ;ConsoleWrite(DllStructGetSize($tStartup) & @crlf)
    DllStructSetData($tStartup, "Size", DllStructGetSize($tStartup))
    DllStructSetData($tStartup, "Flags", BitOR($STARTF_USESTDHANDLES, $STARTF_USESHOWWINDOW))
    DllStructSetData($tStartup, "StdOutput", $hWritePipe)
    DllStructSetData($tStartup, "StdError", $hWritePipe)
    DllStructSetData($tStartup, "ShowWindow", $state)
    _WinAPI_CreateProcess("", $sCmd, 0, 0, True, 0, 0, $sWorkingDir, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess))
    Local $handle = DllStructGetData($tProcess, "hProcess"), $exitCode
    _WinAPI_CloseHandle(DllStructGetData($tProcess, "hThread"))

    Do
        $exitCode = DllCall("kernel32.dll", "long", "GetExitCodeProcess", "hwnd", $handle, "dword*", 0)
    Until $exitCode[0] <> $STILL_ACTIVE
    $exitCode = $exitCode[2]
    ; Close the write end of the pipe before reading from the read end of the pipe
    _WinAPI_CloseHandle($handle)
    _WinAPI_CloseHandle($hWritePipe)

    ; Read data from the child process
    $tBuffer = DllStructCreate("wchar Text[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    While 1
        _WinAPI_ReadFile($hReadPipe, $pBuffer, 4096, $iBytes)
        If $iBytes = 0 Then ExitLoop
        $sData &= StringLeft(DllStructGetData($tBuffer, "Text"), $iBytes / 2)
    WEnd
    _WinAPI_CloseHandle($hReadPipe)
    SetExtended($exitCode)
    Return $sData
EndFunc   ;==>_RunWaitStdOut

$tagSECURITY_ATTRIBUTES or $tagPROCESS_INFORMATION might be another reason why the function fails, but those look quite simple (and correct imho) compared to $tagSTARTUPINFO.

Well, what's the difference between yours $tagSTARTUPINFO_New and original $tagSTARTUPINFO? ;)

There is absolutely nothing wrong with $tagSTARTUPINFO. That's not the reason your code doesn't work.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Well, what's the difference between yours $tagSTARTUPINFO_New and original $tagSTARTUPINFO? ;)

There is absolutely nothing wrong with $tagSTARTUPINFO. That's not the reason your code doesn't work.

No difference, it's a spoon-feed testing example. Well, you said ReadFile fails. Never experienced that that before on x64. My assumption is, that the CreateProcess somehow does not work as intended (pipe contains no data). Looking at the involved structures tagSTARTUPINFO seems to be the most complex, and additionally contains some definitions which I can not match to the MSDN documentation at the moment. So, where else to start?

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

No difference, it's a spoon-feed testing example. Well, you said ReadFile fails. Never experienced that that before on x64. My assumption is, that the CreateProcess somehow does not work as intended (pipe contains no data). Looking at the involved structures tagSTARTUPINFO seems to be the most complex, and additionally contains some definitions which I can not match to the MSDN documentation at the moment. So, where else to start?

I didn't say ReadFile fails. I said your code fails there. Why? I don't know.

Try making $hReadPipe not inheritable.

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

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Any type definition that starts with "LP" is a pointer type. LPTSTR, LPSTR, LPWSTR are all pointers.

Anyway, what's the point of this? The last time I checked AutoIt allowed STDIO redirection so why are you re-inventing the wheel?

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

I didn't say ReadFile fails. I said your code fails there. Why? I don't know.

I know, that's why I was looking at the CreateProcess call.

Try making $hReadPipe not inheritable.

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

Will do so, thanks for the hint ;).

Edit:

Setting this

DllStructSetData($tSecurity, "InheritHandle", False)

makes the function fail even on x86, no win there.

Any type definition that starts with "LP" is a pointer type. LPTSTR, LPSTR, LPWSTR are all pointers.

Thanks for the Info. Then it seems that the structure is correct, there has to be another reason why the function fails on x64.

Anyway, what's the point of this? The last time I checked AutoIt allowed STDIO redirection so why are you re-inventing the wheel?

Well, I took this statement "AutoIt doesn't support Unicode from the console" for granted, I think I tested it some time ago (not sure though). And I want the unicode STDOUT. Edited by KaFu

Share this post


Link to post
Share on other sites

Facepalm. No, Picard Facepalm.

AutoIt doesn't support it because the console doesn't support it. That should be rather obvious if you spent three seconds thinking about it since why would I arbitrarily limit a UNICODE application to ANSI if I didn't absolutely have to?

Share this post


Link to post
Share on other sites

Because you didn't know of the undocumented cmd.exe /u switch? Give the example in the first post a try, works fine on x86... btw, I think I found a hint, might have to do with x64 redirection, CreateProcess says it can't find the file (cmd.exe)...

Share this post


Link to post
Share on other sites

Because you didn't know of the undocumented cmd.exe /u switch?

Terminology failure on my part. The API's don't support it. Okay, so cmd.exe has an undocumented option that sends UNICODE bytes through a byte stream on the off chance the program reading the stream knows to interpret it that way. Fair enough. Perhaps you should have said similar from the start.

Not sure why this matters. Pretty sure file listing functions can be implemented natively in AutoIt. At least they could the last time I checked. Unless this is one of those silly "Let's jump through a bunch of hoops because AutoIt is too slow" type of things.

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

btw, I think I found a hint, might have to do with x64 redirection, CreateProcess says it can't find the file (cmd.exe)...

That's not it either. Your script from the above slightly modified to show that CreateProcess is ok:
;#AutoIt3Wrapper_UseX64=n
#include <NamedPipes.au3>
#include <WinAPI.au3>


; Example Start
#include <array.au3> ; for example only
$timer = TimerInit()
$aFiles = _FileListToArrayEx(@ProgramFilesDir, "*.*", 0, -1, True, True)
ConsoleWrite(TimerDiff($timer) & @CRLF & $aFiles[0] & " files read" & @CRLF)
;_ArrayDisplay($aFiles)
; Example End

Func _FileListToArrayEx($s_path, $s_mask = "*.*", $i_flag = 0, $s_exclude = -1, $f_recurse = False, $f_full_path = False)
    ; By mfecteau & Ascend4nt, modified by KaFu
    ; http://www.autoitscript.com/forum/index.php?showtopic=33930&st=60&p=799369&#entry799369

    If FileExists($s_path) = 0 Then Return SetError(1, 1, 0)

    ; Strip trailing backslash, and add one after to make sure there's only one
    $s_path = StringRegExpReplace($s_path, "[\\/]+\z", "") & "\"

    ; Set all defaults
    If $s_mask = -1 Or $s_mask = Default Then $s_mask = "*.*"
    If $i_flag = -1 Or $i_flag = Default Then $i_flag = 0
    If $s_exclude = -1 Or $s_exclude = Default Then $s_exclude = ""

    ; Look for bad chars
    If StringRegExp($s_mask, "[/:><\|]") Or StringRegExp($s_exclude, "[/:><\|]") Then
        Return SetError(2, 2, 0)
    EndIf

    ; Strip leading spaces between semi colon delimiter
    $s_mask = StringRegExpReplace($s_mask, "\s*;\s*", ";")
    If $s_exclude Then $s_exclude = StringRegExpReplace($s_exclude, "\s*;\s*", ";")

    ; Confirm mask has something in it
    If StringStripWS($s_mask, 8) = "" Then Return SetError(2, 2, 0)
    If $i_flag < 0 Or $i_flag > 2 Then Return SetError(3, 3, 0)

    ; Validate and create path + mask params
    Local $a_split = StringSplit($s_mask, ";"), $s_hold_split = ""
    For $i = 1 To $a_split[0]
        If StringStripWS($a_split[$i], 8) = "" Then ContinueLoop
        If StringRegExp($a_split[$i], "^\..*?\..*?\z") Then
            $a_split[$i] &= "*" & $a_split[$i]
        EndIf
        $s_hold_split &= '"' & $s_path & $a_split[$i] & '" '
    Next
    $s_hold_split = StringTrimRight($s_hold_split, 1)
    If $s_hold_split = "" Then $s_hold_split = '"' & $s_path & '*.*"'

    Local $i_pid, $s_stdout, $s_hold_out, $s_dir_file_only = "", $s_recurse = "/s "
    If $i_flag = 1 Then $s_dir_file_only = ":-d"
    If $i_flag = 2 Then $s_dir_file_only = ":D"
    If Not $f_recurse Then $s_recurse = ""

    $command = @ComSpec & " /u /c dir /b " & $s_recurse & "/a" & $s_dir_file_only & " " & $s_hold_split
    $s_hold_out = _RunWaitStdOut($command, "", @SW_HIDE)
    ; ConsoleWrite($command & @crlf & $s_hold_out & @crlf & @extended & @crlf)

    $s_hold_out = StringRegExpReplace($s_hold_out, "\v+\z", "")
    If Not $s_hold_out Then Return SetError(4, 4, 0)

    ; Parse data and find matches based on flags
    Local $a_fsplit = StringSplit(StringStripCR($s_hold_out), @LF), $s_hold_ret
    $s_hold_out = ""

    If $s_exclude Then $s_exclude = StringReplace(StringReplace($s_exclude, "*", ".*?"), ";", "|")

    For $i = 1 To $a_fsplit[0]
        If $s_exclude And StringRegExp(StringRegExpReplace( _
                $a_fsplit[$i], "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\Q" & $s_exclude & "\E") Then ContinueLoop
        If StringRegExp($a_fsplit[$i], "^\w:[\\/]+") = 0 Then $a_fsplit[$i] = $s_path & $a_fsplit[$i]
        If $f_full_path Then
            $s_hold_ret &= $a_fsplit[$i] & Chr(1)
        Else
            $s_hold_ret &= StringRegExpReplace($a_fsplit[$i], "((?:.*?[\\/]+)*)(.*?\z)", "$2") & Chr(1)
        EndIf
    Next

    $s_hold_ret = StringTrimRight($s_hold_ret, 1)
    If $s_hold_ret = "" Then Return SetError(5, 5, 0)

    Return StringSplit($s_hold_ret, Chr(1))
EndFunc   ;==>_FileListToArrayEx

; ====================================================================================================
; Execute a command and display the results
; ====================================================================================================
; Paul Campbell (PaulIA), ProgAndy, modified by KaFu
; http://www.autoitscript.com/forum/index.php?showtopic=76607&view=findpost&p=555091

Func _RunWaitStdOut($sCmd, $sWorkingDir = "", $state = @SW_SHOW)

    Local $iBytes, $sData, $hReadPipe, $hWritePipe, $tBuffer, $tProcess, $tSecurity, $tStartup
    Local $STILL_ACTIVE = 0x103
    Local Const $STARTF_USESHOWWINDOW = 0x1
    Local Const $STARTF_USESTDHANDLES = 0x100

    ; Set up security attributes
    $tSecurity = DllStructCreate($tagSECURITY_ATTRIBUTES)
    DllStructSetData($tSecurity, "Length", DllStructGetSize($tSecurity))
    DllStructSetData($tSecurity, "InheritHandle", True)

    ; Create a pipe for the child process's STDOUT
    _NamedPipes_CreatePipe($hReadPipe, $hWritePipe, $tSecurity)

    $tProcess = DllStructCreate($tagPROCESS_INFORMATION)
    $tStartup = DllStructCreate($tagSTARTUPINFO)
    DllStructSetData($tStartup, "Size", DllStructGetSize($tStartup))
    ;DllStructSetData($tStartup, "Flags", BitOR($STARTF_USESTDHANDLES, $STARTF_USESHOWWINDOW)) ;<-!!! Here, to see
    DllStructSetData($tStartup, "StdOutput", $hWritePipe)
    DllStructSetData($tStartup, "StdError", $hWritePipe)
    DllStructSetData($tStartup, "ShowWindow", $state)
    _WinAPI_CreateProcess("", $sCmd, 0, 0, True, 0, 0, $sWorkingDir, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess))

    Exit ; The End for this demo


    Local $handle = DllStructGetData($tProcess, "hProcess"), $exitCode
    _WinAPI_CloseHandle(DllStructGetData($tProcess, "hThread"))

    Do
        $exitCode = DllCall("kernel32.dll", "long", "GetExitCodeProcess", "hwnd", $handle, "dword*", 0)
    Until $exitCode[0] <> $STILL_ACTIVE
    $exitCode = $exitCode[2]
    ; Close the write end of the pipe before reading from the read end of the pipe
    _WinAPI_CloseHandle($handle)
    _WinAPI_CloseHandle($hWritePipe)

    ; Read data from the child process
    $tBuffer = DllStructCreate("wchar Text[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    While 1
        _WinAPI_ReadFile($hReadPipe, $pBuffer, 4096, $iBytes)
        If $iBytes = 0 Then ExitLoop
        $sData &= StringLeft(DllStructGetData($tBuffer, "Text"), $iBytes / 2)
    WEnd
    _WinAPI_CloseHandle($hReadPipe)
    SetExtended($exitCode)
    Return $sData
EndFunc   ;==>_RunWaitStdOut

edit: since you didn't listen to me... let me fix it for you:

;#AutoIt3Wrapper_UseX64=n
#include <NamedPipes.au3>
#include <WinAPI.au3>



; Example Start
#include <array.au3> ; for example only
$timer = TimerInit()
$aFiles = _FileListToArrayEx(@ProgramFilesDir, "*.*", 0, -1, True, True)
ConsoleWrite(TimerDiff($timer) & @CRLF & $aFiles[0] & " files read" & @CRLF)
_ArrayDisplay($aFiles)
; Example End

;===============================================================================
;
; Description:    lists all or preferred files and or folders in a specified path (Similar to using Dir with the /B Switch)
; Syntax:          _FileListToArrayEx($sPath, $sFilter = '*.*', $iFlag = 0, $sExclude = '')
; Parameter(s):     $sPath = Path to generate filelist for
;                   $sFilter = The filter to use. Search the Autoit3 manual for the word "WildCards" For details, support now for multiple searches
;                           Example *.exe; *.txt will find all .exe and .txt files
;                  $iFlag = determines weather to return file or folders or both.
;                   $sExclude = exclude a file from the list by all or part of its name
;                           Example: Unins* will remove all files/folders that start with Unins
;                       $iFlag=0(Default) Return both files and folders
;                      $iFlag=1 Return files Only
;                       $iFlag=2 Return Folders Only
;
; Requirement(s):   None
; Return Value(s):  On Success - Returns an array containing the list of files and folders in the specified path
;                       On Failure - Returns the an empty string "" if no files are found and sets @Error on errors
;                       @Error or @extended = 1 Path not found or invalid
;                       @Error or @extended = 2 Invalid $sFilter or Invalid $sExclude
;                      @Error or @extended = 3 Invalid $iFlag
;                       @Error or @extended = 4 No File(s) Found
;
; Author(s):        SmOke_N, modified by mfecteau, Ascend4nt & KaFu
;                   http://www.autoitscript.com/forum/index.php?showtopic=33930&view=findpost&p=799369
; Note(s):          The array returned is one-dimensional and is made up as follows:
;                   $array[0] = Number of Files\Folders returned
;                   $array[1] = 1st File\Folder
;                   $array[2] = 2nd File\Folder
;                   $array[3] = 3rd File\Folder
;                   $array[n] = nth File\Folder
;
;                   All files are written to a "reserved" .tmp file (Thanks to gafrost) for the example
;                   The Reserved file is then read into an array, then deleted
;===============================================================================
Func _FileListToArrayEx($s_path, $s_mask = "*.*", $i_flag = 0, $s_exclude = -1, $f_recurse = False, $f_full_path = False)

    If FileExists($s_path) = 0 Then Return SetError(1, 1, 0)

    ; Strip trailing backslash, and add one after to make sure there's only one
    $s_path = StringRegExpReplace($s_path, "[\\/]+\z", "") & "\"

    ; Set all defaults
    If $s_mask = -1 Or $s_mask = Default Then $s_mask = "*.*"
    If $i_flag = -1 Or $i_flag = Default Then $i_flag = 0
    If $s_exclude = -1 Or $s_exclude = Default Then $s_exclude = ""

    ; Look for bad chars
    If StringRegExp($s_mask, "[/:><\|]") Or StringRegExp($s_exclude, "[/:><\|]") Then
        Return SetError(2, 2, 0)
    EndIf

    ; Strip leading spaces between semi colon delimiter
    $s_mask = StringRegExpReplace($s_mask, "\s*;\s*", ";")
    If $s_exclude Then $s_exclude = StringRegExpReplace($s_exclude, "\s*;\s*", ";")

    ; Confirm mask has something in it
    If StringStripWS($s_mask, 8) = "" Then Return SetError(2, 2, 0)
    If $i_flag < 0 Or $i_flag > 2 Then Return SetError(3, 3, 0)

    ; Validate and create path + mask params
    Local $a_split = StringSplit($s_mask, ";"), $s_hold_split = ""
    For $i = 1 To $a_split[0]
        If StringStripWS($a_split[$i], 8) = "" Then ContinueLoop
        If StringRegExp($a_split[$i], "^\..*?\..*?\z") Then
            $a_split[$i] &= "*" & $a_split[$i]
        EndIf
        $s_hold_split &= '"' & $s_path & $a_split[$i] & '" '
    Next
    $s_hold_split = StringTrimRight($s_hold_split, 1)
    If $s_hold_split = "" Then $s_hold_split = '"' & $s_path & '*.*"'

    Local $i_pid, $s_stdout, $s_hold_out, $s_dir_file_only = "", $s_recurse = "/s "
    If $i_flag = 1 Then $s_dir_file_only = ":-d"
    If $i_flag = 2 Then $s_dir_file_only = ":D"
    If Not $f_recurse Then $s_recurse = ""

    $command = @ComSpec & " /u /c dir /b " & $s_recurse & "/a" & $s_dir_file_only & " " & $s_hold_split
    $s_hold_out = _RunWaitStdOut($command, "", @SW_HIDE)
    ; ConsoleWrite($command & @crlf & $s_hold_out & @crlf & @extended & @crlf)

    $s_hold_out = StringRegExpReplace($s_hold_out, "\v+\z", "")
    If Not $s_hold_out Then Return SetError(4, 4, 0)

    ; Parse data and find matches based on flags
    Local $a_fsplit = StringSplit(StringStripCR($s_hold_out), @LF), $s_hold_ret
    $s_hold_out = ""

    If $s_exclude Then $s_exclude = StringReplace(StringReplace($s_exclude, "*", ".*?"), ";", "|")

    For $i = 1 To $a_fsplit[0]
        If $s_exclude And StringRegExp(StringRegExpReplace( _
                $a_fsplit[$i], "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\Q" & $s_exclude & "\E") Then ContinueLoop
        If StringRegExp($a_fsplit[$i], "^\w:[\\/]+") = 0 Then $a_fsplit[$i] = $s_path & $a_fsplit[$i]
        If $f_full_path Then
            $s_hold_ret &= $a_fsplit[$i] & Chr(1)
        Else
            $s_hold_ret &= StringRegExpReplace($a_fsplit[$i], "((?:.*?[\\/]+)*)(.*?\z)", "$2") & Chr(1)
        EndIf
    Next

    $s_hold_ret = StringTrimRight($s_hold_ret, 1)
    If $s_hold_ret = "" Then Return SetError(5, 5, 0)

    Return StringSplit($s_hold_ret, Chr(1))
EndFunc   ;==>_FileListToArrayEx

; ====================================================================================================
; Execute a command and display the results
; ====================================================================================================
; Paul Campbell (PaulIA), ProgAndy, modified by KaFu
; http://www.autoitscript.com/forum/index.php?showtopic=76607&view=findpost&p=555091

Func _RunWaitStdOut($sCmd, $sWorkingDir = "", $state = @SW_SHOW)

    Local $iBytes, $sData, $hReadPipe, $hWritePipe, $tBuffer, $tProcess, $tSecurity, $tStartup
    Local $STILL_ACTIVE = 0x103
    Local Const $STARTF_USESHOWWINDOW = 0x1
    Local Const $STARTF_USESTDHANDLES = 0x100

    ; Set up security attributes
;~     $tSecurity = DllStructCreate($tagSECURITY_ATTRIBUTES)
;~     DllStructSetData($tSecurity, "Length", DllStructGetSize($tSecurity))
;~     DllStructSetData($tSecurity, "InheritHandle", True)

    ; Create a pipe for the child process's STDOUT
    _NamedPipes_CreatePipe($hReadPipe, $hWritePipe);, $tSecurity)

    ;**************
    _WinAPI_SetHandleInformation($hReadPipe, 1, 0) ; redundant in this new situation
    _WinAPI_SetHandleInformation($hWritePipe, 1, 1)
    ;**************

    ; Create child process
    $tProcess = DllStructCreate($tagPROCESS_INFORMATION)
    $tStartup = DllStructCreate($tagSTARTUPINFO)
    DllStructSetData($tStartup, "Size", DllStructGetSize($tStartup))
    DllStructSetData($tStartup, "Flags", BitOR($STARTF_USESTDHANDLES, $STARTF_USESHOWWINDOW))
    DllStructSetData($tStartup, "StdOutput", $hWritePipe)
    DllStructSetData($tStartup, "StdError", $hWritePipe)
    DllStructSetData($tStartup, "ShowWindow", $state)
    _WinAPI_CreateProcess("", $sCmd, 0, 0, True, 0, 0, $sWorkingDir, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess))
    Local $handle = DllStructGetData($tProcess, "hProcess"), $exitCode
    _WinAPI_CloseHandle(DllStructGetData($tProcess, "hThread"))

    Do
        $exitCode = DllCall("kernel32.dll", "long", "GetExitCodeProcess", "hwnd", $handle, "dword*", 0)
    Until $exitCode[0] <> $STILL_ACTIVE
    $exitCode = $exitCode[2]
    ; Close the write end of the pipe before reading from the read end of the pipe
    _WinAPI_CloseHandle($handle)
    _WinAPI_CloseHandle($hWritePipe)

    ; Read data from the child process
    $tBuffer = DllStructCreate("wchar Text[4096]")
    $pBuffer = DllStructGetPtr($tBuffer)
    While 1
        _WinAPI_ReadFile($hReadPipe, $pBuffer, 4096, $iBytes)
        If $iBytes = 0 Then ExitLoop
        $sData &= StringLeft(DllStructGetData($tBuffer, "Text"), $iBytes / 2)
    WEnd
    _WinAPI_CloseHandle($hReadPipe)
    SetExtended($exitCode)
    Return $sData
EndFunc   ;==>_RunWaitStdOut
Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Just so everyone is clear, the '/u' switch is documented, and has been in effect since Windows 2000. If you need assistance in using it, here's the command-line help output:

/U  Causes the output of internal commands to a pipe or file to be Unicode

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

since you didn't listen to me... let me fix it for you

Nice ;), thanks a lot, seems to work fine now ;) ...

Just so everyone is clear, the '/u' switch is documented, and has been in effect since Windows 2000.

Upsa, overlooked that, good to know m8 :) Edited by KaFu

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