C#在读取async时等待shell命令返回
我需要从C#应用程序运行一个外部进程,但要等待它返回,然后再继续执行。同时,我需要从stdout获取并更新一个包含进度信息的文本框。由于该命令可能会运行几分钟并在这段时间内打印信息,因此显示该命令是绝对必要的;但多个命令可能会运行,并且必须有序,因此等待也是如此 我尝试使用:C#在读取async时等待shell命令返回,c#,asynchronous,C#,Asynchronous,我需要从C#应用程序运行一个外部进程,但要等待它返回,然后再继续执行。同时,我需要从stdout获取并更新一个包含进度信息的文本框。由于该命令可能会运行几分钟并在这段时间内打印信息,因此显示该命令是绝对必要的;但多个命令可能会运行,并且必须有序,因此等待也是如此 我尝试使用: p = Process.Start(); p.BeginOutputReadLine(); p.WaitForExit(); 但这会在等待时冻结UI线程,并阻止输出出现。这: p.Start(); p.BeginOutp
p = Process.Start();
p.BeginOutputReadLine();
p.WaitForExit();
但这会在等待时冻结UI线程,并阻止输出出现。这:
p.Start();
p.BeginOutputReadLine();
while (!p.HasExited)
{
Application.DoEvents();
Thread.Sleep(100);
}
效果更好,但完全错误/一个坏主意,实际上没有等待整个周期
我还曾短暂尝试使用BackgroundWorker启动shell进程,但我不确定如何让UI线程在不阻塞worker的情况下等待它完成
我想做的是提供一个类似于ShowDialog()
的DialogResult ShellExecute(String cmd)
,如果用户允许命令完成、单击取消或命令的返回代码,则返回OK/Cancel(/fail)结果。在命令完成(或取消)之前,它不应返回
所有shell命令都使用以下命令运行:
ProcessStartInfo info = new ProcessStartInfo
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = "cmd.exe",
Arguments = "/c " + command
};
要重定向输出并有效地执行shell命令
如何正确地创建一个启动异步进程的函数,但仍然要等到异步进程完成后才能返回?从ProcessStartInfo创建进程 澄清:ProcessStartinfo si需要如下内容
si.CreateNoWindow=true;
si.RedirectStandardOutput=true;
si.RedirectStandardError=true;
si.StandardOutputEncoding=Encoding.UTF8;
si.StandardErrorEncoding=Encoding.UTF8;
si.WindowStyle=ProcessWindowStyle.Hidden;
然后以前
p.Start();
使用
与
修改了大部分代码以使其正常工作
ProgressForm
类提供了一个QueueCommand
方法,获取所需的shell命令和pre/post委托。显示时,进度表单使用后台工作程序执行每个命令,处理返回代码,并在适当时执行下一个命令
后台工作程序等待每个shell命令完成(Process.WaitForExit()
),同时异步提供UI线程输出。完成后,它调用一个方法,启用successful/ok按钮并隐藏进度条
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
int exec = 1;
while (CommandQueue.Count > 0)
{
if (e.Cancel)
{
e.Result = 1;
return;
}
WriteLine("Running command {0}, {1} remaining.", exec++, CommandQueue.Count);
StagedCommand command = CommandQueue.Peek();
try
{
if (command.Pre != null) command.Pre();
int result = ShellExec(command.Command);
if (command.Post != null) command.Post();
CommandQueue.Dequeue();
if (result != 0)
{
e.Result = result;
return;
}
}
catch (Exception exc)
{
WriteLine("Error: {0}", exc.Message);
e.Result = 1;
return;
}
}
WriteLine("All commands executed successfully.");
e.Result = 0;
return;
}
int ShellExec(String command)
{
WriteLine(command);
Style = ProgressBarStyle.Marquee;
ProcessStartInfo info = new ProcessStartInfo
{
UseShellExecute = false,
LoadUserProfile = true,
ErrorDialog = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.UTF8,
RedirectStandardError = true,
StandardErrorEncoding = Encoding.UTF8,
FileName = "cmd.exe",
Arguments = "/c " + command
};
Process shell = new Process();
shell.StartInfo = info;
shell.EnableRaisingEvents = true;
shell.ErrorDataReceived += new DataReceivedEventHandler(ShellErrorDataReceived);
shell.OutputDataReceived += new DataReceivedEventHandler(ShellOutputDataReceived);
shell.Start();
shell.BeginErrorReadLine();
shell.BeginOutputReadLine();
shell.WaitForExit();
return shell.ExitCode;
}
显然,如果要从StdErr捕获数据,则必须使用p.ErrorDataReceievd+=errorHandler重复此练习。这不会阻止函数创建和启动进程返回。我已经成功地重定向了输出,我更加关注无阻塞的等待。
private static void OutputHandler(object theProcess, DataReceivedEventArgs evtdata)
{
//evtdata.Data has the output data
//use it, display it, or discard it
}
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
int exec = 1;
while (CommandQueue.Count > 0)
{
if (e.Cancel)
{
e.Result = 1;
return;
}
WriteLine("Running command {0}, {1} remaining.", exec++, CommandQueue.Count);
StagedCommand command = CommandQueue.Peek();
try
{
if (command.Pre != null) command.Pre();
int result = ShellExec(command.Command);
if (command.Post != null) command.Post();
CommandQueue.Dequeue();
if (result != 0)
{
e.Result = result;
return;
}
}
catch (Exception exc)
{
WriteLine("Error: {0}", exc.Message);
e.Result = 1;
return;
}
}
WriteLine("All commands executed successfully.");
e.Result = 0;
return;
}
int ShellExec(String command)
{
WriteLine(command);
Style = ProgressBarStyle.Marquee;
ProcessStartInfo info = new ProcessStartInfo
{
UseShellExecute = false,
LoadUserProfile = true,
ErrorDialog = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.UTF8,
RedirectStandardError = true,
StandardErrorEncoding = Encoding.UTF8,
FileName = "cmd.exe",
Arguments = "/c " + command
};
Process shell = new Process();
shell.StartInfo = info;
shell.EnableRaisingEvents = true;
shell.ErrorDataReceived += new DataReceivedEventHandler(ShellErrorDataReceived);
shell.OutputDataReceived += new DataReceivedEventHandler(ShellOutputDataReceived);
shell.Start();
shell.BeginErrorReadLine();
shell.BeginOutputReadLine();
shell.WaitForExit();
return shell.ExitCode;
}