Is there a C# way to read exceptions from cmd processes? - c#

The specific problem I am seeing when executing a cmd process with something like "del *.txt" where one of the 'txt' files is open and cannot be deleted, the cmd process will output a line of text (saying something like 'file in use, cannot delete file') to the console, but not to the StandardOutput or the StandardError. According to this question [ https://stackoverflow.com/a/320779/832705 ] from 2008, the answer is no, but I am wondering if that might have changed in the past 4 years, or if someone has since found a workaround way. Also, I might be misinterpreting that answer, it might mean CLR exceptions and not cmd exceptions.
here is my process setup/start code:
ProcessStartInfo psi = new ProcessStartInfo("cmd", string.Empty);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.UseShellExecute = false;
Process p = new Process();
p.StartInfo = psi;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
outputfilesw = new StreamWriter(outputfile, true);
try
{
p.Start();
p.BeginOutputReadLine();
//work code
}

You just have to call p.BeginErrorReadLine() to start the asynchronous read of StandardError. Answer added at suggestion of OP.

You can read the output, and you can process the text returned. So, you should be able to find the text that indicates an error, even if it doesn't land in the error output.
Also, it is important to note that only the process being run can determine which output stream gets a message. So, if the command you're using decides to send errors to the standard stream, no amount of OS or C# work will change that.

Related

Capture ALL output when running batch file in c#

There are numerous examples out there which describe how to capture the standard and error output when running a batch file in c#.
I am following those instructions and while it captures MOST of the output it doesn't capture all.
And I can't find any explanation or help in dealing with this.
For example I have to run a command via a batch file to generate a PDF from Tableau. All the resulting outputs are captured correctly including confirmation of login to the server, confirmation of which PDF will be generated etc. However when an error occurs the error is not captured. However if I run the batch file manually (ie not via code) I can see the error message in the console.
See screenshot below....you can see the command line calls and the server responses. But the most important information, the error message (highlighted) is not being captured when running via code.
Can anyone see anything to add to the following code snippet that would capture this information? Or a reason why its not being captured??
System.Diagnostics.ProcessStartInfo procStartInfo = new
System.Diagnostics.ProcessStartInfo(batchFilePath + batchFileName);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process p = new Process();
p.EnableRaisingEvents = true;
p.StartInfo = procStartInfo;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
StringBuilder sb = new StringBuilder();
p.OutputDataReceived += (sender, args) => sb.AppendLine(args.Data);
p.ErrorDataReceived += (sender, args) => sb.AppendLine(args.Data); ;
p.Exited += Exited;
p.Start();
p.BeginOutputReadLine();
Appreciate any help on this!
Thanks
Probably missing:
p.BeginErrorReadLine();
And note that sb.AppendLine(args.Data); is not thread safe, consider synchronizing the access

Trigger Apache Nutch Crawl Programmatically

I'm trying to create a ASP.NET web api to trigger a crawl event to happen. I can't seem to get cygwin to process any of the commands I give it. The only thing I can really do is get it to open a terminal. Once the terminal is open I'd have to redirect the pwd to another location and then trigger my command I want.
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.CreateNoWindow = false;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
info.FileName = "C:\\cygwin64\\bin\\mintty.exe";
p.StartInfo = info;
p.Start();
StreamWriter sw = p.StandardInput;
if (sw.BaseStream.CanWrite)
{
sw.WriteLine(#"cd C:\Users\UName\Desktop\apache-nutch-2.3-mongodb\runtime\local\");
sw.WriteLine("bin/autoCrawl");
}
sw.Close();
p.WaitForExit();
I've tried many approaches, this is the last one I've tried but it just does nothing. Is there a way to launch this crawl from my .NET application? I've looked into the NutchApi about creating a new job with a type of crawl but I'm not sure if that applies here or not.
I ended up figuring out how to use the NutchApi to answer my question.

Running CMD commands from C#

First of all, i searched a lot to avoid asking a duplicate question. If there is one, i will delete this question immediately.
All the solutions on the web are suggesting to use Process.StartInfo like this one
How To: Execute command line in C#, get STD OUT results
I dont want to run a batch file, or an .exe.
I just want to run some commands on cmd like
msg /server:192.168.2.1 console "foo" or ping 192.168.2.1
and return the result if there is one.
How can i do that ?
Those commands are still exe files, you just need to know where they are. For example:
c:\windows\system32\msg.exe /server:192.168.2.1 console "foo"
c:\windows\system32\ping.exe 192.168.2.1
The only proper way to do this is to use Process.Start. This is demonstrated adequately in this question, which is itself a duplicate of two others.
However, as DavidG says, the commands are all exe files and you can run them as such.
Apparently, i found an answer
while (true)
{
Console.WriteLine("Komut giriniz.");
string komut = Console.ReadLine();
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
//startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C" + komut;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
process.StartInfo = startInfo;
Console.WriteLine(process.Start());
string line = "";
while (!process.StandardOutput.EndOfStream)
{
line = line + System.Environment.NewLine + process.StandardOutput.ReadLine();
// do something with line
}
Console.WriteLine(line);
Console.ReadLine();
}
seems like if you can run cmd.exe with arguments including your command.
thanks for contributing.

Passing arguments to running process in C#

I've some troubles with running processes and passing args to them.
I know how to run process with some args
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c something");
Process p = Process.Start(psi)
The problem is that after script is executed process is terminated. That's why there is "/c"
But I'm running multiple scripts and I would like to run them in one process ("cmd.exe") not to start new process every time.
Is there some solutions for it ?
I hope somebody understand what I'm talking about ;)
I recommend you utilize a batch file to script the execution of your executables and call your batch file instead. Or, you can do this -
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = new StreamWriter(p.StandardInput))
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("mysql -u root -p");
sw.WriteLine("mypassword");
sw.WriteLine("use mydb;");
}
}
It sounds like you ought to investigate redirecting the standard input - be sure to also set psi.UseShellExecute to false. You'll probably also want to redirect standard output, so you can have some way of knowing what your child process is doing.
Read more about redirection here.

