How to display output of iperf cmd prompt in textbox - c#

I am using iperf-2.0.5-2-win32 tool to find network bandwidth. I have written codes in c# which opens the cmd prompt, pass iperf parameters to start server side & client side. iperf-2.0.5-2-win32 exe will not open directly, need to open through cmd prompt only.
At present the output(Transfer rate & Bandwidth) is displaying on cmd prompt itself. I want these output to be displayed in textbox
I have tried StreamReader also. But it takes null, I have also tried OutputDataReceived Event, its also taking null.
Found few codes for ipconfig & ping.but those were not working with iperf codes.
button_click event(),
{
Process Client_proc = new Process();
ProcessStartInfo Client_command = new ProcessStartInfo("cmd.exe");
string ip = txtIP.Text;
Client_command.CreateNoWindow = true;
Client_command.WindowStyle = ProcessWindowStyle.Hidden;
Client_command.WorkingDirectory = #"E:\Iperf\RunEXE_Through_Application\iperf-2.0.5-2-win32";
Client_command.Arguments = "/c START iperf -c " + ip;
Client_proc.StartInfo = Client_command;
Client_command.RedirectStandardOutput = true;
Client_command.UseShellExecute = false;
Client_proc.OutputDataReceived += new DataReceivedEventHandler(Client_proc_OutputDataReceived);
Client_proc.Start();
Client_proc.BeginOutputReadLine();
Client_proc.WaitForExit();
}
void Client_proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => txtOutput.Text += newLine;
txtOutput.BeginInvoke(append);
}
}
Plz help me.Earlier responses are appreciated
Thanks

you use this complete code for your disposal
It is not perfect (some problems when using multiple streams )
public void RunProcess(string FileName, string Arguments, bool EventWhenExit )
{
process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.LoadUserProfile = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName; // Gets or sets the application or document to start.
process.StartInfo.Arguments = Arguments;//Gets or sets the set of command-line arguments to use when starting the application
Thread.Sleep(1000);
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);/*New line */
}
process.Start();
process.BeginOutputReadLine();
PID = process.Id;
}
private void myprocess_Exited(object sender, EventArgs e)
{
process.Refresh();
Thread.Sleep(2000);
onProcessEnd(this, "ENDOF " + Proc.ToString());
Console.WriteLine("Process exsiting ");
}
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string OutputFromProcess = e.Data;
//fire event to event handler class for further use
onDataOutputFromProcess(this, OutputFromProcess, Proc.ToString());
}
than in your GUI layer you should bind to onDataOutputFromProcess event
there you should have something like
if (screenToPrint.InvokeRequired) //&& this.Visible)
{
try
{
this.Invoke(new Action<AppendToScreenParam>(AppendTextFullConfig), new object[] { append });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return;
}
else
{
screenToPrint.SelectionFont = font;
screenToPrint.SelectionColor = append.Color;
//screenToPrint.AppendText(append.Message);
string TextToPrint = string.Format("{0}\n", append.Message);
screenToPrint.AppendText(TextToPrint);
}
}

Maybe it is because iperf process is returning error. Subscribe the ErrorDataReceived event with Client_proc.ErrorDataReceived += Client_proc_ErrorDataReceived; and see the results. If command returns error, you can see the error message as output.
void Client_proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.txtOutput.BeginInvoke(new MethodInvoker(() => { this.txtOutput.Text = e.Data; }));
}
}

Related

Create and actively use Process without freezing main thread

I'm currently trying to create new NodeJS process and while it's running, put it's console output into my winform textbox.
Whenever this process is executed, it's freezing main thread as if form is waiting for this process to exit. After the process is closed thats when the console output is added to the textbox.
What I'm trying to achieve is simultaneously have this node process running in the background and have whatever it's outputing in the textbox.
Edit 1:
I Managed to run the console without freezing main thread but the output only shows when the process is closed
My current code:
private void Btn_connect_Click(object sender, EventArgs e)
{
if(backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.Start();
while (worker.CancellationPending != true)
{
Thread.Sleep(200);
AddText(nodeProcess.StandardOutput.ReadToEnd());
worker.ReportProgress(1);
}
e.Cancel = true;
}
public void AddText(string text)
{
if(txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { text });
return;
}
txt_log.Text += "\n " + text;
}
Instead of a BackgroundWorker you could try using Process.BeginOutputReadLine, Process.OutputDataReceived, and Process.Exited.
void StartProcess()
{
Process nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.EnableRaisingEvents = true;
nodeProcess.Exited += nodeProcess_Exited;
nodeProcess.OutputDataReceived += nodeProcess_OutputDataReceived;
nodeProcess.Start();
nodeProcess.BeginOutputReadLine();
}
void nodeProcess_Exited(object sender, EventArgs e)
{
// Do something when the process exits, if you need to.
// You'll want to check InvokeRequired before you modify any of your form's controls.
}
void nodeProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { e.Data });
return;
}
txt_log.Text += "\n " + e.Data;
}

