Search code examples
vb.netmultithreadingfilestreamstreamreader

Filestream read only locking PC


I'm trying to read the Windows update log on remote PCs on my LAN. Most of the time I can successfully read the file but at times the program locks up. Likely due to one issue or another - doesn't really matter. What I do need is a way to recover when the Filestream/Streamreader locks up - I'm not sure which is causing the lock. Some streams can set a timeout but the filestream below returns False on a .CanTimeout call.

How can I break out if the stream locks up? (Sometimes the lock is so tight a power off is needed to recover.)

Is there a way to test if the stream will fail before I actually attempt the read?

Is there an alternate way to read a remote log file that another program has open? (I'm using the stream method because the regular File.IO was blocked because the file is open on the remote PC.)


I'm getting closer (I think) with this code. I browed the pathExists code from the referenced post but it was the OP and not an answer.

Imports System.IO
Import System.Threading
...
Function GetAULog(PCName As String) As String
    Try
        Dim sLogPath As String = String.Format("\\{0}\c$\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
        If PCName = My.Computer.Name Then
            sLogPath = String.Format("C:\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
        End If
        ' read file open by another process
        If Not pathExists(sLogPath) Then
            MsgBox("AU log file not found - PC on?")
            Return "NA"
        End If
        Using fs As New FileStream(sLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
            Using sr As New StreamReader(fs)
                Dim s As String = sr.ReadToEnd
                Return s
            End Using
        End Using
    Catch ex As Exception
        MsgBox(ex.Message)
        Return ""
    End Try
End Function

Public Function pathExists(path As String) As Boolean
    Dim exists As Boolean = True
    Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
    t.Start()
    Dim completed As Boolean = t.Join(500)
    'half a sec of timeout
    If Not completed Then
        exists = False
        t.Abort()
    End If
    t = Nothing
    Return exists
End Function

At least when the PC is off the pathExists() code returns False in short order.

My problem now is the process does not end when the program exits - at least in the IDE, didn't check runtime.

I added t = Nothing but that didn't help. I couldn't figure out the proper Using syntax to test that. How do I properly cleanup after a thread timeout?


Solution

  • "Final" code shown below. The exceptions are not firing when the timeout occurs so the .Abort was evidently OK.

    When the timeout does occur, because the remote PC did not respond, there is a process left hanging which goes away after 30 seconds or so. I notice this when using the IDE, I run the program and test a PC that is off. If I then exit the program the form closes but the IDE hangs for ~30 seconds - I can click Stop-Debugging at this point and it works, but the IDE continues on its own after the ~30 second timeout.

    I guess the t = Nothing in the Finally block does not dispose of the thread. t.Dispose does not exists.

    So, things are working OK with the exception of the dangling thread that eventually clears itself up. The program is no longer hanging to the point where it cannot not be stopped.

    'Imports System.IO
    'Imports System.Threading
    Public Function pathExists(path As String) As Boolean
        ' check for file exists on remote PC
        Dim exists As Boolean = False
        Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
        Try
            t.Start()
            Dim completed As Boolean = t.Join(500)
            'half a sec of timeout
            If Not completed Then
                exists = False
                t.Abort()
            End If
        Catch ex2 As ThreadInterruptedException
            MsgBox("timeout on AU log exists test" & vbNewLine & ex2.Message,, "ThreadInterruptedException")
        Catch exAbort As ThreadAbortException
            MsgBox("timeout on AU log exists test" & vbNewLine & exAbort.Message,, "ThreadAbortException")
        Catch ex As Exception
            MsgBox("exception on AU log exists test" & vbNewLine & ex.Message)
        Finally
            t = Nothing
        End Try
        Return exists
    End Function