app crashes with ProcessStartInfo arguments - c#

I am trying to run a process called "prog.exe" with the arguments "blah $00" (sort of a code) but whatever I try fails.
string file = "blah $00";
string result = string.Empty;
ProcessStartInfo P = new ProcessStartInfo(#"""" + "prog.exe" + #"""");
P.Arguments = #"""" + file + #"""";
P.CreateNoWindow = true;
P.UseShellExecute = false;
P.RedirectStandardOutput = true;
Process.Start(P);
using (Process process = Process.Start(P))
{
using (StreamReader str = process.StandardOutput)
result = str.ReadToEnd();
}
MessageBox.Show(result);
When this code is executed, my program just crashes and I am forced to close it using the Task Manager.
I am not sure what's wrong with my code (am I not setting the arguments correctly?), so any help would be appreciated.

Run your process with given argument from console and see what happens. If result is something you expect, just remove double quotes and this should resolve your problem.

I don't think your program crashes. It just waits for "prog.exe" to finish! I bet, that your program continues running as soon as you are done working with prog.exe and close it - and make sure in task manager that it really is gone.

Related

how to catch command prompt process finish in C# and do something after that?

I am working with voice records. I need to use an .exe file for convert Wav to .mp3 file. Everything is fine I can execute this exe but I need to do something after when process end with my output .mp3 file. I know my output directory but i cant handle MP3 file before its not created yet. I know maybe I need to use Thread.sleep(); or something like that because I cant catch a file before its not exist.
Here is my code:
string mp3GuidName = Guid.NewGuid().ToString();
var mp3FilePath = WavFilePath.Replace("finalWavFile", mp3GuidName).Replace("wav", "mp3");
var extrasFilePath = HttpContext.Current.Server.MapPath("/").Replace("DevApp.Web", "Extras");
string strArguments = "/c start " + extrasFilePath + "lame.exe --abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = strArguments ;
process.StartInfo = startInfo;
process.Start();
var attactment = new Attachment
{
CreatedOn = DateTime.Now,
UpdatedOn = DateTime.Now,
Title = mp3GuidName +".mp3",
Size = _storageProvider.GetFile(mp3FilePath).GetSize(), // here I am trying to get mp3 file but i cant catch it. Because if this wav files size is huge, then convert process is taking time and my mp3 file is not created yet.
FileExtension = _storageProvider.GetFile(mp3FilePath).GetFileType()
};
attactment.MimeType = _storageProvider.GetMimeType(attactment.FileExtension);
attactment.FileUrl = mp3GuidName+".mp3";// file.GetName();
attactment.AttachmentFolderId = folder.Id;
_attachmentRepository.Add(attactment);
I was try to use process.WaitForExit();but I cant solve this problem. I still cant acces to mp3 file.
so how can I catch when the process finish?
Best Regards.
Remove the start command argument from your argument string and you should be able to use process.WaitForExit(); to wait for Lame to finish with encoding:
string strArguments = "/c " + extrasFilePath + "lame.exe --abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
However, you can simplify your code and avoid this dance with cmd.exe altogether by starting lame.exe directly:
string strArguments = "--abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
...
startInfo.FileName = extrasFilePath + "lame.exe";
startInfo.Arguments = strArguments;
...
Below some information about why using the start command argument in your scenario is counterproductive.
Executing console applications such as lame.exe using cmd.exe (or from a console or batch file) normally block cmd.exe (or console/batch file) until the console application exits.
However, using the start command turns this normally blocking execution of a console application into a non-blocking execution. With this argument, the cmd.exe (or console/batch file) will continue execution while the console application is still running. In your particular case it means cmd.exe will exit right after it has started lame.exe (since it has nothing else to execute), effectively sabotaging your attempt to wait for lame.exe to finish.
Edited:
Base on the comments below I have to make it clear that the oroginal solution I recommended does not check if the file is free but it checks only if the file exists!
So I rather recommend the following:
private bool IsBusy(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch ()
{
return true;
}
finally
{
if (stream != null)
stream.Close();
}
return false;
}
the usage will be:
while(IsBusy(fileinfo))
{
//just wait
}
ORIGINAL:
You can use a while loop to find out when the file is ready:
while(!File.Exists(mp3FileName))
{
//just wait
}
// add the attachment here

External tool fails to output in my app but outputs nicely in CMD screen

I am trying to use the GFIX tool that gets shipped with Firebird Database inside my C#/WPF Application to execute certain commands on the database.
Firebird http://www.firebirdsql.org/en/firebird-2-5-3-upd1/
Gfix http://www.firebirdsql.org/manual/gfix.html
To do this I use the following code:
public string RunExternalExe(string filename, string arguments = null)
{
var process = new Process();
process.StartInfo.FileName = filename;
if (!string.IsNullOrEmpty(arguments))
{
process.StartInfo.Arguments = arguments;
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
var stdOutput = new StringBuilder();
process.OutputDataReceived += (sender, args) => stdOutput.Append(args.Data);
string stdError = null;
try
{
process.Start();
process.BeginOutputReadLine();
stdError = process.StandardError.ReadToEnd();
process.WaitForExit();
}
catch (Exception e)
{
throw new Exception("OS error while executing " + Format(filename, arguments) + ": " + e.Message, e);
}
if (process.ExitCode == 0)
{
return stdOutput.ToString();
}
else
{
var message = new StringBuilder();
if (!string.IsNullOrEmpty(stdError))
{
message.AppendLine(stdError);
}
if (stdOutput.Length != 0)
{
message.AppendLine("Std output:");
message.AppendLine(stdOutput.ToString());
}
throw new Exception(Format(filename, arguments) + " finished with exit code = " + process.ExitCode + ": " + message);
}
}
private string Format(string filename, string arguments)
{
return "'" + filename +
((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) +
"'";
}
Found there
How To: Execute command line in C#, get STD OUT results
also I tried every other approach that gets explained in that question, but it still doesn't get me any output.
I try to execute the following command
gfix.exe -user foo -pa foo -shut single -force 0 app1:\bar.fdb
What I see if I execute it in CMD is the following output
"Your user name and password are not defined. Ask your database administrator to set up a Firebird login."
That's an obvious error because user foo with password foo doesn't exist.
So my problem isn't the error itself, its just the fact that I do NOT get this output inside my C# application not matter what I tried so far.
Since I am seeing the error output in my CMD screen it should get output in my C# application or is there any possibility that the tool itself is blocking the output and I don't have a chance to get it?
What I tried so far:
Calling the gfix.exe itself with the arguments.
Calling a bat that contains the call to gfix.exe with its arguments.
Calling CMD with /c or /k that calls the gfix.exe with arguments.
Calling CMD with /c or /k that calls a bat that calls the gfix.exe.
I believe I tried all possible combinations of calling this tool but still I don't get an output.
Also I have tried both RedirectStandardError and RedirectStandardOutput, with async/sync approaches (Begin.. and ReadToEnd), also I tried to input the arguments with the help of RedirectStandardInput and wrote the lines exactly as I would type it with CMD, first a cd "C:\Test" and than the call to gfix.exe all in vain...
Further info the tool works fine if I input everything correctly its runs through and does exactly what it should do, but I would also like to catch when the tool fails and want to output the corresponding error.
Edit:
Notice that I tried the following now, without my C# app involved only doubleclick the bat or executing it in CMD.
I have modified my test bat file to this:
gfix.exe -user foo -pa foo -shut single -force 0 app1:/bar.fdb > Test.txt 2> error.txt
Which creates 2 Text files - both empty.
If I run this .bat in CMD no error is displayed, if I remove the 2> error.txt the error message again gets displayed in the CMD screen. So the redirect seems to "work" only that my txt files are empty... could the gfix tool block this?!?
This works for me:
using (var process = Process.Start(
new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
RedirectStandardError = true,
UseShellExecute = false,
}))
{
process.WaitForExit();
if (process.ExitCode != 0)
{
var errorMessage = process.StandardError.ReadToEnd();
Assert.Fail(errorMessage);
}
}
Note the RedirectStandardError = true.
When error happens gfix outputs it not to stdout, but to stderr. This is obvious and std behavior.

get Redirecting output from plink

I am currently trying to write a function that runs plink and captures the output from that process. When I try StandardOutput I get nothing and when I try StandardError I get "Unable to read from standard input: The handle is invalid." I can only assume that this means I am trying to read while plink is waiting for input. I am also passing my command via a text file called apCommands.txt. When I run this command manually it works and gives me the required output, I just can't figure out why I can't redirect that output so I can store it in a results file. Here is the code I have right now.
string pArgs = "-m \"" + ABSPATH + "\\apCommands.txt\" -ssh myHost -l user -pw password";
string output;
if (File.Exists(ABSPATH + "\\apTest.txt"))
File.Delete(ABSPATH + "\\apTest.txt");
StreamWriter apTest = new StreamWriter(ABSPATH + "\\apTest.txt");
Process runAP = new Process();
ProcessStartInfo apInfoStart = new ProcessStartInfo(ABSPATH + "\\plink.exe");
apInfoStart.Arguments = pArgs;
apInfoStart.RedirectStandardOutput = true;
apInfoStart.CreateNoWindow = true;
apInfoStart.UseShellExecute = false;
runAP.StartInfo = apInfoStart;
runAP.Start();
output = runAP.StandardOutput.ReadToEnd();
runAP.WaitForExit();
apTest.WriteLine("Standard Output");
apTest.WriteLine(output);
apTest.Close();
runAP.Close();
Thanks,
To solve this error, use apInfoStart.RefirectStandardInput = true;
see also: https://social.msdn.microsoft.com/Forums/vstudio/en-US/697324fa-6ce6-4324-971a-61f6eec075be/redirecting-output-from-plink
You can do the same for StandardOutput and StandardError

command redirection issue

here is my code
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = "ffmpeg.exe";
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = "-i " + videoName;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
The command works, but strOutput string is empty, results are shown within the console. Am I missing something here?
It's possible the program is writing its output to StandardError instead of StandardOutput. Try using .RedirectStandardError = true and then .pProcess.StandardError.ReadToEnd() to capture that output.
If you need the possibility of capturing both standard error and standard out in (roughly) the proper interleave, you will likely need to use the async versions with callbacks on OutputDataReceived and ErrorDataReceived and using BeginOutput/ErrorReadLine.
Try to capture Std Error too as on any event of error, it will be used instead.
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardError = true;
pProcess.StartInfo.RedirectStandardOutput = true;
//Start the process
pProcess.Start();
//Wait for process to finish
pProcess.WaitForExit();
//Get program output
string strError = pProcess.StandardError.ReadToEnd();
string strOutput = pProcess.StandardOutput.ReadToEnd();
I just wonder why you wait for exit WaitForExit after reading the output, it should be in reversed order as your app may dump more until it finally completes the ops

C#: get external shell command result line by line

I am writing a C# winform application that starts a second process to execute shell commands like "dir" and "ping". I redirect the second process's output so my app can receive the command result. It roughly works fine.
The only problem is my winform app receives the command line output as a whole instead of line by line. For example, it has to wait for the external "ping" command to finish (which takes many seconds or longer) and then receives the whole output (many lines) at once.
What I want is the app receives the cmdline output in real-time, i.e. by lines not by block. Is this doable?
I am using this code to read the output:
while ((result = proc.StandardOutput.ReadLine()) != null)
But it does not work the way I expected.
Thanks in advance.
EDIT: here is the code I am using:
System.Diagnostics.ProcessStartInfo procStartInfo = new
System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// The following commands are needed to redirect the standard output.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result;
try {
while ((result = proc.StandardOutput.ReadLine()) != null)
{
AppendRtfText(result+"\n", Brushes.Black);
}
} // here I expect it to update the text box line by line in real time
// but it does not.
Have a look at the example in this msdn article on how to do the reading completly async.
Beyond that I expect your code does to read line by line now but the UI doesn't get any time to repaint (missing Application.DoEvents(); after updating the RTFTextBox
Instead of loop using while ((result = proc.StandardOutput.ReadLine()) != null) you should of using:
...
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
This will start asynchronous reading the lines when they arrives, you then handle the lines read by e.Data in proc_DataReceived handler, since you are use BeginOutputReadline the e.Data will be a string lines.
This could be usefull:
http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/8d6cebfc-9b8b-4667-85b5-2b92105cd0b7/
http://www.dotnetperls.com/redirectstandardoutput
I had the same issue and got around it with the following. I found that if I had an error in the external app I was getting no output at all using the ReadToEnd() method, so switched to use the line by line streamreader. Will be switching over to use the answer provided by Saa'd though as that looks like the proper way to handle it.
Also found this solution: c# coding convention public/private contexts which provides for error handling at the same time and giving a fuller explanation to the use of externalApp.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
Process externalApp = new Process();
externalApp.StartInfo.FileName = config.ExternalApps + #"\location\DeleteApp.exe";
externalApp.StartInfo.Arguments = Directory.GetCurrentDirectory() + #"\..\..\..\project\argumentsForDeleteApp.xml";
externalApp.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
externalApp.StartInfo.UseShellExecute = false;
externalApp.StartInfo.RedirectStandardOutput = true;
Console.Out.WriteLine(DateTime.UtcNow.ToLocalTime().ToString() +
":###### External app: " + externalApp.StartInfo.FileName + " - START");
externalApp.Start();
using (StreamReader reader = externalApp.StandardOutput)
{
while (!reader.EndOfStream)
{
string result = reader.ReadLine();
Console.Out.WriteLine(result);
}
}
externalApp.WaitForExit();

Categories

Resources