Is there any way to timeout during Streamreader.Readline?

rgreco

New member
Joined
Sep 5, 2013
Messages
4
Programming Experience
1-3
Hey all,

First, a little foreword: My boss had some .cmd scripts that he used to install various programs on our network, and since I had free time, I was asked to consolidate these scripts into one overarching project. I chose to start fiddling with vb.net because I have never really programmed before, and we already had visual studio installed. I trust that if you looked at my code you would be shocked at my bad practices. But for now, my crappy, almost nonexistent programming skills have paid the bills.

So basically, this lumbering beast of a program monitors the progress of software installation. For the most part, it works fine.

In the future I would like to get into multi-threading, but I am hesitant due to my lack of experience. I managed to set up a background worker, and it works beautifully, allowing me to have a responsive program, a progress bar and a status bar. Before I get into multi-threading I need to fix my one major issue.

My code spawns an invisible command prompt and then uses that command prompt to start another program called rex with the installation parameters. Rex then does the heavy lifting regarding the installation, and then tells the command prompt we spawned if the installation was successful or not.

I read the feedback, and then log the details. So this generally takes a minute or two, but it occasionally takes over twelve hours.

Here's My Question + Stuff I've Tried:
I will be linking my code at the bottom of this post, in that code you will see line = installSR.readline. I was hoping someone could help me figure out how to make a timeout work. I have tried a bunch of different methods to function as a timeout, but the issue is that my code locks up on that one line until the command prompt responds with information from Rex.



  • I tried measuring time before and after the readline, but that didn't work because when the timeouts happen the code literally hangs on that one line and never progresses to the timeout checking code.



  • I tried checking to see if the command prompt has exited before I let it start reading lines. And though that seemed promising, and even works in other parts of my code, it didn't work here. (And I don't know why it didn't work.) It just says that the command prompt has been closed, and then the readline hangs anyway. I really don't know why, if anyone could shed some light on that I would appreciate it.



  • I also tried putting all of this code in a separate worker, and the timeout would count down correctly. However, even when I sent the .abort command or .dispose commands (for the streamreader, writer, and command prompt process) I could tell that the processes weren't actually responding with their abort catch information. And then eventually the program would crash. I assume this is because I had too many open threads? Maybe I need to look into that more? It seems like maybe the only possible way to make this work is to get this solution working.

Also, I would love and appreciate any unrelated advice about my code.

