如何在vb.net win表单应用程序中正确停止线程

时间:2014-03-18 18:54:42

标签: vb.net

我有两个并行运行的线程,一旦用户点击提交按钮(两个线程完成一项复杂的任务并需要大约10分钟才能完成),但在此过程中如果用户希望中止,我应该停止线程。我尝试使用线程的abort方法,但线程继续在后台运行,如果我重新启动应用程序,它会立即抛出错误。

1 个答案:

答案 0 :(得分:0)

一种方法是通过API:OpenThread function + SuspendThread function + ResumeThread function + CloseHandle function

另一种方法是:ManualResetEvent Class和/或AutoResetEvent Class


我之前写过一个片段来暂停/恢复外部流程的主题,您只需看到上面的文档就可以编写自己的代码,在Google中搜索示例,或者根据需要编辑下面的代码段:

#Region " Pause-Resume Thread Class "

' [ Pause-Resume Thread Functions ]
'
' // By Elektro H@cker
'
' Examples :
'
' Process_Thread.Pause_Thread("ffmpeg.exe")       ' Pause  ffmpeg.exe (with thread 0)
' Process_Thread.Resume_Thread("ffmpeg.exe")      ' Resume ffmpeg.exe (with thread 0)
' Process_Thread.Pause_Thread("cmd.exe", , True)  ' Pause  all instances of cmd.exe (with thread 0)
' Process_Thread.Resume_Thread("cmd.exe", , True) ' Resume all instances of cmd.exe (with thread 0)
' Process_Thread.Pause_Thread("Process.exe", 2)   ' Pause the thread 2 of "Process.exe"
' Process_Thread.Resume_Thread("Process.exe", 2)  ' Resume the thread 2 of "Process.exe"
' MsgBox(Process_Thread.Thread_Handle_Dictionary.Count)

Public Class Process_Thread

    <System.Runtime.InteropServices.DllImport("kernel32.dll")> _
    Private Shared Function OpenThread(ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Boolean, ByVal dwThreadId As UInt32) As IntPtr
    End Function

    <System.Runtime.InteropServices.DllImport("kernel32.dll")> _
    Private Shared Function SuspendThread(hThread As IntPtr) As UInteger
    End Function

    <System.Runtime.InteropServices.DllImport("kernel32.dll")> _
    Private Shared Function ResumeThread(hThread As IntPtr) As UInt32
    End Function

    <System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError:=True)> _
    Private Shared Function CloseHandle(ByVal hObject As IntPtr) As <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
    End Function

    ''' <summary>
    ''' Dictionary to store the current paused threads.
    ''' </summary>
    Public Shared Thread_Handle_Dictionary As New Dictionary(Of String, IntPtr)

#Region " Pause Thread "

    ''' <summary>
    ''' Function to pause a thread.
    ''' </summary>
    ''' 
    ''' <param name="Process_Name">The name of the process, ex: cmd.exe</param>
    ''' <param name="Thread_Number">The thread to pause, ex: 0</param>
    ''' <param name="Recursive"> <value name="True">Pause the thread in all processes found recursively.</value></param>
    ''' <returns>True if the process is found; otherwise, False.</returns>
    Public Shared Function Pause_Thread(ByRef Process_Name As String, _
                                  Optional ByVal Thread_Number As Int32 = 0, _
                                  Optional ByVal Recursive As Boolean = False) As Boolean

        If Process_Name.ToLower.EndsWith(".exe") Then _
        Process_Name = Process_Name.Substring(0, Process_Name.Length - 4)

        Dim proc() As Process = Process.GetProcessesByName(Process_Name)

        If Not proc.Length = 0 Then

            If Recursive Then

                For proc_num As Integer = 0 To proc.Length - 1
                    Try
                        Thread_Handle_Dictionary.Add(Process_Name.ToLower & Thread_Number.ToString & ";" & proc(proc_num).Handle.ToString, _
                                                     OpenThread(&H2, True, proc(proc_num).Threads(Thread_Number).Id))
                        SuspendThread(Thread_Handle_Dictionary.Item(Process_Name.ToLower & Thread_Number.ToString & ";" & proc(proc_num).Handle.ToString))
                        Application.DoEvents()
                    Catch ex As Exception
                        MsgBox(ex.Message) ' The handle already exist in the Dictionary.
                        Return False
                    End Try
                Next

            Else

                Try
                    Thread_Handle_Dictionary.Add(Process_Name.ToLower & Thread_Number.ToString & ";" & proc(0).Handle.ToString, _
                                                 OpenThread(&H2, True, proc(0).Threads(Thread_Number).Id))
                    SuspendThread(Thread_Handle_Dictionary.Item(Process_Name.ToLower & Thread_Number.ToString & ";" & proc(0).Handle.ToString))
                Catch ex As Exception
                    MsgBox(ex.Message) ' The handle already exist in the Dictionary.
                    Return False
                End Try

            End If

        Else ' proc.Length = 0

            Throw New Exception("Process """ & Process_Name & """ not found.")
            Return False

        End If

        Return True

    End Function

#End Region

#Region " Resume Thread "

    ''' <summary>
    ''' Function to resume a thread.
    ''' </summary>
    ''' 
    ''' <param name="Process_Name">The name of the process, ex: cmd.exe</param>
    ''' <param name="Thread_Number">The thread to resume, ex: 0</param>
    ''' <param name="Recursive"> <value name="True">Resume the thread in all processes found recursively.</value></param>
    ''' <returns>True if the process is found; otherwise, False.</returns>
    Public Shared Function Resume_Thread(ByRef Process_Name As String, _
                                  Optional ByVal Thread_Number As Int32 = 0, _
                                  Optional ByVal Recursive As Boolean = False) As Boolean

        If Process_Name.ToLower.EndsWith(".exe") Then _
        Process_Name = Process_Name.Substring(0, Process_Name.Length - 4)

        Dim Process_Exist As Boolean = False ' To check if process exist in the dictionary.

        Dim Temp_Dictionary As New Dictionary(Of String, IntPtr) ' Replic of the "Thread_Handle_Dictionary" dictionary.

        For Each Process In Thread_Handle_Dictionary
            If Process.Key.StartsWith(Process_Name.ToLower & Thread_Number.ToString) Then Process_Exist = True
            Temp_Dictionary.Add(Process.Key, Process.Value)
        Next

        If Process_Exist Then

            If Recursive Then
                For Each Process In Temp_Dictionary
                    If Process.Key.ToLower.Contains(Process_Name.ToLower & Thread_Number.ToString) Then
                        ResumeThread(Process.Value)
                        CloseHandle(Process.Value)
                        Thread_Handle_Dictionary.Remove(Process.Key)
                    End If
                    Application.DoEvents()
                Next
            Else

                For Each Process In Temp_Dictionary
                    If Process.Key.ToLower.Contains(Process_Name.ToLower & Thread_Number.ToString) Then
                        ResumeThread(Process.Value)
                        CloseHandle(Process.Value)
                        Thread_Handle_Dictionary.Remove(Process.Key)
                        Exit For
                    End If
                    Application.DoEvents()
                Next

            End If

            Return True

        Else

            Throw New Exception("Process """ & Process_Name & """ with thread number """ & Thread_Number & """ not found.")
            Return False

        End If

    End Function

#End Region

End Class

#End Region