Sign in to follow this  
Followers 0
Chimaera

Run / RunWait and Exit Codes

29 posts in this topic

Just a small question for the learned masses if i may

I have a script that uses command line to access Winrar and MultiPar and ive run into this scenario

I currently use RunWait like this

$ProcessPID = RunWait(@ComSpec & ' /c ' & @TempDir & '\Sup\par2j.exe' & ' r ' & '"' & $aParFile[$i] & '"', "", @SW_HIDE)

and then i access $ProcessPID for all the exit codes of which there are many to provide error support for all the command programs i am using.

So i made all the script, then i realised that i couldn't cancel the process and further investigation it looks like i will have to completely rewrite to be able to halt the process

and use Run to be able to do it.

The downside is i lose all that error support built in to the programs as i cant access it from Run

and only give basic support like this

                ProcessWait($ProcessPID)
                $sOutput = ""
                While 1
                    $sOutput = StdoutRead($ProcessPID)
                    If StringInStr($sOutput, "Password", 2) > 0 Then ; Monitoring Window for specific text during test process
                        MsgBox(64, "Password Error", "Archive Is Passworded")
                        Exit
                    ElseIf StringInStr($sOutput, "No files extracted", 2) > 0 Then ; Monitoring Window for specific text during test process
                        MsgBox(64, "Archive Error", "Archive Extraction Failed")
                        Exit
                    EndIf
                WEnd

This is a shame because the support is already there and i cant access it.

So the question i would like to ask is..

Can Run be made to return the $PID and if you set a flag then it returns the exit codes from the programs?

There probably is a horrendously complicated answer but i would interested to hear why or why not.

Thanks for your time

Share this post


Link to post
Share on other sites



You call the DOS environment and let it run par2j.exe. The return value you get is form DOS not from par2j.exe

Can't you directly call par2j?

$ProcessPID = RunWait(@TempDir & '\Sup\par2j.exe' & ' r ' & '"' & $aParFile[$i] & '"', "", @SW_HIDE)

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

Search OpenProcess in the Forum, that's where I would start.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

How could Run return the error code if the process hasn't finished yet?

Share this post


Link to post
Share on other sites

I think you can't get the return code of the started program AND have the ability to cancel it whenever you need to.

Doesn't each program give sufficient error information to the console? Use Run, grab all error messages sent to StdOut and kill the process of needed.


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

I found >this thread while searching for information about this that might be what you're looking for.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

As part of a project I didn't want to deal with this issue, so I wrote a few functions to make it easier. One wraps the full CreateProcess API, the other checks the existence of the process by the handle returned from the first function. If the process has ended, it returns the exit code in @extended.

#include <WinAPI.au3>
#include <StructureConstants.au3>
#include <WindowsConstants.au3>

