Jump to content

STDOutRead / Logging: Missing some stuff occasionally


Recommended Posts

Hi there,

I want to have some sort of tail in a GUI.

To accomplish this, I have created a little script.

Here's a quick and dirty conversion of it.

Note that you will have to press exit to close the app.

SpecialEvents don't seem to work, but I don't care.

As mentioned, this is a small part of another project.

You may also replace the /c with a /k switch to work with cmd via the input-box.

; *** Start added by AutoIt3Wrapper ***
#include <Constants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
; *** End added by AutoIt3Wrapper ***


Global $tLog=@ScriptDir&'\log.txt', $POpen=1, $tLine='', $tOutput='', $Stream
Global $pGui, $pButton1, $pButton2, $pButton3, $pEdit1, $pProgress1, $pInput1, $pLabel1, $pLabel2

_Process_Gui()
_Process_Run(@ComSpec & ' /c dir '&@SystemDir, @ScriptDir) 
While $POpen
    Sleep(100)
WEnd    
    

; ---------------------------------------------------------------------------------------------
; Create a small gui that will be used to monitor the progess
; ---------------------------------------------------------------------------------------------
Func _Process_Gui($a=0)
    AutoItSetOption('GUIOnEventMode', 1)
    $pGui = GUICreate('Test', 550, 410, 100)
    GUISetFont(8, 400, 0, "MS Sans Serif")
    GUICtrlSetOnEvent ($GUI_EVENT_CLOSE, '_Process_OnEvent')
    GUICtrlSetOnEvent ($GUI_EVENT_MINIMIZE, '_Process_OnEvent')
    GUICtrlSetOnEvent ($GUI_EVENT_RESTORE, '_Process_OnEvent')
    $pLabel1 = GUICtrlCreateLabel('Watch this', 35, 35, 380, 20)
    $pProgress1 = GUICtrlCreateProgress(35, 55, 480, 20) 
    $pLabel2 = GUICtrlCreateLabel('', 35, 85, 380, 20)
    If $a=0 Then
        $pEdit1 = GUICtrlCreateEdit('', 35, 115, 480, 210, $ES_READONLY+$WS_HSCROLL)
    Else
        $pEdit1 = GUICtrlCreateEdit('', 35, 115, 480, 210, $ES_READONLY+$WS_HSCROLL+$WS_VSCROLL+$ES_AUTOVSCROLL)
    EndIf
    $pInput1 = GUICtrlCreateInput('', 35, 340, 154, 20)
    $pButton1 = GUICtrlCreateButton('Write STDIn', 198, 340, 154, 20)
    GUICtrlSetOnEvent ($pButton1, '_Process_OnEvent')
    $pButton2 = GUICtrlCreateButton('Log', 361, 340, 154, 20)
    GUICtrlSetOnEvent ($pButton2, '_Process_OnEvent')
    $pButton3 = GUICtrlCreateButton('Exit', 35, 370, 480, 20)
    GUICtrlSetOnEvent ($pButton3, '_Process_OnEvent')
    GUISetState(@SW_SHOW)
EndFunc

; ---------------------------------------------------------------------------------------------
; On-Event functions for the _Process_Gui
; ---------------------------------------------------------------------------------------------
Func _Process_OnEvent()
    Switch @GUI_CtrlId
        Case $GUI_EVENT_MINIMIZE
            GUISetState(@SW_MINIMIZE, $pGui)
        Case $GUI_EVENT_RESTORE
            GUISetState(@SW_RESTORE, $pGui)
        Case $GUI_EVENT_CLOSE
            Exit
        Case $pButton1
            StdinWrite($Stream, GUICtrlRead($pInput1)&@CRLF)
            If @error = -1 Then
                $tOutput = $tOutput & @CRLF & 'That did not work.'
                $tOutput = StringRight($tOutput, StringLen($tOutput) - StringInStr($tOutput, @LF, 0, -14))
                ControlSetText($pGui, '', $pEdit1, $tOutput)
            EndIf   
        Case $pButton2; open log
            ShellExecute($tLog)
        Case $pButton3
            $POpen=0
            GUICtrlSetData($pLabel1, 'Please wait for the process to exit...'); set hint
            GUICtrlSetColor($pLabel1, '0xff0000'); paint the text red
            GUICtrlSetState($pInput1, $GUI_DISABLE)
            GUICtrlSetState($pButton1, $GUI_DISABLE)
            GUICtrlSetState($pButton2, $GUI_DISABLE)
            GUICtrlSetState($pButton3, $GUI_DISABLE)
            AutoItSetOption('GUIOnEventMode', 0)
    EndSwitch
