简单描述一下该示例中代码执行的操作:
外壳命令(cmd.exe
) 首先运行,使用start /WAIT
作为参数。或多或少有相同的功能/k
:控制台在没有任何特定任务的情况下启动,等待发送命令时处理命令。
StandardOutput, StandardError and StandardInput全部重定向,设置重定向标准输出, 重定向标准错误 and 重定向标准输入的属性进程启动信息 to true
.
写入控制台输出流时,将引发输出接收到的数据事件;它的内容可以从e.Data
的成员数据接收事件参数.
StandardError
将使用其收到错误数据出于同一目的而举办的活动。
您可以对这两个事件使用单个事件处理程序,但是经过一些测试后,您可能会意识到这可能不是一个好主意。将它们分开可以避免一些奇怪的重叠,并可以轻松区分错误和正常输出(请注意,您可以找到写入错误流而不是输出流的程序)。
StandardInput
可以重定向将其分配给流写入器 stream.
每次将字符串写入流时,控制台都会将该输入解释为要执行的命令。
此外,该进程还被指示提高它的Exited事件终止时,设置其启用引发事件财产给true
.
The Exited
当进程关闭时会引发事件,因为Exit
命令被处理或调用.Close()方法(或者最终,.Kill()方法,仅当进程由于某种原因不再响应时才应使用)。
由于我们需要将控制台输出传递给一些 UI 控件(RichTextBoxes
在此示例中)并且 Process 事件在 ThreadPool 线程中引发,我们必须将此上下文与 UI 同步。
这可以使用流程来完成同步对象属性,将其设置为父窗体或使用Control.BeginInvoke方法,它将在控件句柄所属的线程上执行委托函数。
在这里,一个方法调用者代表代表就是用于此目的。
用于实例化 Process 并设置其属性和事件处理程序的核心函数:
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
public partial class frmCmdInOut : Form
{
Process cmdProcess = null;
StreamWriter stdin = null;
public frmCmdInOut() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
rtbStdIn.Multiline = false;
rtbStdIn.SelectionIndent = 20;
}
private void btnStartProcess_Click(object sender, EventArgs e)
{
btnStartProcess.Enabled = false;
StartCmdProcess();
btnEndProcess.Enabled = true;
}
private void btnEndProcess_Click(object sender, EventArgs e)
{
if (stdin.BaseStream.CanWrite) {
stdin.WriteLine("exit");
}
btnEndProcess.Enabled = false;
btnStartProcess.Enabled = true;
cmdProcess?.Close();
}
private void rtbStdIn_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter) {
if (stdin == null) {
rtbStdErr.AppendText("Process not started" + Environment.NewLine);
return;
}
e.Handled = true;
if (stdin.BaseStream.CanWrite) {
stdin.Write(rtbStdIn.Text + Environment.NewLine);
stdin.WriteLine();
// To write to a Console app, just
// stdin.WriteLine(rtbStdIn.Text);
}
rtbStdIn.Clear();
}
}
private void StartCmdProcess()
{
var pStartInfo = new ProcessStartInfo {
FileName = "cmd.exe",
// Batch File Arguments = "/C START /b /WAIT somebatch.bat",
// Test: Arguments = "START /WAIT /K ipconfig /all",
Arguments = "START /WAIT",
WorkingDirectory = Environment.SystemDirectory,
// WorkingDirectory = Application.StartupPath,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
};
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;
// stdin.AutoFlush = true; <- already true
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) => {
stdin?.Dispose();
cmdProcess?.Dispose();
};
}
}
由于 StandardInput 已被重定向到 StreamWriter:
stdin = cmdProcess.StandardInput;
我们只需写入 Stream 来执行命令:
stdin.WriteLine(["Command Text"]);
![Console redirection in real time](https://i.stack.imgur.com/76r4E.gif)
样本表格可以是从 PasteBin 下载.
VB.Net版本
控件名称:
rtbStdOut
-> RichTextBox(蓝色背景),接收StdOut
rtbStdErr
-> RichTextBox(中间),接收StdErr
rtbStdIn
-> RichTextBox(底部),写入 StdIn
btnStartProcess
-> 按钮(右侧),启动进程
btnEndProcess
-> 按钮(左侧),停止进程
从 Google 云端硬盘下载此表格
Imports System.Diagnostics
Imports System.IO
Public Class frmCmdInOut
Private cmdProcess As Process = Nothing
Private stdin As StreamWriter = Nothing
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
rtbStdIn.Multiline = False
rtbStdIn.SelectionIndent = 20
End Sub
Private Sub btnStartProcess_Click(sender As Object, e As EventArgs) Handles btnStartProcess.Click
btnStartProcess.Enabled = False
StartCmdProcess(Me)
btnEndProcess.Enabled = True
End Sub
Private Sub btnEndProcess_Click(sender As Object, e As EventArgs) Handles btnEndProcess.Click
If stdin.BaseStream IsNot Nothing AndAlso stdin.BaseStream.CanWrite Then stdin.WriteLine("exit")
btnEndProcess.Enabled = False
btnStartProcess.Enabled = True
cmdProcess?.Close()
End Sub
Private Sub rtbStdIn_KeyPress(sender As Object, e As KeyPressEventArgs) Handles rtbStdIn.KeyPress
If e.KeyChar = ChrW(Keys.Enter) Then
If stdin Is Nothing Then
rtbStdErr.AppendText("Process not started" + Environment.NewLine)
Return
End If
e.Handled = True
If stdin.BaseStream.CanWrite Then
stdin.Write(rtbStdIn.Text + Environment.NewLine)
stdin.WriteLine() ' To write to a Console app, just stdin.WriteLine(rtbStdIn.Text);
End If
rtbStdIn.Clear()
End If
End Sub
Private Sub StartCmdProcess(synchObj As Control)
' Arguments = $"start /WAIT cscript.exe script.vbs /xpr",
' Batch File Arguments = "/C START /b /WAIT batchfile.bat",
' Test: Arguments = "START /WAIT /K ipconfig /all",
' start with /U
' StandardErrorEncoding = Encoding.Unicode,
' StandardOutputEncoding = Encoding.Unicode,
Dim pStartInfo = New ProcessStartInfo() With {
.FileName = "cmd.exe",
.Arguments = "START /WAIT",
.CreateNoWindow = True,
.RedirectStandardError = True,
.RedirectStandardInput = True,
.RedirectStandardOutput = True,
.UseShellExecute = False,
.WindowStyle = ProcessWindowStyle.Hidden,
.WorkingDirectory = Application.StartupPath
}
cmdProcess = New Process() With {
.EnableRaisingEvents = True,
.StartInfo = pStartInfo,
.SynchronizingObject = synchObj
}
cmdProcess.Start()
cmdProcess.BeginErrorReadLine()
cmdProcess.BeginOutputReadLine()
stdin = cmdProcess.StandardInput
AddHandler cmdProcess.OutputDataReceived,
Sub(s, evt)
If evt.Data IsNot Nothing Then
rtbStdOut.AppendText(evt.Data + Environment.NewLine)
rtbStdOut.ScrollToCaret()
End If
End Sub
AddHandler cmdProcess.ErrorDataReceived,
Sub(s, evt)
If evt.Data IsNot Nothing Then
rtbStdErr.AppendText(evt.Data + Environment.NewLine)
rtbStdErr.ScrollToCaret()
End If
End Sub
AddHandler cmdProcess.Exited,
Sub(s, evt)
stdin?.Dispose()
cmdProcess?.Dispose()
End Sub
End Sub
End Class