1 回答

TA貢獻1816條經(jīng)驗 獲得超6個贊
代碼在此示例中執(zhí)行的操作的簡要說明:
shell 命令 ( cmd.exe) 首先運行,start /WAIT用作參數(shù)?;蚨嗷蛏倥c以下功能相同/k:控制臺在沒有任何特定任務的情況下啟動,在發(fā)送命令時等待處理命令。
StandardOutput,StandardError并且StandardInput都被重定向,將ProcessStartInfo 的RedirectStandardOutput、RedirectStandardError和RedirectStandardInput屬性設置為。true
控制臺輸出流在寫入時將引發(fā)OutputDataReceived事件;它的內(nèi)容可以從DataReceivedEventArgs的e.Data成員中讀取。將其ErrorDataReceived事件用于相同目的。 您可以對這兩個事件使用單個事件處理程序,但是,經(jīng)過一些測試后,您可能會意識到這可能不是一個好主意。將它們分開可以避免一些奇怪的重疊,并允許輕松區(qū)分錯誤與正常輸出(注意,您可以找到寫入錯誤流而不是輸出流的程序)。
StandardError
StandardInput可以重定向,將其分配給StreamWriter流。
每次將字符串寫入流時,控制臺都會將該輸入解釋為要執(zhí)行的命令。
此外,進程被指示在終止時引發(fā)它的Exited事件,將其EnableRaisingEvents屬性設置為true。
在Exited當該過程被關閉,因為引發(fā)事件Exit命令被處理或調(diào)用.Close()方法(或最終的.Kill()方法,應僅用于whcich僅當一個進程不響應了,對于一些原因)。
由于我們需要將控制臺輸出傳遞給某些 UI 控件(RichTextBoxes在此示例中)并且 Process 事件在 ThreadPool 線程中引發(fā),因此我們必須將此上下文與 UI 同步。
這可以通過使用 Process SynchronizingObject屬性、將其設置為父窗體或使用Control.BeginInvoke方法來完成,該方法將在控件句柄所屬的線程上執(zhí)行委托函數(shù)。
在這里,代表委托的MethodInvoker用于此目的。
用于實例化 Process 并設置其屬性和事件處理程序的核心函數(shù):
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
StreamWriter stdin = null;
public partial class frmCmdInOut : Form
{
Process cmdProcess = null;
StreamWriter stdin = null;
public frmCmdInOut() => InitializeComponent();
private void MainForm_Load(object sender, EventArgs 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 以執(zhí)行命令:
stdin.WriteLine(["Command Text"]);
示例表單可以從 PasteBin 下載。
- 1 回答
- 0 關注
- 289 瀏覽
添加回答
舉報