Cyb*_*ops 1 .net c# process winforms
从网络上的各种来源中,我整理了以下代码,用于通过CMD.exe和捕获来自STDOUT和的输出来执行命令STDERR。
public static class Exec
{
public delegate void OutputHandler(String line);
// <summary>
/// Run a command in a subprocess
/// </summary>
/// <param name="path">Directory from which to execute the command</param>
/// <param name="cmd">Command to execute</param>
/// <param name="args">Arguments for command</param>
/// <param name="hndlr">Command output handler (null if none)</param>
/// <param name="noshow">True if no windows is to be shown</param>
/// <returns>Exit code from executed command</returns>
public static int Run(String path, String cmd, String args,
OutputHandler hndlr = null, Boolean noshow = true)
{
// Assume an error
int ret = 1;
// Create a process
using (var p = new Process())
{
// Run command using CMD.EXE
// (this way we can pipe STDERR to STDOUT so they can get handled together)
p.StartInfo.FileName = "cmd.exe";
// Set working directory (if supplied)
if (!String.IsNullOrWhiteSpace(path)) p.StartInfo.WorkingDirectory = path;
// Indicate command and arguments
p.StartInfo.Arguments = "/c \"" + cmd + " " + args + "\" 2>&1";
// Handle noshow argument
p.StartInfo.CreateNoWindow = noshow;
p.StartInfo.UseShellExecute = false;
// See if handler provided
if (hndlr != null)
{
// Redirect STDOUT and STDERR
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
// Use custom event handler to capture output
using (var outputWaitHandle = new AutoResetEvent(false))
{
p.OutputDataReceived += (sender, e) =>
{
// See if there is any data
if (e.Data == null)
{
// Signal output processing complete
outputWaitHandle.Set();
}
else
{
// Pass string to string handler
hndlr(e.Data);
}
};
// Start process
p.Start();
// Begin async read
p.BeginOutputReadLine();
// Wait for process to terminate
p.WaitForExit();
// Wait on output processing complete signal
outputWaitHandle.WaitOne();
}
}
else
{
// Start process
p.Start();
// Wait for process to terminate
p.WaitForExit();
}
// Get exit code
ret = p.ExitCode;
}
// Return result
return ret;
}
// <summary>
/// Run a command in a subprocess and return output in a variable
/// </summary>
/// <param name="path">Directory from which to execute the command</param>
/// <param name="cmd">Command to execute</param>
/// <param name="args">Arguments for command</param>
/// <param name="outp">Variable to contain the output</param>
/// <returns>Exit code from executed command</returns>
public static GetOutputReturn GetOutput(String path, String cmd, String args)
{
GetOutputReturn ret = new GetOutputReturn();
ret.ReturnCode = Run(path, cmd, args, (line) =>
{
ret.Output.AppendLine(line);
});
return ret;
}
}
public class GetOutputReturn
{
public StringBuilder Output = new StringBuilder();
public int ReturnCode = 1;
}
Run Code Online (Sandbox Code Playgroud)
我可以通过以下三种不同方式在控制台应用程序中使用它:
static void Main(string[] args)
{
int ret;
Console.WriteLine("Executing dir with no capture and no window");
ret = Exec.Run(@"C:\", "dir", "");
Console.WriteLine("Execute returned " + ret);
Console.WriteLine("Press enter to continue ...");
Console.ReadLine();
Console.WriteLine("Executing dir with no capture and window");
ret = Exec.Run(@"C:\", "dir", "", null, false);
Console.WriteLine("Execute returned " + ret);
Console.WriteLine("Press enter to continue ...");
Console.ReadLine();
Console.WriteLine("Executing dir with capture and no window");
var results = Exec.GetOutput(@"C:\", "dir", "");
Console.WriteLine(results.Output.ToString());
Console.WriteLine("Execute returned " + results.ReturnCode);
Console.ReadLine();
Console.WriteLine("Executing dir with real-time capture and no window");
ret = Exec.Run(@"C:\", "dir", "", ShowString);
Console.WriteLine("Execute returned " + ret);
}
public delegate void StringData(String str);
static void ShowString(String str)
{
Console.WriteLine(str);
}
public delegate void StringData(String str);
static void ShowString(String str)
{
Console.WriteLine(str);
}
Run Code Online (Sandbox Code Playgroud)
第一次运行不收集任何输出,仅显示退出代码。
第二次运行不收集任何输出,但显示窗口。
输出将实时显示在控制台窗口中的效果。
第三次运行使用GetOutput收集输出。
这样的结果是直到运行完成才出现输出。
上一次运行使用处理程序来接收和实时显示输出。
从外观上看,它看起来像第二轮,但完全不同。
对于接收到的每一行输出,将调用ShowString。
显示字符串仅显示字符串。
但是,它可以处理数据所需的任何操作。
我正在尝试调整上一次运行的时间,以便可以使用命令的输出实时更新文本框。我遇到的问题是如何在正确的背景下获得它(因为缺乏更好的期限)。因为OutputHandler是异步调用的,所以它必须使用该InvokeRequired/BeginInvoke/EndInvoke机制与UI线程同步。我对如何使用参数有一点问题。在我的代码中,textBox可能是选项卡控件中的几个,因为可能会发生多个背景“运行”。
到目前为止,我有这个:
private void btnExecute_Click(object sender, EventArgs e)
{
// Get currently selected tab page
var page = tcExecControl.SelectedTab;
// Get text box (always 3rd control on the page)
var txt = (TextBox)page.Controls[2];
// Create string handler
var prc = new Exec.OutputHandler((String line) =>
{
if (txt.InvokeRequired)
txt.Invoke(new MethodInvoker(() =>
{ txt.Text += line; }));
else txt.Text += line;
});
// Command and arguments are always 1st and 2nd controls on the page
var result = Exec.Run(@"C:\", page.Controls[0].Text, page.Controls[1], prc);
}
Run Code Online (Sandbox Code Playgroud)
但这似乎不起作用。我看不到txtBox的任何输出。
实际上,程序基本上挂在处理程序中。
如果我更改代码以使用GetOutput,然后将结果输出写入文本框,则一切正常。所以我知道我已经正确设置了命令。使用调试器,我可以在“ if(txt.InvokeRequired)”行上设置断点,并且看到输出的第一行正确显示。此时,代码采用if语句的真实路径,但是如果我在该txt.Text += line;行上设置断点,它将永远不会到达那里。
谁能帮我吗?我确定我想念什么。
此示例中代码执行的简要说明。
首先将shell命令(cmd.exe)start /WAIT作为参数运行。几乎与以下功能相同/k:在没有任何特定任务的情况下启动控制台,等待发送命令时处理命令。
StandardOutput,StandardError并StandardInput都重定向,设置RedirectStandardOutput,RedirectStandardError和RedirectStandardInput所述的性质的ProcessStartInfo到true。
控制台输出流在写入时将引发OutputDataReceived 事件。它的内容可以从DataReceivedEventArgs的e.Data成员中读取。出于相同的目的,将使用其ErrorDataReceived事件。
(您可以对两个事件使用单个事件处理程序,但是经过一些测试,您可能会意识到这不是一个好主意。将它们分开可以避免一些奇怪的重叠,并可以轻松地将错误与正常输出区分开来)。
StandardError
StandardInput可以重定向,将其分配给StreamWriter流。
每次将字符串写入流时,控制台都会将该输入解释为要执行的命令。
此外,还指示该进程在终止时引发其Exited事件,并将其EnableRaisingEvents属性设置为true。当关闭进程时会引发
该Exited事件,因为Exit处理了一条命令,调用了.Close()方法或.Kill()方法。仅出于某种原因,当流程不再响应时,才应使用
该.Kill()方法。
由于我们需要将控制台输出传递给某些UI控件(RichTextBoxes在本示例中),因此必须将事件上下文与UI同步。
可以使用Process SynchronizingObject属性,将其设置为UI控件(容器Window可能是首选,因为有多个控件要同步),或者使用Control.BeginInvoke方法,可以在以下位置执行委托函数:控件的句柄所属的线程。
此处,代表委托的MethodInvoker用于此目的。
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
StreamWriter stdin = null;
private void StartCmdProcess()
{
ProcessStartInfo pStartInfo = new ProcessStartInfo {
FileName = "cmd.exe",
Arguments = "start /WAIT",
WorkingDirectory = Environment.SystemDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
};
Process cmdProcess = new Process {
StartInfo = pStartInfo,
EnableRaisingEvents = true,
// Test without and with this
// When SynchronizingObject is set, no need to BeginInvoke()
//SynchronizingObject = this
};
cmdProcess.Start();
cmdProcess.BeginErrorReadLine();
cmdProcess.BeginOutputReadLine();
stdin = cmdProcess.StandardInput;
cmdProcess.OutputDataReceived += (s, evt) => {
if (evt.Data != null)
{
BeginInvoke(new MethodInvoker(() => {
rtbStdOut.AppendText(evt.Data + Environment.NewLine);
rtbStdOut.ScrollToCaret();
}));
}
};
cmdProcess.ErrorDataReceived += (s, evt) => {
if (evt.Data != null) {
BeginInvoke(new Action(() => {
rtbStdErr.AppendText(evt.Data + Environment.NewLine);
rtbStdErr.ScrollToCaret();
}));
}
};
cmdProcess.Exited += (s, evt) => {
cmdProcess.Close();
if (cmdProcess != null) {
cmdProcess.Dispose();
}
};
}
Run Code Online (Sandbox Code Playgroud)
样本表格可以从PasteBin下载。