我在显示启动画面的方式上遇到了一个奇怪的问题,这导致了 InvalidAsynchronousStateException被扔掉。
首先,这是 Main{} 的代码,我在其中启动启动窗体:
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.Start();
Application.Run(new MainForm());
}
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
Application.Run(splash);
}
}
我使用的是.NET2.0,Win XP。
在一些测试中,应用程序运行了几个小时,我注意到异常数量偶尔会增加一两个。 (通过 PerfMon 获得的数字,查看“抛出的异常数”计数器。)这些异常似乎被运行时捕获并吞掉,因为它们确实 不会产生涟漪并导致应用程序本身出现任何问题。至少我无法确定任何事情。
我发现系统触发 UserPreferenceChanged 事件时会引发异常。自从发现这一点后,我可以生成异常 随意更改背景图片或屏保等
我自己没有在代码中的任何地方明确订阅此事件,但我了解(通过 Google 的力量)所有顶级控件和表单都会订阅 自动到此事件。
我仍然不确定为什么会首先触发此事件,因为它似乎是在应用程序整夜运行时发生的,但我想这是另一个有待解决的谜团。
现在,如果我停止启动窗体线程的运行,异常就会消失。运行线程,它会回来。那么,似乎有些东西没有取消订阅该事件,这可能会导致随后的异常?
有趣的是,如果我用默认的开箱即用表单替换启动表单,问题仍然存在:
static void ShowSplash()
{
using (Form splash = new Form())
{
Application.Run(splash);
}
}
显示此表单时,任何 UserPreferenceChanged 事件都不会导致任何异常。一旦表单关闭并且线程退出,就会抛出异常。
进一步的研究使我得出this Microsoft article ,其中包含以下注释:
Common causes are a splash screens created on a secondary UI thread or any controls created on worker threads.
嗯,看起来有罪。请注意,我的应用程序没有卡住或执行任何异常操作。
目前,这更多的是出于好奇,但我担心将来可能会有一些隐藏的麻烦等着咬人。
对我来说,Application.Run 启动的表单或消息泵在终止时似乎没有正确清理。
有什么想法吗?
最佳答案
是的,您与 SystemEvents 类发生了冲突。该类创建一个监听系统事件的隐藏窗口。特别是 UserPreferenceChanged 事件,许多控件使用该事件来知道何时需要重新绘制自己,因为系统颜色已更改。
问题是,创建窗口的初始化代码对调用它的线程的单元状态非常敏感。在您的情况下这是错误的,您没有调用 Thread.SetApartmentState() 来切换到 STA。这对于显示 UI 的线程非常非常重要。
请注意,您的解决方法实际上并不是修复,系统事件将在错误的线程上引发。您的启动线程而不是程序的 UI 线程。当实际的系统事件被触发时,您仍然会遇到随机情况并且极难诊断故障。最臭名昭著的是,当用户锁定工作站时,程序在再次解锁时会死锁。
我认为调用 Thread.SetApartmentState() 应该可以解决您的问题。不能 100% 确定,这些 UI 线程交互非常难以分析,而且我还没有弄错。请注意,.NET 已经有非常 solid support用于启动画面。它确实得到了这样的细节。
关于c# - 使用辅助 UI 消息泵作为启动屏幕时出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3274865/