Duplicate process start doesn't react as expected

i'm trying to open a process with c# and react to it, when it's been closed. This work's for me:
private void StartProc()
{
var process = new System.Diagnostics.Process { StartInfo = { FileName = "PathTo.exe" } };
process.Start();
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
private void Editor_Exited(object sender, EventArgs e)
{
MessageBox.Show("Process canceled");
}
Lets say I'm opening a text editor with this code. If there is already an instance of this text editor the code won't open a second instance and also jumps instant in the Editor_Exited Code.
I want the code to open a new instance and don't jump in the Editor_Exited code.
string processName = "PathTo.exe";
var process = new System.Diagnostics.Process { StartInfo = { FileName = processName } };
if (process.Start())
{
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
else
{
var p = Process.GetProcessesByName(processName);
p.WaitForExit();
}
I get this is not 100% what you are asking for, but its a work around

How to get the the result of batch file to C#

I am currently struggling with bringing existing data in batch file to C# window form
The whole objective is getting a result lively from batch file to C# rich text box but I am keep getting failure of doing it.
The procedure works like click button->run batch file secretly->C# gets data lively->display in rich text box
I was successful to run a batch file but it runs in another new CMD causing hanging problem during debugging.
I would like to know whether anyone can wrote me a code to overcome such problem. Hope for the best answer
ProcessStartInfo cmd = new ProcessStartInfo();
Process process = new Process();
cmd.FileName = #"cmd";
cmd.UseShellExecute = false;
cmd.RedirectStandardError = true;
cmd.RedirectStandardInput = true;
cmd.RedirectStandardOutput = true;
cmd.CreateNoWindow = true;
process.EnableRaisingEvents = false;
process.StartInfo = cmd;
process.Start();
process.StandardInput.Write(#"cd C:\Users\%username%\Desktop\Claymore's Dual Ethereum+Decred_Siacoin_Lbry_Pascal AMD+NVIDIA GPU Miner v11.0" + Environment.NewLine);
process.StandardInput.Write(#"EthDcrMiner64.exe -allpools 1 -epool asia1.ethereum.miningpoolhub.com:20535 -ewal AJStudio.AJStudio001 -epsw x -esm 2" + Environment.NewLine);
process.StandardInput.Close();
string result = process.StandardOutput.ReadToEnd();
StringBuilder sb = new StringBuilder();
sb.Append("[result info]" + DateTime.Now + "\r\n");
sb.Append(result);
sb.Append("\r\n");
richTextBox1.Text = sb.ToString();
process.WaitForExit();
process.Close();
To get real-time feedback from Process, use OutputDataReceived and ErrorDataReceived events. Something like this:
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += Process_OutputDataReceived;
process.ErrorDataReceived += Process_ErrorDataReceived;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) return;
log("ERROR: " + e.Data);
}
private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) return;
log(e.Data);
}

C# WPF application capture python script console output

