I want to create a Command prompt in WPF where the user can type the command and get the output in the same window, exactly how the original Command Prompt works.
Initially I used Process.Start()
and put it into another thread to not block the main thread. I have used StreamReader and StreamWriter to write and capture the output. However I am unable to continuously listen for output or write the commands.
Here's my code till now -
public class TerminalController
{
public Process CmdProcess { get; set; }
public StreamReader Reader { get; set; }
public StreamWriter Writer { get; set; }
public void Init()
{
if (CmdProcess != null)
{
try
{
Close();
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
CmdProcess = new Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "cmd";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
CmdProcess.StartInfo = startInfo;
CmdProcess.Start();
Writer = CmdProcess.StandardInput;
StartReading();
}
public void Init(string command)
{
if (CmdProcess != null)
{
try
{
Close();
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
CmdProcess = new Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "cmd";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
CmdProcess.StartInfo = startInfo;
CmdProcess.Start();
}
private void Write(String command)
{
using (Writer = CmdProcess.StandardInput)
{
Writer.Write(command);
}
}
private void StartReading()
{
using (Reader = CmdProcess.StandardOutput)
{
RaiseTerminalReadEvent(null, Reader.ReadToEnd());
}
}
public async void RunCommand(String command)
{
RaiseTerminalWriteEvent(command);
await Task.Run(() => Write(command));
await Task.Run(() => StartReading());
//Close();
}
public Task InitAsync()
{
return Task.Run(() => Init());
}
public void Close()
{
try
{
CmdProcess.Close();
Reader.Close();
Writer.Close();
CmdProcess = null;
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
internal Task RunCommandAsync(string command)
{
return Task.Run(() => RunCommand(command));
}
}
I can run the commands only once, this is the output when I sent dir command -
dir
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
E:\Microsoft\WPF\Example\Example\bin\Debug>More?
If I send the command again, it gives the following exception -
Cannot write to a closed TextWriter.
at System.IO.__Error.WriterClosed()
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Write(String value)
at Example.Controllers.TerminalController.Write(String command) in e:\Microsoft\WPF\Example\Example\Controllers\TerminalController.cs:line 91
So how can I continuously run the command and listen for output. Do I need to create a new process everytime? Is there any other alternative?
Your
using
blocks dispose the streams as soon as you first use them.Therefore, you can't use them again.
Don't do that.