; #FUNCTION# ====================================================================================================================
; Name ..........: _Parallel_CreateProcess
; Description ...: Launch a new process.
; Syntax ........: _Parallel_CreateProcess($sCommand, $sWrkDir, $iShow, $iOpts)
; Parameters ....: $sCommand            - Command line including parameters.
;                  $sWrkDir             - Working directory. Default = "".
;                  $iShow               - Initial state of any application window, any of the @SW_ macros.
;                  $iOpts               - Options 0x10 or 0x10000 from internal Run() command, BitOR'd.
; Return values .: Success - A two element array:
;                           [0] - PID
;                           [1] - process handle (MUST be closed when finished with it)
;                  Failure - Sets @error = 1 and returns 0 as PID and handle
; Author ........: Erik Pilsits
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _Parallel_CreateProcess($sCommand, $sWrkDir, $iShow, $iOpts)
    ; use options provided by internal Run() command for consistent usage
    ; but only supports 0x10 and 0x10000
    ;
    Local $aRet[2] = [0, 0]
    Local $sSTARTUP = DllStructCreate($tagSTARTUPINFO)
    Local $sPROCINFO = DllStructCreate($tagPROCESS_INFORMATION)
    DllStructSetData($sSTARTUP, "Size", DllStructGetSize($sSTARTUP))
    DllStructSetData($sSTARTUP, "Flags", $STARTF_USESHOWWINDOW)
    DllStructSetData($sSTARTUP, "ShowWindow", $iShow)
    ;
    ; check options
    If BitAND($iOpts, 0x10) = 0x10 Then
        ; inherit parent stdio handles
        DllStructSetData($sSTARTUP, "Flags", BitOR($STARTF_USESHOWWINDOW, $STARTF_USESTDHANDLES))
        DllStructSetData($sSTARTUP, "StdInput", _WinAPI_GetStdHandle(0))
        DllStructSetData($sSTARTUP, "StdOutput", _WinAPI_GetStdHandle(1))
        DllStructSetData($sSTARTUP, "StdError", _WinAPI_GetStdHandle(2))
    EndIf
    Local $dwCreationFlags = 0
    If BitAND($iOpts, 0x10000) = 0x10000 Then
        ; CREATE_NEW_CONSOLE = 0x10
        $dwCreationFlags = 0x10
    EndIf
    ; check working dir
    Local $sWrkDirType = "wstr"
    If $sWrkDir = "" Then
        $sWrkDirType = "ptr"
        $sWrkDir = 0
    EndIf
    ;
    Local $ret = DllCall("kernel32.dll", "bool", "CreateProcessW", "ptr", 0, "wstr", $sCommand, "ptr", 0, "ptr", 0, "bool", 0, _
            "dword", $dwCreationFlags, "ptr", 0, $sWrkDirType, $sWrkDir, "struct*", $sSTARTUP, "struct*", $sPROCINFO)
    If @error Or Not $ret[0] Then Return SetError(1, 0, $aRet)
    ;
    ; close unused thread handle
    _WinAPI_CloseHandle(DllStructGetData($sPROCINFO, "hThread"))
    ;
    ; return array of info, [0] = PID, [1] = process handle
    $aRet[0] = DllStructGetData($sPROCINFO, "ProcessID")
    $aRet[1] = DllStructGetData($sPROCINFO, "hProcess")
    Return SetError(0, 0, $aRet)
EndFunc   ;==>_Parallel_CreateProcess

; #FUNCTION# ====================================================================================================================
; Name ..........: _ProcessExistsByHandle
; Description ...: Check if a process exists by handle.
; Syntax ........: _ProcessExistsByHandle($hProc)
; Parameters ....: $hProc               - Open handle to a process, for example from a call to OpenProcess.
; Return values .: Success - 0 if process has ended, 1 if it still exists
;                  @extended contains the exit code
;                  Failure - 0 and sets @error
; Author ........: Erik Pilsits
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _ProcessExistsByHandle($hProc)
    ; there was an error creating the process
    If Not $hProc Then Return SetError(1, 0, 0)
    ;
    Local $iExit = DllCall("kernel32.dll", "bool", "GetExitCodeProcess", "handle", $hProc, "dword*", -1)
    If @error Or (Not $iExit[0]) Then
        ; error
        Return SetError(1, 0, 0)
    ElseIf $iExit[2] = 259 Then
        ; process STILL_ACTIVE
        ; extra check if process used STILL_ACTIVE as a valid exit code, $WAIT_OBJECT_0
        If _WinAPI_WaitForSingleObject($hProc, 0) = 0 Then
            Return SetExtended($iExit[2], 0)
        Else
            Return 1
        EndIf
    Else
        ; process does not exist, return exit code in @extended
        Return SetExtended($iExit[2], 0)
    EndIf
EndFunc   ;==>_ProcessExistsByHandle
Edited by wraithdu

Share this post


Link to post
Share on other sites

Nice couple of functions wraiithdu.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Scite4AutoIt3 has been using Run to return the exitcode for several years. The quickest way for me to get the UDFs for my own use is to search Jos' sources rather then my own as I know where they exist.

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Scite4AutoIt3 has been using Run to return the exitcode for several years.

Have a look at AutoIt3Wrapper.au3 for any of these sections:Tidy, Obfuscator, AU3check or AutoIt3.

Some of it was originally written by MHZ. :)

Jos

Edited by Jos

Visit the SciTE4AutoIt3 Download page for the latest versions        Beta files                                                          Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites

 

You call the DOS environment and let it run par2j.exe. The return value you get is form DOS not from par2j.exe

Can't you directly call par2j?

$ProcessPID = RunWait(@TempDir & '\Sup\par2j.exe' & ' r ' & '"' & $aParFile[$i] & '"', "", @SW_HIDE)

Yet it still gives me the return codes everytime?

Ill have a look later thx

I'm quite sure I've seen a script which obtained the exit code from PID.

