c# - 从 C# winforms 执行批处理文件忽略超时
问题描述
所有,我正在尝试通过 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
是否有适当的设置组合我可以调用以防止出现命令窗口,同时仍重定向输出并正确执行脚本?
解决方案
timeout
您的代码的问题是重定向标准输入时不支持该命令。这是一个很好的例子,说明为什么应该始终重定向 stdout 和 stderr。批处理文件实际上发出了一条错误消息,但是因为您没有捕获 stderr 流,所以您没有看到错误消息。如果人们查看了 stderr 输出,那么Stack Overflow 上涉及Process
“不起作用”的场景的所有问题都可以轻松解决。
一个解决该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
。
推荐阅读
- android - 快速点击 Android 的概览按钮两次时发生内部异常/崩溃
- java - 将 JTextField 文本放入 JList
- android - Room 预打包的数据库具有无效的架构
- sql - 在 Access 2016 中按组计算总百分比
- javascript - 如果不能全部放在一个上,则将一半的单词放到下一行
- apache - Apache中的RewriteRule重写URL不起作用
- javascript - How to populate select option:select dynamically
- postgresql - 通过函数表达式使 Postgresql INDEX 覆盖顺序
- python - 将年龄分类到另一个列年龄组
- c++ - 从 c++ 运行的进程偶尔会错过一个选项