I have a program that executes a batch file using an asynchronous background worker. Here is the code:
public static void Execute(CmdObj obj, bool batch)
{
CmdObj = obj;
var theWorker = new BackgroundWorker();
theWorker.RunWorkerCompleted += WorkerCompleted;
theWorker.WorkerReportsProgress = true;
if(batch)
{
theWorker.DoWork += WorkerBatchWork;
}else{
theWorker.DoWork += WorkerCommandWork;
}
theWorker.RunWorkerAsync();
}
private static void WorkerBatchWork(object sender, DoWorkEventArgs e)
{
RunBatch(CmdObj);
}
private static void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var temp = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
if (temp != null)
ProgramPath = temp.Substring(6);
WriteLog(CmdObj.Activity, false);
WriteLog(CmdObj.Error, true);
CmdObj.TheButton.Enabled = true;
}
private static void RunBatch(CmdObj obj)
{
var process = new Process();
var startInfo = new ProcessStartInfo
{
FileName = obj.SrcFile,
WindowStyle = ProcessWindowStyle.Normal,
CreateNoWindow = false,
RedirectStandardInput = true,
RedirectStandardOutput = false,
RedirectStandardError = true,
UseShellExecute = false
};
try
{
if (!obj.SrcFile.ToLower().Trim().EndsWith(".bat"))
throw new FileLoadException("Not a valid .bat file",obj.SrcFile);
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//obj.Activity = process.StandardOutput.ReadToEnd();
obj.Error = process.StandardError.ReadToEnd();
}
catch (Exception ex)
{
obj.Exception = ex;
}
finally
{
process.Close();
}
}
class CmdObj
{
public string Command { get; set; }
public string SrcFile { get; set; }
public string Activity { get; set; }
public string Error { get; set; }
public Exception Exception { get; set; }
public Button TheButton { get; set; }
}
So when I run this program and choose a batch file to execute, I get a blank CMD window. Is there some way to show the output in the CMD window when my program executes the batch file? Alternatively, if I could have the CMD output sent to a textbox or some other control (in real-time), that would work as well.
Thanks!
Since you have one of the following properties set to true, you'll get a blank black Window
RedirectStandardInput
RedirectStandardOutput
RedirectStandardError
This will actually happen because you've told your application to receive/send the output/input from the target process
I see that you were trying to get the output from the batch file using this line
//obj.Activity = process.StandardOutput.ReadToEnd();
Unfortunately, this won't work unless you set RedirectStandardOutput to True so that your application would have the ability to read the output from the target batch file. Otherwise, the output will be empty
Example
private void RunBatch(CmdObj obj)
{
var process = new Process();
var startInfo = new ProcessStartInfo
{
FileName = obj.SrcFile,
WindowStyle = ProcessWindowStyle.Normal,
CreateNoWindow = false,
RedirectStandardInput = true,
RedirectStandardOutput = true, //This is required if we want to get the output
RedirectStandardError = true,
UseShellExecute = false
};
try
{
if (!obj.SrcFile.ToLower().Trim().EndsWith(".bat"))
throw new FileLoadException("Not a valid .bat file",obj.SrcFile);
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
obj.Activity = process.StandardOutput.ReadToEnd(); //Get the output from the target process
obj.Error = process.StandardError.ReadToEnd();
}
catch (Exception ex)
{
obj.Exception = ex;
}
finally
{
process.Close(); //Terminate the process after getting what is required
}
}
Notice that: UseShellExecute must be False in order to use RedirectStandardOutput
Thanks,
I hope you find this helpful :)
Don't you need to call
process.BeginErrorReadLine();
process.BeginOutputReadLine();
after process starts?
I had some problems without this if I remember right.
Related
Hi have a service which needs to execute it's updated shell script independent from the .Core process. I have tried /bin/bash, /bin/nohup and /bin/setsid. But every time the script stops the systemd service the script seems also to be stopped. How do I get this independent?
private static Process StartLinuxUpdateScript(string pathToUpdateScript, string pathToUpdateZip)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/setsid",
UseShellExecute = true,
Arguments = $" bash {pathToUpdateScript} {pathToUpdateZip}"
}
};
return process;
}
maybe it helpfull:
public void RunProcess(string fileName, string arg)
{
var process = new Process
{
StartInfo =
{
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = fileName,
Arguments = arg ,
UseShellExecute = false,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
process.Start();
string processOutput;
while ((processOutput = process.StandardError.ReadLine()) != null)
{
Console.WriteLine(processOutput);
}
process.Dispose();
}
So basicly the ReadLine() method just hangs right on the last output line.
I dont need to end the process. The main goal is to do "cmd-shell" with stdin and stdout (in variables/Printed out).
If the same can be achieved with async methods, please, leave it in comments.
Thanks in advance.
{
Process p = new Process()
{
StartInfo = new ProcessStartInfo("cmd")
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
Arguments = "/K",
}
};
p.ErrorDataReceived += (s, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
Console.WriteLine(e.Data);
}
};
p.Start();
while (true)
{
Console.Write("/SHELL/ #> ");
input = Console.ReadLine();
p.StandardInput.WriteLine(input);
p.BeginErrorReadLine();
Console.WriteLine("Errorgoing...");
while (p.StandardOutput.Peek() > -1) {
Console.WriteLine(p.StandardOutput.Peek());
Console.WriteLine(p.StandardOutput.ReadLine());
// When it hangs, Peek() outputs 67, that represents "C" char.
// Seems like just the last stdout line dont want to be printed out.
}
}
}
}```
After few implemented tricks I've ended up with the following code. Quite tricky.
static async Task Main(string[] args)
{
using (SemaphoreSlim semaphore = new SemaphoreSlim(0, 1))
{
Process p = new Process()
{
StartInfo = new ProcessStartInfo("cmd")
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
Arguments = "/K set PROMPT=PROMPT$P$G$_",
}
};
p.ErrorDataReceived += (s, e) =>
{
if (e.Data?.Length > 0)
{
Console.WriteLine(e.Data);
}
};
bool wasprompt = false;
string prompt = "";
p.OutputDataReceived += (s, e) =>
{
if (e.Data?.Length > 0)
{
if (e.Data.StartsWith("PROMPT") && e.Data.EndsWith(">"))
{
prompt = e.Data.Substring(6, e.Data.Length - 7);
semaphore.Release();
wasprompt = true;
}
else
{
if (!wasprompt)
Console.WriteLine(e.Data);
else
wasprompt = false;
}
}
};
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
await semaphore.WaitAsync();
while (!p.HasExited)
{
Console.Write($"/SHELL/ {prompt}#> ");
string input = Console.ReadLine();
p.StandardInput.WriteLine(input);
if (input == "exit") break;
await semaphore.WaitAsync();
}
p.WaitForExit();
}
Console.WriteLine("Bye.");
Console.ReadKey();
}
Few times tested and looks like it works as you expect.
The idea was adding $_ to PROMPT environment variable to force the prompt printed to output. Then I catch that prompt and implement input/output synchronization with SemaphoreSlim.
When I am running relog command from command prompt, I am getting the output without any issue.
Now when I am trying to run command using C# code, I am not getting any output. What could be the issue?
public static string Bash()
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
WorkingDirectory = #"C:\blgs",
Arguments = "relog.exe sample.blg",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
how can i make my console application window to behave like a command prompt window and execute my command line arguments?
This should get you started:
public class Program
{
public static void Main(string[] args)
{
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
proc.Start();
new Thread(() => ReadOutputThread(proc.StandardOutput)).Start();
new Thread(() => ReadOutputThread(proc.StandardError)).Start();
while (true)
{
Console.Write(">> ");
var line = Console.ReadLine();
proc.StandardInput.WriteLine(line);
}
}
private static void ReadOutputThread(StreamReader streamReader)
{
while (true)
{
var line = streamReader.ReadLine();
Console.WriteLine(line);
}
}
}
The basics are:
open cmd.exe process and capture all three streams (in, out, err)
pass input from outside in
read output and transfer to your own output.
The "Redirect" options are important - otherwise you can't use the process' respective streams.
The code above is very basic, but you can improve on it.
I believe you are looking for this
var command = "dir";
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
Console.WriteLine(result);
Can I start a process (using C# Process.Start()) in the same console as the calling program? This way no new window will be created and standard input/output/error will be the same as the calling console application. I tried setting process.StartInfo.CreateNoWindow = true; but the process still starts in a new window (and immediately closes after it finishes).
You shouldn't need to do anything other than set UseShellExecute = false, as the default behaviour for the Win32 CreateProcess function is for a console application to inherit its parent's console, unless you specify the CREATE_NEW_CONSOLE flag.
I tried the following program:
private static void Main()
{
Console.WriteLine( "Hello" );
var p = new Process();
p.StartInfo = new ProcessStartInfo( #"c:\windows\system32\netstat.exe", "-n" )
{
UseShellExecute = false
};
p.Start();
p.WaitForExit();
Console.WriteLine( "World" );
Console.ReadLine();
}
and it gave me this output:
You could try redirecting the output of this process and then printing it on the calling process console:
public class Program
{
static void Main()
{
var psi = new ProcessStartInfo
{
FileName = #"c:\windows\system32\netstat.exe",
Arguments = "-n",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(psi);
while (!process.HasExited)
{
Thread.Sleep(100);
}
Console.WriteLine(process.StandardOutput.ReadToEnd());
}
}
Alternative approach using the Exited event and a wait handle:
static void Main()
{
using (Process p = new Process())
{
p.StartInfo = new ProcessStartInfo
{
FileName = #"netstat.exe",
Arguments = "-n",
RedirectStandardOutput = true,
UseShellExecute = false
};
p.EnableRaisingEvents = true;
using (ManualResetEvent mre = new ManualResetEvent(false))
{
p.Exited += (s, e) => mre.Set();
p.Start();
mre.WaitOne();
}
Console.WriteLine(p.StandardOutput.ReadToEnd());
}
}