Unfortunately I cannot remember the topic.

I found the one below from BrewManNH just after i saw your post, thx to both

I found >this thread while searching for information about this that might be what you're looking for.

 

 

As part of a project I didn't want to deal with this issue, so I wrote a few functions to make it easier. One wraps the full CreateProcess API, the other checks the existence of the process by the handle returned from the first function. If the process has ended, it returns the exit code in @extended.

; #FUNCTION# ====================================================================================================================
; Name ..........: _ProcessExistsByHandle
; Description ...: Check if a process exists by handle.
; Syntax ........: _ProcessExistsByHandle($hProc)
; Parameters ....: $hProc               - Open handle to a process, for example from a call to OpenProcess.
; Return values .: Success - 0 if process has ended, 1 if it still exists
;                  @extended contains the exit code
;                  Failure - 0 and sets @error
; Author ........: Erik Pilsits
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _ProcessExistsByHandle($hProc)
    ; there was an error creating the process
    If Not $hProc Then Return SetError(1, 0, 0)
    ;
    Local $iExit = DllCall("kernel32.dll", "bool", "GetExitCodeProcess", "handle", $hProc, "dword*", -1)
    If @error Or (Not $iExit[0]) Then
        ; error
        Return SetError(1, 0, 0)
    ElseIf $iExit[2] = 259 Then
        ; process STILL_ACTIVE
        Return 1
    Else
        ; process does not exist, return exit code in @extended
        Return SetExtended($iExit[2], 0)
    EndIf
EndFunc   ;==>_ProcessExistsByHandle

 Ill have a play with this as well and see what i can come up with thx for sharing.

@Jos and Mhz i had a look at the suggested file and can see the parts you mentioned so ill read them in a bit thx

The bit i am curious about is why it was never included with Run?

Was there some major thing against it being added ?

Just curious

Share this post


Link to post
Share on other sites

The bit i am curious about is why it was never included with Run?

Was there some major thing against it being added ?

Just curious

Think about this a little longer and come back when you still can't figure out why Run can't supply the ReturnCode at return time of the function.  ;)


Visit the SciTE4AutoIt3 Download page for the latest versions        Beta files                                                          Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites

Out of curiousity I did the following test:

Compiled the following script to C.temptest.exe

Exit 12345

If you then run

$ReturnValue = RunWait(@ComSpec & ' /c ' & 'C:\temp\test.exe', "", @SW_HIDE)
MsgBox(0, "ComSpec: Return Value", $ReturnValue)
$ReturnValue = RunWait('C:\temp\test.exe', "", @SW_HIDE)
MsgBox(0, "Run: Return Value", $ReturnValue)

you will get 12345 both times.


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

I was hoping for more, but this is what SS64 has to say about it

ErrorLevel

CMD /C will return an errorlevel, for example CMD /c dir Z: where the drive Z: does not exist, will return %errorlevel% = 1 to the calling CMD shell.

Edited by wraithdu

Share this post


Link to post
Share on other sites

My two cents:

#include <WinAPI.au3>

Global Const $PROCESS_ALL_ACCESS = 0x1F0FFF

Global $iPID = Run("test.exe")
ConsoleWrite("PID: " & $iPID & @LF)
Global $hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 1, $iPID)
ConsoleWrite("Process handle: " & $hProcess & @LF)
ProcessWaitClose($iPID)
Global $aRet = DllCall("kernel32.dll", "bool", "GetExitCodeProcess", "HANDLE", $hProcess, "dword*", -1)
ConsoleWrite("Exit code: " & $aRet[2] & @LF)

 

BTW: The only way how Run() could provide the exit code is through a callback function I think. But this is really not necessary.


Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Share this post


Link to post
Share on other sites

