【问题标题】:Executing batch file from C# winforms ignores timeout从 C# winforms 执行批处理文件忽略超时
【发布时间】:2020-11-05 06:06:12
【问题描述】:

所有, 我正在尝试通过 C# winforms 应用程序执行一系列批处理文件。在这个早期阶段,使用测试批处理文件,除非我设置 UseShellExecute = true,否则我无法让流程执行遵守批处理文件中的超时,这是我试图避免的事情。我的目标是执行脚本文件并将输出重定向到 GUI,如下面的代码所示:

    Process process;
    public void ExecuteScript(string workingDirectory, string batchFileName)
    {
        if (process != null)
            process.Dispose();
        
        process = new Process();
        process.StartInfo.WorkingDirectory = workingDirectory;
        process.StartInfo.FileName = workingDirectory + batchFileName;
        process.StartInfo.Arguments = "";
     
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.UseShellExecute = false;

        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;
        process.EnableRaisingEvents = true;
        process.OutputDataReceived += proc_OutputDataReceived;

        process.Start();
        process.BeginOutputReadLine();
        process.Exited += OnProcessExit;
    }

    private void OnProcessExit(object sender, EventArgs e)
    {
        Console.WriteLine("the script has ended");
    }
   
    private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        this.Invoke((Action)(() =>
        {
            textBox1.AppendText(Environment.NewLine + e.Data);

        }));

        (sender as Process)?.StandardInput.WriteLine();
    } 

我的批处理文件如下所示:

@echo off
echo This is a running script
timeout /t 10
echo Done sleeping. Will Exit
exit

我可以调用适当的设置组合来防止命令窗口出现,同时仍然重定向输出并正确执行脚本吗?

【问题讨论】:

    标签: c# winforms batch-file process


    【解决方案1】:

    您的代码的问题是重定向标准输入时不支持timeout 命令。这是一个很好的例子,说明为什么应该总是 重定向 stdout 和 stderr。批处理文件实际上发出了一条错误消息,但是因为您没有捕获 stderr 流,所以您没有看到错误消息。 Stack Overflow 上的太多问题涉及 Process 场景,如果人们查看 stderr 输出,就可以轻松解决“不起作用”的场景。

    解决timeout 命令的这个限制的方法是使用waitfor 命令,使用一个已知不存在的信号名称和一个超时值,例如waitfor /t 10 placeholder.

    这是一个完全独立的控制台程序,它演示了重定向标准输入时timeout 命令的失败,以及waitfor 的解决方法:

        const string batchFileText =
    @"@echo off
    echo Starting batch file
    timeout /t 5
    waitfor /t 5 placeholder
    echo Timeout completed
    exit";
    
        static void Main(string[] args)
        {
            string batchFileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".bat");
    
            try
            {
                File.WriteAllText(batchFileName, batchFileText);
    
                ProcessStartInfo psi = new ProcessStartInfo
                {
                    FileName = batchFileName,
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardInput = true,
                    RedirectStandardError = true,
                };
                Process process = new Process
                {
                    EnableRaisingEvents = true,
                };
    
                process.OutputDataReceived += Process_OutputDataReceived;
                process.ErrorDataReceived += Process_ErrorDataReceived;
                process.Exited += Process_Exited;
                process.StartInfo = psi;
                process.Start();
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();
            }
            finally
            {
                File.Delete(batchFileName);
            }
        }
    
        private static void Process_Exited(object sender, EventArgs e)
        {
            WriteLine("Process exited");
        }
    
        private static void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                WriteLine($"stdout: {DateTime.Now:HH:mm:ss.sss}: {e.Data}");
            }
        }
    
        private static void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                WriteLine($"stderr: {DateTime.Now:HH:mm:ss.sss}: {e.Data}");
            }
        }
    

    请注意,如果发生超时,waitfor 命令会向 stderr 写入一条消息(在这种情况下总是如此)。您可能希望也可能不希望它出现在捕获的 stderr 流中。如果没有,您可以使用2>nul 专门重定向该命令的标准错误。例如。 waitfor /t 10 placeholder 2>nul.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 2016-09-09
      • 2012-12-26
      • 1970-01-01
      相关资源
      最近更新 更多