onuserpreferencechanged hang - 处理多个表单和mutlipe ui线程

时间:2013-06-19 12:36:19

标签: c# winforms hang

我认为我的问题类似于:

.NET 4.0 and the dreaded OnUserPreferenceChanged Hang

我也看了看:

http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html#BeginInvokeDance

我已删除了启动画面。

我还尝试添加建议的代码:Microsoft.Win32.SystemEvents.UserPreferenceChanged + = delegate {};到我们的main()方法。

我正在寻找有关如何排除故障的一些想法和信息。

对于main()方法,我们启动一个windowmanager类,它是一个使用Application.Run的表单 它只是任务栏中的一个图标(我们不显示窗口)。

每当我们启动一个对象时,我们都有一个后台线程,它创建一个表单,然后执行Application.Run(form)

在Application.Run(form)form.IsHandleCreated = false时出现。

我使用MysteriousHang网站上的冰箱应用程序。 (我修改它以继续在循环中发送更改通知)。

我应该如何处理创建和运行新表单?是否在后台线程上创建表单是否重要,即使它的句柄尚未创建?

1 个答案:

答案 0 :(得分:5)

  

我也对术语“UI线程”感到困惑。

UI线程是一个泵送消息循环的线程。并且以与用户界面对象兼容的模式运行,它需要是STA,单线程公寓。这是一个COM实现细节,对于非线程安全且需要STA的常见UI操作非常重要,例如Drag + Drop,剪贴板,shell对话框,如OpenFileDialog和ActiveX组件。

CLR的工作是调用CoInitializeEx()并选择公寓类型。它由程序中Main()入口点的[STAThread]属性引导。出现在创建UI对象(如Winforms或WPF应用程序)的项目中。但不是控制台模式的应用程序或服务。对于工作线程,换句话说是由代码而不是Windows创建的线程,公寓类型由您传递给Thread.SetApartmentState()方法的内容选择。默认是MTA,错误的味道。线程池线程始终是MTA,无法更改。

SystemEvents类有一个不值得羡慕的任务,即确定哪个线程是程序中的UI线程。重要的是它可以在正确的线程上引发事件。它通过使用启发式实现,第一个线程订阅事件是一个STA线程被认为是合适的。

当猜测不准确时,事情会出错。或者在您尝试创建创建UI对象的多个线程的情况下,猜测只能对其中一个进行正确。您可能也忘了调用Thread.SetApartmentState(),因此对于它们中的任何一个都不正确。 WPF更强烈地声明这一点,并且当线程不是STA时将生成异常。

UserPreferenceChanged事件是一个麻烦制造者,它由您在工具箱上找到的一些控件订阅。他们使用它来知道活动的视觉风格主题已经改变,所以他们将使用新的主题颜色重新绘制自己。这些控件的部分中的事件处理程序中的一个重要缺陷是它们假定事件是在正确的线程上引发的,该线程是创建控制对象的相同线程。

在您的计划中,就是这种情况。结果往往令人不愉快,微妙的绘画问题是一个小缺陷,僵局肯定是可能的。出于某种原因,使用Windows + L锁定工作站并解锁它特别容易导致死锁。在这种情况下会引发UserPreferenceChanged事件,因为桌面从用户桌面的安全桌面切换。

侦听UserPreferenceChanged事件并且使用安全线程实践(使用Control.BeginInvoke)的控件是DataGridView,NumericUpDown,DomainUpDown,ToolStrip + MenuStrip和ToolStripItem派生类,可能是RichTextBox和ProgressBar(不清楚)。

消息应该清楚,您使用的是不安全的线程实践,并且它们可以字节。通常,在工作线程上创建UI永远不会有任何意义,Winforms或WPF程序的主线程已经能够支持多个窗口。除了避免危险控制之外,这是你应该努力摆脱这个问题。