Process WaitForExit not blocking - c#

I am using a FilesystemWatcher class to convert audio to a listenable format as they come in. This worked perfectly for a while, until right around the time we upgraded to 4.5 on that server. Now, to get it to work, I need to debug it and set a breakpoint so the method does not exit before the process runs.
It only takes a few milliseconds for sox to convert the audio. Setting a Thread.sleep(10000) doesnt fix the issue. Downgrading the project to .net 2.0 did nothing.
What's really frustrating, is process.WaitForExit() seemingly does not block. While debugging, I step right over it and wait until the file shows up in the folder.
As always, I really appreciate the assistance.
Attached is the relevant code:
void FileCreated(object sender, FileSystemEventArgs e)
{
string newname = String.Format(#"{0}{1}.mp3", _mp3FolderPath, e.Name.Replace(".wav", ""));
string soxArgs = #"-t wav -r 8k -c 1";
ProcessStartInfo p = new ProcessStartInfo
{
FileName = _soxPath,
Arguments = string.Format("{0} {1} {2}", soxArgs, e.FullPath, newname),
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = false,
RedirectStandardInput = false,
RedirectStandardOutput = false
};
using (Process process = Process.Start(p))
{
process.WaitForExit();
Thread.Sleep(10000);
//process.Close();
}
}

Related

Pass args to Main(string[] args)

I am trying to build a Console Application to start my .NetCore Web Applications that I've got built as a .dll
Unfortunately the Args entered in the ProcessStartInfo are not being received by my application, however my application does start and i get a unexpected behaviour in the Console.WriteLine Method.
This code is inside my SocketAPI Project which is a .NetCore 2.2 WebApplication | API Project:
public static void Main(string[] args)
{
// Outputs -> Received :
// ?? Why is args.Length empty? Not even 0 or null??
Console.WriteLine(string.Format("Received : ",args.Length));
CreateWebHostBuilder(args).Build().Run();
}
It gets declared and called by my ProcessRunner which is a Class that holds the current Process:
I am also referring to this documentation:
dotnet command documentation on microsoft.com
Which describes: dotnet [command] [arguments]
This Code is inside the ProcessRunner Constructor
ProcessStartInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = string.Format("BHR.{0}.dll {1}", Name, args),
WorkingDirectory = ".\\",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
Later on I'm calling Process.Start(); inside the ProcessRunner to start the Process.
As said I do get Output but no Args... So why do i see "Received :" but nothing on the end?
Do i have to enable my SocketAPI to receive args when starting them as a process? I've been trying to deal with the problem since 2 days now and I'm completely clueless...
Kind Regards
In your code you are not adding a placeholder for the argument length.
Console.WriteLine(string.Format("Received: {0} ",args.Length));
FYI, This works for me:
var arguments = "myarg1 myarg2";
var dir = #"C:\somedir\somechilddir";
var info = new System.Diagnostics.ProcessStartInfo("dotnet", $"someproject.dll {arguments}");
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.WorkingDirectory = dir;
var process = new System.Diagnostics.Process();
process.StartInfo = info;
process.Start();
process.WaitForExit();
Perhaps, try resolving your working directory using Path.GetFullPath();
Big thank you to #AlexanderHiggins for showing me how dull i should feel now!
In your code you are not adding a placeholder for the argument length.
Console.WriteLine(string.Format("Received: {0} ",args.Length));
Sorry for bothering!

Get and Set the position in Process.StandardOutput

See as an example I open CMD and navigate to the folder I want to get the data from then I use it to open the app with arugents with standard input(all synchronously) the code so far
public static Process Start(bool DoNotShowWindow = false)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = DoNotShowWindow
};
Process cmdProcess = new Process
{
StartInfo = cmdStartInfo,
EnableRaisingEvents = true
};
return cmdProcess;
}
//in other method
Process cli = InteractWithConsoleApp.Start();
cli.Start();
cli.StandardInput.WriteLine("cd /");
cli.StandardInput.WriteLine("cd " + path);
cli.StandardInput.WriteLine("fantasygold-cli getbalance abc");
Thread.Sleep(5000);
Problem
Now when I use StandardOutput.Readline, it starts from the beginning and returns me everything like first two lines of copyright,empty lines and even the input which in my case, after waiting 5 secs for the result I want to read line or to the end depending the input from where I had inputted.
possible solution
One solution I found was to change the position but it turned out it doesn't support it and even copying to another stream reader doesn't works(the position is not by line).
Well I can use filters like check a double or for an address starts with F and has a length of 36. The problem comes when I want to get the whole JSON say for like the past transactions, for which I think using filters like '{' and then check for '}'Caveat in this would be bad code, which I don't want.
TLDR
So, what could be the solution to my problem here :)
I found the answer, to open the file in subdirectory just use this cli.StartInfo.FileName = Directory.GetCurrentDirectory() + #"\SubfolderName\fantasygold-cli";
and the arguements like getbalance as cli.StartInfo.Arguments = "getbalance amunim"

C# Program automation - Program hangs/doesn't work