EndFunc 


; ---------------------------------------------------------------------------------------------
; The Adlib_Function that is called by _Process_Run to write monitored output to log and update edit-control
; ---------------------------------------------------------------------------------------------
Func _Process_AL_Run()
    ;$tLine = _Trans_Dos2Ascii($tLine)
    FileWrite($tLog, $tLine); write the log
    $tOutput = $tOutput & $tLine; append the new text
    $tOutput = StringRight($tOutput, StringLen($tOutput) - StringInStr($tOutput, @LF, 0, -14)); just get the last 14 lines
    ControlSetText($pGui, '', $pEdit1, $tOutput); set the text
    $tLine=''; reset the new input
EndFunc   ;==>_Process_AL_Run

; ---------------------------------------------------------------------------------------------
; Run a process and moitor it by using AdLib (here _Process_AL_Run )
; ---------------------------------------------------------------------------------------------
Func _Process_Run($a, $B); $a=program, $b=directory
    ;_PrintDebug('+' & @ScriptLineNumber & ' Calling _Process_Run')
    $stream = Run($a, $b, @SW_HIDE, $STDIN_CHILD + $STDERR_CHILD + $STDOUT_CHILD); run hidden with stdout/err-read
    If Not @error Then
        AdlibEnable ("_Process_AL_Run", 750)
        While 1
            $line = StdoutRead($Stream); capture while getting stream
            If @error Then ExitLoop
            If $line Then $tLine = $tLine & $Line; capture while getting stream
            $line = StderrRead($Stream)
            If @error Then ExitLoop
            If $line Then $tLine = $tLine & $Line; capture while getting stream
            Sleep(10)
        WEnd
        AdlibDisable()
        _Process_AL_Run(); write the last additions
    Else
        $tOutput = $tOutput & @CRLF & 'Could not run command.' & @CRLF
        $tOutput = StringRight($tOutput, StringLen($tOutput) - StringInStr($tOutput, @LF, 0, -14))
        ControlSetText($pGui, '', $pEdit1, $tOutput)
    EndIf
EndFunc 

Exit

As you can see, I added a sleep to avoid a big cpu-usage.

The other thing I use is a temporary variable to store the output and update the edit-control later (to avoid flickering and high cpu-usage).

When using 7zip, I rarely don't get the last output 7zip creates / send to stdout. Seems to be 1 % of my attemps.

Do I have another option than reducing the sleep to avoid that?

The workaround I tried to use was some sort of tail.udf that read created log-files, but the size via FileGetSize of the log was not updated properly. :)

I posted that here

I could watch the update with a regular tail, so the filesize had to increase, but it wouldn't do so instantly, but a few seconds later.

The result was that the gui was not updated and it looked like the application hang.

The last time I played with tail was with AutoIt 3.2.10 on a XP SP2 installation.

Someone told me that the the filegetsize-"call" he saw in an old opensource-code may be a little bit buggy.

-- No offense here! I don't even know if AutoIt uses these calls in later versions nor if is really true. --

Do you know if there is another option to get the filesize with a dll-call or something?

I'd be happy if you could give me an advice.

Best Regards

Dabus

Edited by dabus
Link to comment
Share on other sites

Your code is flawed. You shouldn't quit reading until both StdOutRead() and StdErrRead() set @error. As you have it written now, whichever stream ends first will end the loop even though there may be data on the other stream. So, in your 1% case, which is nothing more than a classic race condition, the child process ends right after StdOutRead() but right before StdErrRead(). The stderr stream is closed, StdErrRead() sees it can not get any more data, sets @error and your loop ends even though StdOutRead() has more data it can read.

Edit: Also, for simplicity, you may wish to look at the merged flag for STDIO. Since you are concatenating both streams into the same buffer anyway, it would simplify the code.

Edited by Valik
Link to comment
Share on other sites

Bah, beaten to the punch. Yes, what he said.

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

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...