我必须调用 Application.ExitThread() 吗?

Do I have to call Application.ExitThread()?

using System.Windows.Forms;

public class App
{
    [STAThread]
    public static void Main()
    {
        string fname;
        using (var d = new OpenFileDialog())
        {
            if (d.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            fname = d.FileName;
        }
        //Application.ExitThread();
        for (; ;)
            ;
    }
}

上面的代码显示了一个文件对话框。一旦我 select 一个文件并按下 open,for 循环就会执行,但(冻结的)对话框仍然存在。

一旦我取消注释 Application.ExitThread(),对话框就会按预期消失。

这是否按预期工作?为什么 using 不让 window 消失?我在哪里可以找到有关此的更多信息?

不,您不必致电 Application.ExitThread()

Application.ExitThread() 终止调用线程的消息循环并强制销毁冻结的对话框。虽然 "that works",但如果知道冻结的原因,最好解冻对话框。

在这种情况下,按 open 似乎会触发一个没有任何机会完成的关闭事件。 Application.DoEvents() 给它机会并让对话框消失。

您已经发现了单线程应用程序的主要问题...长时间 运行ning 操作冻结了用户界面。

您的 DoEvents() 本质上是调用 "pauses" 您的代码并提供其他操作,例如 UI,运行 的机会,然后继续。问题是您的 UI 现在再次冻结,直到您再次调用 DoEvents()。实际上,DoEvents() 是一个 very problematic approach (some call it evil)。你真的不应该使用它。

你有更好的选择。

将长时间的 运行ning 操作放在另一个线程中有助于确保 UI 保持响应并尽可能高效地完成工作。处理器能够在两个线程之间来回切换,给人一种同时执行的错觉,而没有成熟的多进程的困难。

实现此目的的一种更简单的方法是使用 BackgroundWorker, though they have generally fallen out of favor (for reasons I'm not going to get into in this post: further reading)。然而,它们仍然是 .NET 的一部分,并且学习曲线比其他方法低,所以我仍然建议新开发人员在业余项目中使用它们。

目前最好的方法是 .NET 的 Tasks library. If your long running operation is already in a thread (for example, it's a database query and you are just waiting for it to complete), and if the library supports it, then you could take advantage of Tasks using the async keyword and not have to think twice about it. Even if it's not already in a thread or in a supported library, you could still spin up a new Task and have it executed in a separate Thread via Task.Run()。 .NET 任务具有内置语言支持等优势,例如协调多个任务和将任务链接在一起。

JDB 已经在 为什么 中解释过(一般来说)您的代码没有按预期工作。让我添加一点建议解决方法(针对您的特定情况以及当您只需要使用系统对话框然后像控制台应用程序一样继续时)。

使用 Application.DoEvents(),好的, 似乎 可以工作,在您的情况下,您没有可重入代码。但是,您确定所有相关消息都已正确处理吗?您应该调用 Application.DoEvents() 多少次?您确定 正确地初始化了所有内容吗(我说的是 ApplicationContext)?第二个问题更实用,OpenFileDialog 需要 COM,COM(此处)需要 STAThreadSTAThread 需要消息泵。我不能告诉你它会以何种方式失败,但可以肯定的是它可能会失败。

首先请注意,通常应用程序使用 Application.Run() 启动主消息循环。您不会希望看到 new MyWindow().ShowDialog(),对吧?您的示例没有什么不同,让 Application.Run(Form) 重载为您创建 ApplicationContext(并在表单关闭时处理 HandleDestroyed 事件,最终调用 - 惊喜 - Application.ExitThread())。不幸的是 OpenFileDialog 没有继承自 Form 然后你必须将它托管在 dummy 表单中才能使用 Application.Run().

如果使用设计器在窗体中添加对话框,则无需显式调用 dlg.Dispose()(让 WinForms 管理对象的生命周期)。

using System;
using System.Windows.Forms;

public class App
{
    [STAThread]
    public static void Main()
    {
        string fname = AskForFile();
        if (fname == null)
            return;

        LongRunningProcess(fname);
    }

    private static string AskForFile()
    {
        string fileName = null;

        var form = new Form() { Visible = false };
        form.Load += (o, e) => { 
            using (var dlg = new OpenFileDialog())
            {
                if (dlg.ShowDialog() == DialogResult.OK)
                    fileName = dlg.FileName;
            }

            ((Form)o).Close();
        };

        Application.Run(form);

        return fileName;
    }
}