The reason I disagree with the above and similar methods used in AutoIt3Wrapper.... it hinges on the fact that you can open that handle before the process has ended. What play a racing game? If the process ends before you can get that handle, your exit code is lost. If you do it my way (and I'm not saying my way is best), you take the chance out of it and you will always get your exit code.

Those are my 2 cents.

Share this post


Link to post
Share on other sites

@funkey

Good except you are missing a UDF call to finish the task. Have a look at the examples in the link that BrewManNH posted at >#7 .

@wraithdu

I cannot disagree with your statement about your code. It is rather interesting code that you created and perhaps a good alternative for some.

The mention of a race condition is that the UDFs used in Scite4AutoIt3 or used elsewhere have shown no signs of failure over the years. Perhaps with AutoIt already running at speed can run a process and open the handle to the process while the process is just getting setup to execute. There is some overhead in starting a process like reading the information in the header for how to execute, where to start reading the file to the end and add to the memory, etc. The CPU cycles of running a process and opening the process handle is possibly far few CPU cycles then what it takes to run a process and get to the point of execution. Yeah, it is just theory, though if no signs of failure exist then it can only be treated as a success.

Does your process creation by UDF have enough advantage for users to not use the inbuilt function Run()? Users like convenience and like stuff built in and features added to enhance what already exists. For example, I would expect a UDF called _Run() would always come 2nd in a race to Run() based on choice.

Another thing to consider is that WinAPI* functions already exist to open the process handle and to close the process handle. The function to get the exit code exists in WinAPIEx* and may be added as a STD UDF at some point. The functions that I have posted will become obsolete as the WinAPI* functions increase in number to include what is missing. This is what I see as the future.

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

One thing to keep in mind is that when AutoIt's Run() is used with STDOUT/ERR redirected is that internally AutoIt has to already have a process handle open, and can only close it once the entire redirected I/O has been read.  AutoItWrapper has some calls with redirects like that, and its fine to get the Process handle before the first read from those pipes.  Getting a process handle after any StdOut/ErrRead call is suspect, at best.  And getting it after Run() without a redirect is definitely a toss-up.

By the way, wraithdu - ProcessExistsByHandle() mistakenly assumes that any STILL_ACTIVE return code means the process is still running, whereas that can be a valid exit code.  It needs to use WaitForSingleObject() - preferrably with a 0-ms wait instead.

*edit: - strikethrough wrong comments - see >post #22

Edited by Ascend4nt

Share this post


Link to post
Share on other sites

#20 ·  Posted (edited)

The mention of a race condition is that the UDFs used in Scite4AutoIt3 or used elsewhere have shown no signs of failure over the years. Perhaps with AutoIt already running at speed can run a process and open the handle to the process while the process is just getting setup to execute. There is some overhead in starting a process like reading the information in the header for how to execute, where to start reading the file to the end and add to the memory, etc. The CPU cycles of running a process and opening the process handle is possibly far few CPU cycles then what it takes to run a process and get to the point of execution. Yeah, it is just theory, though if no signs of failure exist then it can only be treated as a success.

Does your process creation by UDF have enough advantage for users to not use the inbuilt function Run()? Users like convenience and like stuff built in and features added to enhance what already exists. For example, I would expect a UDF called _Run() would always come 2nd in a race to Run() based on choice.

 

You can rationalize the race condition all you want, but race conditions need to be avoided. Period. It's bad programming.

My UDF is not, by any means, meant as a replacement for Run() / RunWait() except for the specific purpose of this topic, to guarantee the return of a valid process handle and exit code.

One thing to keep in mind is that when AutoIt's Run() is used with STDOUT/ERR redirected is that internally AutoIt has to already have a process handle open, and can only close it once the entire redirected I/O has been read.  AutoItWrapper has some calls with redirects like that, and its fine to get the Process handle before the first read from those pipes.  Getting a process handle after any StdOut/ErrRead call is suspect, at best.  And getting it after Run() without a redirect is definitely a toss-up.

By the way, wraithdu - ProcessExistsByHandle() mistakenly assumes that any STILL_ACTIVE return code means the process is still running, whereas that can be a valid exit code.  It needs to use WaitForSingleObject() - preferrably with a 0-ms wait instead.

 

Your first point is interesting. We'd definitely need a dev's input on that. Would that mean that AutoIt indefinitely holds an open process handle when std output is redirected, until StdioClose() is manually called or all output has been read by the Std*Read() functions?

Second point... my function does not mistakenly assume that at all. It makes the assumption with full knowledge and understanding. I'll refer you to the remarks for GetExitCodeProcess in MSDN:

Important  The GetExitCodeProcess function returns a valid error code defined by the application only after the thread terminates. Therefore, an application should not use STILL_ACTIVE (259) as an error code. If a thread returnsSTILL_ACTIVE (259) as an error code, applications that test for this value could interpret it to mean that the thread is still running and continue to test for the completion of the thread after the thread has terminated, which could put the application into an infinite loop.

Edited by wraithdu

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