Windows窗体运行外部进程而不阻止UI

时间:2014-09-10 15:09:59

标签: c# .net forms process

我想:

  1. 使用文本框显示表单。
  2. 运行外部程序(例如notepad.exe)。
  3. 继续允许用户在记事本运行时将数据输入表单文本框。
  4. 当记事本关闭时,再运行一些(继续)本机表单代码。除其他事项外,这将更新表格。
  5. 我在实现这一目标时遇到了问题。我知道有很多关于这个类似问题的帖子,但我们找不到适用于我的解决方案。

    我试过了:

    • 执行waitforexit,但这当然会阻止用户界面,用户无法输入数据。
    • 尝试异步进程调用,此进程完成时将调用另一个方法。这会导致从另一个线程调用新方法并且无法更新表单的问题。
    • 在UI中执行等待/休眠循环,但这又会自然阻止UI。

    简单Windows窗体程序最简单,最简单的解决方案是什么?没有使用额外的类,所有代码都在 Form1类

4 个答案:

答案 0 :(得分:4)

当进程退出时,Process类会触发Exited事件。您可以向该事件添加处理程序,以便在进程退出时执行代码而不阻止UI线程:

process.EnableRaisingEvents = true;
process.Exited += (s, args) => DoStuff();

或者你可以创建一个Task来表示完成利用TPL进行异步的过程:

public static Task WhenExited(this Process process)
{
    var tcs = new TaskCompletionSource<bool>();
    process.EnableRaisingEvents = true;
    process.Exited += (s, args) => tcs.TrySetResult(true);
    return tcs.Task;
}

这将允许你写:

await process.WhenExited();
UpdateUI();

答案 1 :(得分:2)

你走了:

    void Form1_Load(object sender, EventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            var p = Process.Start("notepad.exe");
            p.WaitForExit();
        }).ContinueWith(antecedant => { MessageBox.Show("Notepad closed"); });
    }

答案 2 :(得分:2)

这是我最喜欢用BackgroundWorker做这样的事情的方法。这样做的好处是RunWorkerCompleted回调位于主线程上,因此它可以与UI进行交互。

public partial class Form1 : Form
{
    ...
    private BackgroundWorker wrk;
    private void button1_Click(object sender, EventArgs e)
    {
        wrk = new BackgroundWorker();
        wrk.DoWork += (s, ea) => { /*Create your process and wait here*/ };
        wrk.RunWorkerCompleted += (s, ea) => { textBox1.Text = "Finished"; };
        wrk.RunWorkerAsync();
    }
}

答案 3 :(得分:1)

您应该在BackgroundWorker中启动进程,以便在同一个线程上捕获完整的事件。

        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate {
            Process proc = Process.Start("YOUR-PROCESS-PATH");
            proc.Start();
            proc.WaitForExit();
        }
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.RunWorkerAsync();

然后在被调用的线程上捕获worker结束事件;

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Do your thing o UI thread
    }