How to get the output of a System.Diagnostics.Process?

I run ffmpeg like this:
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.Start();
p.WaitForExit();
... but the problem is that the console with ffmpeg pops up and disappears right away, so I can't get any feedback. I don't even know if the process ran correctly.
So how can I either:
Tell the console to stay opened
Retrieve in the C# what the console
displayed
What you need to do is capture the Standard Output stream:
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
string q = "";
while ( ! p.HasExited ) {
q += p.StandardOutput.ReadToEnd();
}
You may also need to do something similar with StandardError. You can then do what you wish with q.
It is a bit finicky, as I discovered in one of my questions
As Jon Skeet has pointed out, it is not smart performance-wise to use string concatenation like this; you should instead use a StringBuilder:
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
StringBuilder q = new StringBuilder();
while ( ! p.HasExited ) {
q.Append(p.StandardOutput.ReadToEnd());
}
string r = q.ToString();
Lucas' answer has a race condition: If the process finishes quickly the while loop is left (or never entered) even if there is some output left, that is you might miss out on some data. To prevent that, another ReadToEnd should be done after the process exited.
(Note that in comparison to the old version of my answer, I can no longer see a need for WaitForExit once the process.HasExited flag is true, so this boils down to:)
using (var process = Process.Start(startInfo))
{
var standardOutput = new StringBuilder();
// read chunk-wise while process is running.
while (!process.HasExited)
{
standardOutput.Append(process.StandardOutput.ReadToEnd());
}
// make sure not to miss out on any remaindings.
standardOutput.Append(process.StandardOutput.ReadToEnd());
// ...
}
I know this question is old, but I'll add to it anyway.
If all you wish to do is display the output of a command line process, and you're spawning the process from a console window, you need only redirect the standard input (yes, I know it sounds wrong, but it works).
So:
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.UseShellExecute = false;
p.RedirectStandardInput = true;
p.Start();
p.WaitForExit();
Would do just fine.
For a more specific answer directly related to ffmpeg, passing the "-report" command into ffmpeg will make it dump a log into the current directory with what was said in the display of the process.
‘-report’
Dump full command line and console output to a file named
program-YYYYMMDD-HHMMSS.log in the current directory. This file can be
useful for bug reports. It also implies -loglevel verbose.
Note: setting the environment variable FFREPORT to any value has the
same effect.
From FFMpeg Documentation.

Categories

Resources