我有几个用VB.NET编写的Windows窗体应用程序,目标是我正在迁移到.NET 7的.NET Framework。它们是单示例应用程序,并依赖于MyApplication_StartupNextInstance
事件来执行各种操作,包括创建新表单。
当应用程序在.NET 7上运行第二个示例时,我观察到了各种问题,我可以追溯到这个事件处理程序现在在一个工作线程而不是主线程中运行。根据Microsoft's documentation of the event,此事件应该在主线程中运行。
作为一个最小的例子,我在Visual Studio 2022中创建了两个Windows Forms VB.NET项目,一个针对.NET Framework 4.7.2,另一个针对.NET 7。这两个项目都有一个空窗体“Form1.vb”,并且项目属性设置使其成为单示例应用程序。
然后我在ApplicationEvents.vb中有以下代码:
Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Try
Dim frm2 As Form1 = New Form1() 'Breakpoint here
frm2.Show()
Catch ex As Exception
MsgBox("ERROR: " & ex.Message)
End Try
End Sub
我运行了上面指定的断点设置的解决方案,并显示了Threads面板(Debug -〉Windows -〉Threads)。然后我从Windows资源管理器手动运行.exe,它引发StartupNextInstance
事件并运行上述代码。
.NET Framework版本的代码显示活动线程是“Main Thread”。.NET 7版本的代码显示活动线程是“.NET ThreadPool Worker”。当我继续通过断点时,.NET Framework版本启动窗体的第二个副本,而.NET 7版本打开一个损坏的窗体,该窗体不会响应我的输入(如果向窗体添加控件,则会更明显)。
如果我添加一个WebBrowser控件(它是IE ActiveX控件的 Package 器),.NET Framework版本的代码按预期工作,但.NET 7版本抛出一个异常“ActiveX控件'8856 f961 - 340 a-11 d 0-a96 b-00 c 04 fd 705 a2'无法示例化,因为当前线程不在单线程单元中”,进一步证明问题的核心是事件在哪个线程下运行的差异。
在.NET 7中,通过StartupNextInstance
事件与主线程中的UI元素进行交互的正确方法是什么?
1条答案
按热度按时间xoefb8l81#
确保代码在WinForms应用程序中的UI线程上执行的常用方法是测试在UI线程上创建的某些控件(窗体也是控件)的
InvokeRequired
属性,如果它是True
,则调用同一控件的Invoke
或BeginInvoke
方法以封送对UI线程的方法调用。您可能会想在here中更深入地了解这一点。如果需要,可以忽略
InvokeRequired
属性,因为即使在UI线程上调用Invoke
,它也可以工作,尽管会有不必要的开销。如果您确定代码将在辅助线程上执行,则属性检查也不那么重要。在这个特定的场景中,您可以使用相同的技术。如果您的应用有一个启动窗体,并且该窗体将在应用的生命周期中始终显示,则可以在
StartupNextInstance
事件处理程序中使用它来封送对UI线程的方法调用。在.NET 5之前,您可能会在哪里执行此操作:你现在可以这样做以达到同样的效果: