PictureBox 在执行进程时冻结

PictureBox freezes while executing process

执行进程后我的 PictureBox 出现问题。当我调用进程时,它只执行一个批处理文件,PictureBox(显示 GIF 动画)冻结了。

我想单击启动进程的按钮并显示显示 GIF 动画的 PictureBox,进程完成后我想隐藏 PictureBox。

有人知道如何实现吗?

这是我的代码:

// My executeCmdCommand
public void executeCmdCommand(string argument)
{
    var arg0 = argument; // Path

    System.Diagnostics.Process process = new System.Diagnostics.Process();
    System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
    startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    startInfo.FileName = "cmd.exe";
    startInfo.Arguments = "/C " + arg0;
    process.StartInfo = startInfo;

    process.Start();
    process.WaitForExit();
}

private void button1_Click(object sender, EventArgs e)
{
    DisplayImage();
    string cmdCom = @"C:\Users\Mim\Desktop\test.bat";
    executeCmdCommand(cmdCom);
    HideImage();
}

private void DisplayImage()
{
    imageControl.Show();
    //PictureBox imageControl = new PictureBox();
    imageControl.Width = 400;
    imageControl.Height = 400;
    Bitmap image = new Bitmap(@"C:\Users\foobar\Desktop\loading.gif");
    imageControl.Dock = DockStyle.Fill;
    imageControl.Image = (Image)image;
    Controls.Add(imageControl);
}

private void HideImage()
{
    imageControl.Hide();
}

问题出在你的 executeCmdCommand() 函数中的这行代码:

process.WaitForExit();

看看文档对这个方法的描述:

Process.WaitForExit Method | Microsoft Docs

Sets the period of time to wait for the associated process to exit, and blocks the current thread of execution until the time has elapsed or the process has exited. To avoid blocking the current thread, use the Exited event.

因此,如果您希望 UI 线程在外部进程执行结束之前畅通无阻,则不应调用此方法。但是,如果您仍想在进程结束时收到警告,则可以按照文档中的建议使用 Exited 事件。

您可以在此处找到使用 Exited 事件的示例和其他想法:

Windows Form run external process without blocking UI - Stack Overflow

我只想在我刚刚链接的答案中添加一件事:如果您要将 PictureBox 隐藏在 Process.Exited 事件调用的事件处理程序中,您应该设置您的引用FormProcess.SynchronizingObject 属性 (documentation),否则你的事件处理程序可以(很可能)在不同于你的 UI 线程的线程上执行,导致从 Form 访问任何内容时出错,包括控件。

代码可能是这样的:

using System.Diagnostics;

public void executeCmdCommand(string argument)
{
    Process process = new System.Diagnostics.Process();
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
    startInfo.FileName = "cmd.exe";
    startInfo.Arguments = $"/C {argument}";
    process.StartInfo = startInfo;

    process.EnableRaisingEvents = true;
    process.SynchronizingObject = this;
    process.Exited += (sender, e) => {
       HideImage();
    };  

    process.Start();
    //process.WaitForExit();
}