.net 从其他线程打开Windows窗体时未关闭

.net 从其他线程打开Windows窗体时未关闭,.net,vb.net,winforms,devexpress,.net-2.0,.net,Vb.net,Winforms,Devexpress,.net 2.0,我创建了一个简单的窗口窗体(粘贴在下面)。现在,当运行一个耗费时间的复杂操作时,我使用它向用户显示wait dailog 调用HerculesWaitForm.Show(“一些标题”、“一些标题…)会像往常一样显示表单,但调用HerculesWaitForm.Close时仍会显示表单。我可以做些什么来克服此问题 Imports System.Threading Public Class HerculesWaitForm Inherits DevExpress.Utils.WaitDial

我创建了一个简单的窗口窗体(粘贴在下面)。现在,当运行一个耗费时间的复杂操作时,我使用它向用户显示wait dailog

调用
HerculesWaitForm.Show(“一些标题”、“一些标题…)
会像往常一样显示表单,但调用
HerculesWaitForm.Close
时仍会显示表单。我可以做些什么来克服此问题

Imports System.Threading
Public Class HerculesWaitForm
    Inherits DevExpress.Utils.WaitDialogForm
    Private Shared form As HerculesWaitForm
    Private Shared _thread As Thread
    Private Shared _caption As String, _title As String
    Private Sub New(ByVal caption As String, ByVal title As String)
        MyBase.New(caption, title)
    End Sub
    Private Shared Sub CreateInstance()
        form = New HerculesWaitForm(_caption, _title)
        Application.Run(form)
        Application.DoEvents()
    End Sub
    Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
        _caption = caption
        _title = title
        If form Is Nothing Then
            _thread = New Thread(AddressOf CreateInstance)
            _thread.IsBackground = True
            _thread.Start()
        End If
    End Sub
    Public Overloads Shared Sub SetCaption(ByVal caption As String)
        If form IsNot Nothing Then
            _caption = caption
            form.SetFormCaption()
        Else
            Show(caption, "")
        End If
    End Sub
    Public Overloads Shared Sub Close()
        If form IsNot Nothing Then
            form.CloseForm()
            form = Nothing
        End If
    End Sub
    Private Sub CloseForm()
        If Me.InvokeRequired Then
            Invoke(New MethodInvoker(AddressOf CloseForm))
            Return
        End If
        Application.ExitThread()
    End Sub
    Private Sub SetFormCaption()
        If Me.InvokeRequired Then
            Invoke(New MethodInvoker(AddressOf SetFormCaption))
            Return
        End If
        MyBase.SetCaption(_caption)
    End Sub
End Class

如果您将其称为:

Private Sub doSomethingLong()
    HerculesWaitForm.Show("hi", "there")
    Sleep(someAmount)  'Random long operation '
    HerculesWaitForm.Close()
End Sub
然后,正如您所注意到的,如果打开和关闭调用之间的时间很短,您可能会遇到问题。这是因为对
Show()的调用
在执行操作时,启动第二个线程创建新的hercules对象。长操作立即开始,同时线程开始启动并运行任务

如果对
Close()
的调用发生在线程完成自身初始化并完成
form
对象实例化之前,那么对
Close()
的调用将发现
form=Nothing
并且它将简单地返回而不做任何操作

然后更改
Show()
代码如下:

Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
    _caption = caption
    _title = title
    If form Is Nothing Then
        _thread = New Thread(AddressOf CreateInstance)
        _thread.SetApartmentState(ApartmentState.STA)  'should do this '
        _thread.IsBackground = True
        _thread.Start()
    End If
    While form Is Nothing   ' add   '
        Thread.Sleep(1)     ' this  '
    End While               ' here  '
End Sub
将强制
Show()
方法阻塞,直到工作线程创建了
form
对象-这确保了以后对
Close
的所有调用都不会被简单地跳过(因为
form is nothing
)。不漂亮,但如果不重构整个类,这可能是最快的修复方法

