c# - 使用辅助 UI 消息泵作为启动屏幕时出现异常

标签 c# winforms exception splash-screen

我在显示启动画面的方式上遇到了一个奇怪的问题,这导致了 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/

相关文章:

c# - 与 C#/WinForms 中经过深度优化的 GDI 代码相比,SharpDX 可以带来多少改进?

c# - 自动选择按钮(如何关闭此功能?)

java - 为什么对于给定的类不能同时使用 extends 和 throws

c# - 是否可以拦截第三方应用程序的 WIN32 异常?

c# - 为什么在使用 Entity Framework 时看不到 Local 属性?

c# - 使用 Serilog .NET Core 进行日志记录不输出

c# - 自动重新连接到互联网

c# - 如何使用方法添加多个变化的值

android - "Conversion to Dalvik format failed with error 1"更新到 ADT 14 后

c# - C#中的Gzip压缩和解压