VB.NET:
For Index = 1 To installArray.GetUpperBound(0)
    Try
        If installArray(Index) <> "" Then

            'Here we dim a Command Prompt that we will be using to enter batch commands *SW = streamwriter* *SR = streamreader*  
            Dim CMD As New Process
            Dim installSW As System.IO.StreamWriter
            Dim installSR As System.IO.StreamReader
            installCMD.StartInfo.FileName = "cmd"
            installCMD.StartInfo.UseShellExecute = False
            installCMD.StartInfo.Domain = configNetwork
            installCMD.StartInfo.UserName = halfUsername
            installCMD.StartInfo.Password = password
            installCMD.StartInfo.RedirectStandardInput = True
            installCMD.StartInfo.RedirectStandardOutput = True
            installCMD.StartInfo.CreateNoWindow = True
            installCMD.Start()
            installSW = installCMD.StandardInput
            installSR = installCMD.StandardOutput
            
            fixCount = 0
                
            'These bw.CancellationPending chunks just exist to check if the cancel button has been pushed.
            If bw.CancellationPending = True Then
                e.Cancel = True
                SetControlEnabled(prRunButton, True)
                SetControlEnabled(prResetButton, True)
                Thread.Sleep(25)
                'PerformClick(prResetButton)
                Call Reset_Tab_1(Me, e)
                Return
            Else
            End If
            
            Thread.Sleep(1000)
            installSW.WriteLine("start /B C:\Windows\rex.exe \\" & installArray(Index) & " -u " & username & " -p " & insecurePassword & " -b -c " & installFileLocation)
            installSW.WriteLine("exit")


            logString = logNum & ": Sending install command to: " & installArray(Index) & "..." & vbCrLf
            SetControlTxt(logTB, logString)
            LogScroll(logTB)
            logNum += 1

            Do

                '\\ This loop generally takes a minute. But occasionally it takes 14+ hours.
                '\\ During these 14+ hours, the code is just hanging on the installSR.ReadLine.
              [COLOR=#ff8c00][SIZE=3] [B][U] line = installSR.ReadLine[/U][/B][/SIZE][/COLOR]
                
            Loop Until line.Contains("code") Or line.Contains("specified network") Or line.Contains("executable") Or line.Contains("Error 5") Or line.Contains("Error 53")
            
            If line.Contains("Error 53") Then
                debugLine = (logNum & ": " & "The network path was not found. " & vbCrLf)
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                logNum += 1
                debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                Thread.Sleep(15)
                GoTo installSkip
                
            ElseIf line.Contains("Error 5") Then
                debugLine = (logNum & ": " & "Access was denied. " & vbCrLf)
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                logNum += 1
                debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                Thread.Sleep(15)
                GoTo installSkip
                
            ElseIf line.Contains("executable") Then
                debugLine = (logNum & ": " & "Rex responded with syntax information. Please ensure that your password does not end with special characters such as & before trying again. " & vbCrLf)
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                logNum += 1
                debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                Thread.Sleep(15)
                GoTo installSkip
            End If
            
            logString = logNum & ": " & line & vbCrLf
            SetControlTxt(logTB, logString)
            LogScroll(logTB)
            logNum += 1
            Thread.Sleep(15)
            debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
            SetControlTxt(logTB, debugLine)
            LogScroll(logTB)
            Thread.Sleep(15)
            installMsgArray(Index) = line
            
installskip:


            If fixCount >= 180 Then
                logString = logNum & ": The Install command timed out. (180 seconds)" & vbCrLf
                SetControlTxt(logTB, logString)
                LogScroll(logTB)
                logNum += 1
                Thread.Sleep(15)
                debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
                SetControlTxt(logTB, debugLine)
                LogScroll(logTB)
                Thread.Sleep(15)
                installArray(Index) = ""
                installMsgArray(Index) = ""
                installTimeoutCounter += 1
                installTimeoutArray(Index) = installArray(Index)
            End If


            installCMD.Dispose()
            installSW.Dispose()
            installSR.Dispose()
            
        End If


    Catch ee As Exception
        debugLine = (logNum & ": " & "Mistakes were made.... (some non-descriptive error occured) [during install index number " & Index & "]" & vbCrLf)
        SetControlTxt(logTB, debugLine)
        LogScroll(logTB)
        logNum += 1


        debugLine = (logNum & ": " & ee.ToString & vbCrLf)
        SetControlTxt(logTB, debugLine)
        LogScroll(logTB)
        logNum += 1


        Thread.Sleep(15)
        debugLine = "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" & vbCrLf
        SetControlTxt(logTB, debugLine)
        LogScroll(logTB)
        Thread.Sleep(15)
    End Try
    
    If Sum < 100 Then
        Sum += ProgressBarCounter
        Percent = (FormatNumber(Sum) & "%")
        bw.ReportProgress(Sum)
    Else
        Percent = "100%"
        bw.ReportProgress(100)
    End If
    'This is just updating the percentage & status bar
    
Next
 
You can change the reading from Process to use its asynchronous event model, read about its BeginOutputReadLine method and OutputDataReceived event.
 
BeginOutputReadLine was the key, for sure. I wish someone had taught me about synchronous vs. asynchronous before I wasted all this time searching Google and trying other methods.

On the bright side, I learned quite a bit in my attempts to fix this issue. I am happy to report that my code finally works correctly.

Thanks for the help, John.
 
Back
Top