I have been trying for hours to figure out how to do this but I have a python script that was made into an exe so it acts a console application I'm trying to write a GUI wrapper for it using WPF I have it set up to where it does execute the exe with the command arguments but I want to capture the output from the console and display it in a text box and i can not figure it out. I have tried multiple code snippets but it either does nothing, Outputs after the python exe has finished, or locks up the GUI until the python exe finishes then dumps the completed output to the textbox.
Would someone be able to take a look and see if they can help me with this?
public partial class MainWindow : Window
{
//string output = string.Empty;
private static StringBuilder output = new StringBuilder();
private object syncGate = new object();
private Process process;
private bool outputChanged;
public MainWindow()
{
InitializeComponent();
}
private void RB_Mii_Checked(object sender, RoutedEventArgs e)
{
}
//If we click the button we copy the bin file to the work directory
private void btn_SelMiiQR_Click(object sender, RoutedEventArgs e)
{
//Copy the encrypted.bin file to the working directory
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Input.bin (*.bin)|*.bin|All files (*.*)|*.*";
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (openFileDialog.ShowDialog() == true)
{
var fileName = openFileDialog.FileName;
String exePath = System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;
//If the file exists delete the existing file and copy the newone.
if (System.IO.File.Exists(System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName)))
{
System.IO.File.Delete(System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName));
}
System.IO.File.Copy(fileName, System.IO.Path.GetDirectoryName(exePath) + "\\App\\" + System.IO.Path.GetFileName(fileName));
}
}
//If the button was clicked use the input.bin file and attempt to brute force the movable_sedpart1.bin
private void BTN_MIIBF_Click(object sender, RoutedEventArgs e)
{
//If the mfg has input year or no input use it
if (TB_MFGYR.Text.Length == 0 || TB_MFGYR.Text.Length == 4)
{
string DStype = null;
string MFGYR = null;
//Grab the Year if it has value
if (TB_MFGYR.Text.Length == 4)
{
MFGYR = TB_MFGYR.Text;
}
else
{
MFGYR = null;
}
if (RB_N3ds.IsChecked == true)
{
DStype = "new";
}
else if (RB_O3DS.IsChecked == true)
{
DStype = "old";
}
//Execute Command with Arguments
String exePath = System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;
string dir = System.IO.Path.GetDirectoryName(exePath)+"\\App\\";
//Start the process and export thr console output to the textbox
CreateProcess(dir + "seedminer_launcher.exe", "Mii " + DStype + " " + MFGYR, dir);
}
//Else display Error Message WIP
else
{
tb_outputtext.Text = null;
tb_outputtext.Text = "MFG Year must have 4 characters or none";
}
}
//Execute a new process
private void CreateProcess(string fileName, string arguments, string workdir)
{
// Process process = new Process();
process = new Process();
process.StartInfo.FileName = fileName;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WorkingDirectory = workdir;
process.OutputDataReceived += proc_OutputDataReceived;
process.Start();
process.BeginOutputReadLine();
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
tb_outputtext.Text = tb_outputtext.Text + "\n" + e.Data;
tb_outputtext.ScrollToEnd();
}));
}
private void ReadData()
{
var input = process.StandardOutput;
int nextChar;
while ((nextChar = input.Read()) >= 0)
{
lock (syncGate)
{
output.Append((char)nextChar);
if (!outputChanged)
{
outputChanged = true;
var dispatcher = Application.Current.MainWindow.Dispatcher;
Dispatcher.BeginInvoke(new Action(OnOutputChanged));
}
}
}
lock (syncGate)
{
process.Dispose();
process = null;
}
}
private void OnOutputChanged()
{
lock (syncGate)
{
tb_outputtext.AppendText(output.ToString());
outputChanged = false;
}
}
}
If I understand you correctly then you want your WPF app to continously update the content ot the TextBox while your python executable is running?
I have stripped down your code and used the Windows command ping -t 127.0.0.1 -w 10000 which generates a new line every second to test your code. On my machine your code works as expected: the output in the WPF textbox is updated every second.
What happens if you replace the ping command with your python executable in the code below? Does your python script output a newline character after each line (as mentioned in Process.OutputDataReceived Event)?
MainWindow.xaml.cs
using System;
using System.Diagnostics;
using System.Windows;
namespace SO_Continous_Process_Output
{
public partial class MainWindow : Window
{
private Process process;
public MainWindow()
{
InitializeComponent();
CreateProcess("ping", "-t 127.0.0.1 -w 1000", "");
}
//Execute a new process
private void CreateProcess(string fileName, string arguments, string workdir)
{
// Process process = new Process();
process = new Process();
process.StartInfo.FileName = fileName;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WorkingDirectory = workdir;
process.OutputDataReceived += proc_OutputDataReceived;
process.Start();
process.BeginOutputReadLine();
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
tb_outputtext.Text = tb_outputtext.Text + "\n" + e.Data;
tb_outputtext.ScrollToEnd();
}));
}
}
}
MainWindow.xaml
<Window x:Class="SO_Continous_Process_Output.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Name="tb_outputtext" Text="{Binding ProcessOutput}"></TextBox>
</Grid>
</Window>
Update
Python script
I wrote a python script to test if the output works and I had to set flush=True in order to make it work.
import time
while True:
print('hi!', flush=True)
time.sleep(1)

How can I feed commands to cmd.exe process via an input stream manually?