然而,这实际上是处理长操作的一种非常糟糕的方法。你不是将工作放入工作线程,而是创建一个新的UI线程来运行一个愚蠢的动画,同时阻止真正的UI线程来执行一个耗时的操作。除了糟糕的设计实践之外,这还影响到你的DevExpress widget表单将悬停在所有其他应用程序的顶部(因为从此类调用时,它本身就是一种应用程序),拒绝您的用户与其他应用程序进行多任务处理的能力,因为您的应用程序正在窃取前端和中心,以显示它正在处理某些事情


您确实应该使用ThreadPool、BackgroundWorker或其他类型的工作线程来执行复杂的操作。这样,您的主UI线程就可以自由地提供长操作的状态更新,并避免上述双UI线程混乱。

如果您将其称为:

Private Sub doSomethingLong()
    HerculesWaitForm.Show("hi", "there")
    Sleep(someAmount)  'Random long operation '
    HerculesWaitForm.Close()
End Sub
然后,正如您所注意到的,如果打开和关闭调用之间的时间很短,您可能会遇到问题。这是因为对
Show()的调用
在执行操作时,启动第二个线程创建新的hercules对象。长操作立即开始,同时线程开始启动并运行任务

如果对
Close()
的调用发生在线程完成自身初始化并完成
form
对象实例化之前,那么对
Close()
的调用将发现
form=Nothing
并且它将简单地返回而不做任何操作

然后更改
Show()
代码如下:

Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
    _caption = caption
    _title = title
    If form Is Nothing Then
        _thread = New Thread(AddressOf CreateInstance)
        _thread.SetApartmentState(ApartmentState.STA)  'should do this '
        _thread.IsBackground = True
        _thread.Start()
    End If
    While form Is Nothing   ' add   '
        Thread.Sleep(1)     ' this  '
    End While               ' here  '
End Sub
将强制
Show()
方法阻塞,直到工作线程创建了
form
对象-这确保了以后对
Close
的所有调用都不会被简单地跳过(因为
form is nothing
)。不漂亮,但如果不重构整个类,这可能是最快的修复方法

然而,这实际上是处理长操作的一种非常糟糕的方法。你不是将工作放入工作线程,而是创建一个新的UI线程来运行一个愚蠢的动画,同时阻止真正的UI线程来执行一个耗时的操作。除了糟糕的设计实践之外,这还影响到你的DevExpress widget表单将悬停在所有其他应用程序的顶部(因为从此类调用时,它本身就是一种应用程序),拒绝您的用户与其他应用程序进行多任务处理的能力,因为您的应用程序正在窃取前端和中心,以显示它正在处理某些事情


您真的应该使用线程池、BackgroundWorker或其他类型的工作线程来执行复杂的操作。这样,您的主UI线程就可以自由地提供长操作的状态更新,并避免上述双UI线程混乱。

您需要调用_-thread.SetApartmentState(ApartmentState.STA)在启动线程之前。

您需要调用_thread.SetApartmentState(ApartmentState.STA)在启动线程之前。

您在这方面有任何异常吗?无论如何,请检查这个问题:如果操作完成得很快,那么问题仍然存在,但是如果需要一些时间,那么它会按预期工作。一堆代码实际上没有Form.Close()调用。HerculesWaitForm是一个类型名,永远不要使用HerculesWaitForm.Close()。当你在线程中这样做时,它使用了错误的对象。@HansPassant-我也这么认为,但是
HerculesWaitForm.Close()
实际上调用了共享的、重载的
Close()
函数来正确关闭表单(只要那时表单已经创建)整个班级都是一个残暴的混乱的方法来实现一个单例,而且它似乎真的“有效”",至少达到了预期的效果。无论如何,我称整个过程为犯规。它会尽可能多地勾选错误实践框…你在这方面有什么例外吗?无论如何,请检查这个问题:如果操作完成得非常快,那么问题仍然存在,但如果需要一些时间,那么它会按预期工作。一堆代码,实际上没有Form.Close()调用。HerculesWaitForm是一个类型名,千万不要使用HerculesWaitForm.Close()。在线程中这样做时,它使用了错误的对象。@HansPassant-我也这么认为,但是
HerculesWaitForm.Close()<