I've been trying to make a program to automate the process of running different processes on my computer. So far I've got the following program running a console version of BleachBit(It's like CCleaner), the process appears in task manager, it hits around 25kb process RAM then CPU usage goes to 0% and just sits there doing nothing for ages and never quits.
Is there something wrong I'm doing in my code that could cause this to happen?
I've tried editing the app.manifest to make sure the program has to be run as admin in case it needed more privileges
Also when running similar code in a bat file to run the program, it's opens its own windows and runs fine, so I'm not sure. Any help in the right direction would be fantastic.
The code I'm running is below.
static void Main(string[] args)
{
string Log = "";
if (File.Exists(Environment.CurrentDirectory + "\\BleachBit\\bleachbit_console.exe"))
{
Log += "File exists";
Log += RunProgramCapturingOutput("\\BleachBit\\bleachbit_console.exe", "--preset --clean");
}
else
Log += "Program not found. Please place at \\BleachBit\\bleachbit_console.exe";
File.WriteAllText("log.txt", Log);
Console.ReadLine();
}
public static string RunProgramCapturingOutput(string filename, string arguments)
{
ProcessStartInfo processInfo = new ProcessStartInfo()
{
FileName = Environment.CurrentDirectory + filename,
Arguments = arguments,
CreateNoWindow = false,
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(Environment.CurrentDirectory + filename),
RedirectStandardError = false,
RedirectStandardOutput = true
};
Process process = Process.Start(processInfo);
process.WaitForExit();
string output = output = process.StandardOutput.ReadToEnd();
Console.WriteLine("Output: " + output);
process.Close();
return output;
}
Switching these lines to this:
string output = output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
allows to avoid deadlocks. The program seems to be a relatively slow running program due to hard-drive I/O, just give it time and you'll see it complete.
I found this deadlock issue from https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
Where it states in a code block: "// To avoid deadlocks, always read the output stream first and then wait."

How to run shell script with C# on OSX?

I'd like to use C# to execute a shell script.
Based on similar questions I came to a solution that looks like this.
System.Diagnostics.Process.Start("/Applications/Utilities/Terminal.app","sunflow/sunflow.sh");
It currently opens Terminal, then opens the shell file with the default application (Xcode in my case). Changing the default application is not an option, since this app will need to be installed for other users.
Ideally the solution will allow for arguments for the shell file.
I can't test with a Mac right now, but the following code works on Linux and should work on a Mac because Mono hews pretty closely to Microsoft's core .NET interfaces:
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "foo/bar.sh",
Arguments = "arg1 arg2 arg3",
};
Process proc = new Process()
{
StartInfo = startInfo,
};
proc.Start();
A few notes about my environment:
I created a test directory specifically to double-check this code.
I created a file bar.sh in subdirectory foo, with the following code:
#!/bin/sh
for arg in $*
do
echo $arg
done
I wrapped a Main method around the C# code above in Test.cs, and compiled with dmcs Test.cs, and executed with mono Test.exe.
The final output is "arg1 arg2 arg3", with the three tokens separated by newlines
Thanks Adam, it is good starting point for me. However, for some reason when I tried with above code (changed to my needs) I am getting below error
System.ComponentModel.Win32Exception: Exec format error
see below code that gives above error
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "/Users/devpc/mytest.sh",
Arguments = string.Format("{0} {1} {2} {3} {4}", "testarg1", "testarg2", "testarg3", "testarg3", "testarg4"),
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process proc = new Process()
{
StartInfo = startInfo,
};
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string result = proc.StandardOutput.ReadLine();
//do something here
}
and spent some time and come up with below and it is working in my case - just in case if anyone encounter this error try below
Working Solution:
var command = "sh";
var scriptFile = "/Users/devpc/mytest.sh";//Path to shell script file
var arguments = string.Format("{0} {1} {2} {3} {4}", "testarg1", "testarg2", "testarg3", "testarg3", "testarg4");
var processInfo = new ProcessStartInfo()
{
FileName = command,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process process = Process.Start(processInfo); // Start that process.
while (!process.StandardOutput.EndOfStream)
{
string result = process.StandardOutput.ReadLine();
// do something here
}
process.WaitForExit();

RedirectStandardOutput is buffering lines instead of being instantaneous?

Ok, I am trying to use Tail to monitor a log file, but I cannot get the same behavior programatically as when I manually run it through cmd prompt using the same parameters.
When run through cmd prompt it displays the new lines instantly. Programatically though, I have to wait for about 75+ new lines in log file before the 'buffer' unleashes all the lines.
Here's the code I have now.
private const string tailExecutable = #"C:\tail.exe";
private const string logFile = #"C:\test.log";
private static void ReadStdOut()
{
var psi = new ProcessStartInfo
{
FileName = tailExecutable,
Arguments = String.Format("-f \"{0}\"", logFile),
UseShellExecute = false,
RedirectStandardOutput = true
};
// Running same exe -args through cmd.exe
// works perfectly, but not programmatically.
Console.WriteLine("{0} {1}", psi.FileName, psi.Arguments);
var tail = new Process();
tail.StartInfo = psi;
tail.OutputDataReceived += tail_OutputDataReceived;
tail.Start();
tail.BeginOutputReadLine();
}
static void tail_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
I have used the OutputDataReceived event before but never had these buffering/spamming problems.
I am so confused with about right now.
* Edit *
I found this wintail project on CodeProject and am going to be switching to that because the buffer makes this solution way too slow.
Thanks for the answers.
Process.StandardOutput, when redirected, defaults to a StreamReader with a 4096-byte buffer, so the answer is yes.
In most languages and operating systems the standard stream is usually buffered, but the error stream is not.
Try using:
System.Console.Error

Categories

Resources