Search code examples
vb.netprocessdispose

VB.NET - Console processes being left open after program exit. How do I close them correctly?


BACKGROUND:

I'm writing a program that opens an interactive console application, listens to STDOUT/STDERR, and sends commands to this interactive session. At the end it will issue an exit command and the process normally goes away. If the user clicks the close button I call dispose on the class which issues the exit command and then attempts to force close the session before the program terminates. I notice that after a day of testing I have a bunch of orphaned processes still running. They build up and never quit. Obviously, I've made a terrible mistake.

QUESTION:

How do I ensure that my console processes are fully dead before my application terminates?

MY DISPOSE METHOD:

Protected Overridable Async Sub Dispose(disposing As Boolean)
    If _disposed Then Return

    If disposing Then
        _handle.Dispose()
        ' Free any other managed objects here.
        '           
        If IsConnected Then Await ClosePort().ConfigureAwait(False)
        If _transmissionCancel IsNot Nothing Then _transmissionCancel.Dispose()           
    End If

    ' Free any unmanaged objects here.
    '
    If _consoleReader IsNot Nothing Then _consoleReader.Dispose()
    If _consoleWriter IsNot Nothing Then _consoleWriter.Dispose()
    If _consoleProcess IsNot Nothing Then _consoleProcess.Dispose()

    _disposed = True
End Sub

NOTE: The "ClosePort" method called here features a kill and a wait:

If Not _consoleProcess.WaitForExit(SocketTimeout) Then
    _consoleProcess.Kill()
    _consoleProcess.WaitForExit()
End If

Solution

  • On a suggestion from @hans-passant; I attempted to remove the "Async" modifier from my Dispose method and synchronously wait for the task to complete before completing disposal. I made a task of my function and told it to start. This appears to work but now my program doesn't end when I click the close button. My code jumps from the ".start" to the "End Using" but then the Dispose method stops, passing control back to the UI. If I click close a second time then the dispose method fires again and my program cleanly shuts down.

    I post this as an answer because it appears to have solved my original problem and it is my current answer. However, I don't want to mark this as the solution because I have to click close twice to end my program. What have I done?

    I am still open to other solutions.

    Protected Overridable Sub Dispose(disposing As Boolean)
        If _disposed Then Return
    
        If disposing Then
            _handle.Dispose()
            ' Free any other managed objects here.
            '           
            If IsConnected Then                
                Using holdOnAMinute As Task = Task.Run(Function() ClosePort())                   
                    holdOnAMinute.Start()
                    holdOnAMinute.Wait(SocketTimeout)
                End Using
            End If
        End If
        ' Free any unmanaged objects here.
        '
        If _consoleReader IsNot Nothing Then _consoleReader.Dispose()
        If _consoleWriter IsNot Nothing Then _consoleWriter.Dispose()
        If _consoleProcess IsNot Nothing Then _consoleProcess.Dispose()
        If _transmissionCancel IsNot Nothing Then _transmissionCancel.Dispose()
    
        _disposed = True
    End Sub