Closing a WPF Window or Form after crossing threads?

bmw325ist

New member
Joined
Apr 29, 2010
Messages
3
Programming Experience
1-3
I want to close the main form after another process has exited if the user says yes. I have the MainWindow Class and a modProcess Class coded below. I'm using VS 2010. For whatever reason, the object Me or MainWindow is not accessible, how do I fix it? Thanks.

VB.NET:
Public Class MainWindow
    Dim msProc As New Process
    Private Shared strPathname As String = "C:\Windows\notepad.exe"
    Private Shared strProcname As String = System.IO.Path.GetFileNameWithoutExtension(strPathname)

    Private Sub MainWindow_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Loaded
        CheckStatus()
    End Sub
    Public Sub CheckStatus()
        If (modProcess.IsProcessRunning(strProcname)) Then
            modProcess.MonitorProcess(strProcname)

        Else
            If MsgBox(strProcname & " is Not Running, would you like to open " & strProcname & "?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                modProcess.StartProcess(strPathname)
                System.Threading.Thread.Sleep(5000)
                CheckStatus()
            Else
                Me.Close()
            End If
        End If
    End Sub
    Public Shared Sub ProcessExited(ByVal sender As Object, _
                     ByVal e As System.EventArgs)
        Dim msProc As Process
        msProc = DirectCast(sender, Process)
        If MessageBox.Show(msProc.ProcessName & " has exited at: " & msProc.ExitTime & _
                        Environment.NewLine & "Would you like to restart " & msProc.ProcessName & "?", _
                        "Attention", MessageBoxButton.YesNo, MessageBoxImage.Question) = MessageBoxResult.Yes Then
            msProc.Close()
            modProcess.StartProcess(strPathname)
            System.Threading.Thread.Sleep(500)

            Me.Close()
        Else
            msProc.Close()

        End If
    End Sub

End Class

Second Class:

VB.NET:
Class modProcess

    Public Shared Function IsProcessRunning(ByVal strProcName As String) As Boolean

        Try
            'create an array of Processes 
            Dim npProc() As Process

            'GetProcesses returns an array of processes that are running 
            npProc = Process.GetProcessesByName(strProcName)

            If npProc.Length > 0 Then
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Function

    Public Shared Sub StartProcess(ByVal strFilename As String)
        Dim strProcName As String = IO.Path.GetFileNameWithoutExtension(strFilename)
        Dim proc As New Process
        proc.StartInfo.FileName = strFilename
        proc.Start()

    End Sub
    Public Shared Sub MonitorProcess(ByVal strProcessName As String)
        Dim procs() As Process
        Dim proc As Process
        procs = Process.GetProcessesByName(strProcessName)
        proc = procs.FirstOrDefault
        proc.EnableRaisingEvents = True
        AddHandler proc.Exited, AddressOf MainWindow.ProcessExited

    End Sub

    Public Shared Sub MonitorProcess(ByVal lProc As Long)
        Dim msProc As Process
        msProc = Process.GetProcessById(lProc)
        msProc.EnableRaisingEvents = True
        AddHandler msProc.Exited, AddressOf MainWindow.ProcessExited
    End Sub

    Public Shared Sub KillProcess(ByVal strProcessName As String)
        Dim procs() As Process
        Dim proc As Process
        procs = Process.GetProcessesByName(strProcessName)
        proc = procs.FirstOrDefault
        proc.Kill()

    End Sub

End Class
 
The Process.Exited event is raised on a secondary thread, so the event handler is executed on that same secondary thread. As a rule, you cannot access any controls except on the thread that created them. As such, you'll need to delegate to the UI thread in order to close the main window. You should probably also display your message box on the UI thread, otherwise it will behave non-modally. Here's some example code that delegates to the UI thread in WPF:
VB.NET:
Private Sub ResetTextBoxText()
    If Me.TextBox1.Dispatcher.Thread Is Thread.CurrentThread Then
        Me.TextBox1.Clear()
    Else
        Me.TextBox1.Dispatcher.Invoke(New Action(AddressOf ResetTextBoxText))
    End If
End Sub
So, you write a new method and test whether the thread that owns the control of interest (in your case, the Window) is the current thread. If it is you what you want to do, i.e. display your message box and close the window. If it's not, you invoke a delegate referring to the same method via the control of interest.
 
Thanks for the information... For whatever reason, the Me object is not available. Do I have to instantiate or get the MainWindow object and somehow? I think I have to do that before I can invoke a delegate like you presented in your reply.

Thanks again, Mike.
 
I just noticed that your Process.Exited event handler is declared Shared. Me always refers to the current instance. A Shared method is a member of the class itself, not a specific instance. As such, there is no current instance in your event handler so Me is not valid. You need to remove the Shared modifier from your event handler declaration so that it becomes an instance method and can therefore close the current instance.

That creates a new problem though. I would assume that you have declared that method Shared so that you can access it from the other class without having a reference to the Window object itself. That's a bit of a hack anyway. You should ensure that you have a reference to the MainWindow object in the other class so that you can refer specifically to the method of that object. You could do that by having the window pass the other object a reference to itself, i.e. Me. As it's your app's main window though, you also have the option of using My.Application.MainWindow anywhere in code.

You also have another option. If you're closing the main window then presumably you want to exit the app. To do that you can simply call My.Application.Shutdown from anywhere you like.
 
Back
Top