The question sounds a bit, dense. Here is a slightly longer version:
I need to have the main loop wait for user input and also have a process running and waiting for input from a stream to which the user input is to be sent.
Full story: I'm building a Cmd emulator and at first everything looked fine: The user enters a command, it gets echoed to the output area, processed and StdOut and StdErrOut are captured and also added to the output TextBox.
The only problem was, that, as the cmd process was created and started separately for each command, no state was kept. Neither variables nor codepage nor working directory etc..
So I decided to invent a little hack: Entering an opening or closing parenthesis starts and stops collecting the commands instead of executing them. After the closing parenthesis the list of commands ('batch') is used in the processBatch method to feed them all to the cmd process vie its redirected input. Worked fine.
The only problem was, obviously, now I got state but lost immediate response, so any errors wouldn't pop up until the batch was run.
So I decided to combine the good parts and, well, I knew I was heading for trouble when I realized, that to keep two loops working & waiting I have to use threading. Which I haven't done in years..
In the layout I chose the main() loop waits for user input and startCMDtask() runs startCMD() in a task. Here the input stream is scanned until is has data and then the cmd process is to process them..
But it doesn't work.
List<string> batch = new List<string>();
public volatile string output = "+";
public volatile string outputErr = "-";
Process CMD;
Task cmdTask;
volatile Queue<string> cmdQueue = new Queue<string>();
volatile public bool CMDrunning = false;
Tthis works just fine
private void processBatch()
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
foreach(string line in batch) sw.WriteLine(line);
}
output = "^"; outputErr = "~";
try { output = p.StandardOutput.ReadToEnd(); } catch { }
try { outputErr = p.StandardError.ReadToEnd(); } catch { }
try { p.WaitForExit(); } catch { }
tb_output.AppendText(output + "\r\n" + outputErr + "\r\n");
}
These don't quite, but almost..
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
// info.Arguments = "/K"; // doesn't make a difference
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.StartInfo = info;
}
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { output = "Error starting cmd process.\r\n"; CMDrunning = false; }
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="")
{
sw.WriteLine(cmd);
processOutputStreams();
}
}
catch {}
} while (CMDrunning);
}
private void processOutputStreams()
{
string newOutput = ""; string newOutputErr = "";
while (CMD.StandardOutput.Peek() > 0)
newOutput += (char)(CMD.StandardOutput.Read());
newOutput += "!?"; // at this point stdout is correctly captured (1)
try {
while (CMD.StandardError.Peek() > 0) // from here execution jumps away (2)
{ newOutputErr += (char)(CMD.StandardError.Read()); }
} catch {
newOutputErr = "?"; // never comes here
}
lock (output) // no noticable difference
lock (outputErr) //
{ // if I jump here (3) from (1) the result is displayed
// but not if i comment out the 2nd while loop (2)
if (newOutput != null & newOutput != "") output += newOutput + "\r\n";
if (newOutputErr != null & newOutputErr != "") outputErr += newOutputErr + "\r\n";
}
}
This is the call from the input processor in the main thread:
lock (cmdQueue) cmdQueue.Enqueue(cmd);
I have no idea which part is the problem: the process, the cmd shell, the input stream, the output stream, the threading, the locks or all of it in turns..??
I finally got it working. The reason for the erratic behaviour I described in the code samples was that the 3 streams were not accessed in an async'ed manner.
To rectify I discarded the processOutput function and replaced it by two calls that the process itself triggers. MS documetation gives a fine example here
I also made the StreamWriter sync, that feeds the process and the whole task it runs in as well.
Here is the new code:
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private async void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { cmdErrOutput.Append("\r\nError starting cmd process.");
CMDrunning = false; }
CMD.BeginOutputReadLine();
CMD.BeginErrorReadLine();
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="") await sw.WriteLineAsync(cmd);
}
catch { }
} while (CMDrunning);
try { CMD.WaitForExit(); }
catch { cmdErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
This is how the process is set up now:
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.OutputDataReceived += new DataReceivedEventHandler(cmdOutputDataHandler);
CMD.ErrorDataReceived += new DataReceivedEventHandler(cmdErrorDataHandler);
cmdOutput = new StringBuilder();
cmdErrOutput = new StringBuilder();
CMD.StartInfo = info;
}
And here are the output handlers:
private static void cmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected output.
cmdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void cmdErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected error output.
cmdErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
At the end of the user input porcessing this is how the input queue is ged and the output fetched:
cmdUnDoStack.Push(cmd);
Application.DoEvents();
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => updateOutputArea(uiScheduler));
Using this little routine:
private void updateOutputArea(TaskScheduler uiScheduler)
{
Task.Factory.StartNew(() =>
{
tb_output.AppendText(cmdOutput + "\r\n" + cmdErrOutput + "\r\n");
cmdOutput.Clear();
cmdErrOutput.Clear();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
And now for the special treament some of the good old commands like CLS or COLOR need.. ;-)

Categories

Resources