I'm using sox.exe to play some audio files.
This is how I'm calling it:
SoxPlayer = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
RedirectStandardError = true,
UseShellExecute = false,
FileName = Play,
Arguments = arg,
WorkingDirectory = Application.StartupPath + "\\bin\\"
}
};
and this is the code that should be interpreting the StandardError output:
private void UpdatePlaybackTime(string output)
{
if (string.IsNullOrEmpty(output)) return;
if (!output.Contains("%") || !output.Contains("[")) return;
var index1 = output.IndexOf("%", StringComparison.Ordinal) + 1;
var index2 = output.IndexOf("[", StringComparison.Ordinal);
var time = output.Substring(index1, index2 - index1).Trim();
var times = time.Split(new[] { ":" }, StringSplitOptions.None);
var seconds = Convert.ToDouble(times[0]) * 3600;
seconds = seconds + (Convert.ToDouble(times[1]) * 60);
seconds = seconds + (Convert.ToDouble(times[2]));
if (seconds == 0 || seconds < PlaybackSeconds) return;
PlaybackSeconds = seconds;
}
My goal is to get the playback time from the sox output as accurately as possible, rather than work (as I was doing before) with an internal timer that may lose sync with sox's own.
My first attempt was using this recommendation I found online:
SoxPlayer.ErrorDataReceived += (sender, args) => UpdatePlaybackTime(args.Data);
SoxPlayer.Start();
SoxPlayer.BeginErrorReadLine();
This current code "works" in that I get the information I want, but it seems like UpdatePlaybackTime() is being called every 5 seconds or so. When it's called, the info obtained is accurate, but obviously I want to update the playback info several times per second, not every 5 seconds.
My understanding is that what is happening is that UpdatePlaybackTime is being called when the StandardError buffer gets full. I've tried calling SoxPlayer.BeginErrorReadLine() with my player timer but it says it's already running asynchronously. I've tried SoxPlayer.StandardError.DiscardBufferedData() but it throws an exception because of the asynchronous process that is ongoing.
So, how can I manage to capture the playback information how I need? Thank you in advance!
EDIT:
After discussing this code and how it's not working because of buffering, I've also tried the following inside a separate BackgroundWorker thread, with the same result (i.e. updates only about every 5 seconds):
SoxPlayer.Start();
SoxTimer.RunWorkerAsync();
private void SoxTimer_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
var sr = new StreamReader(SoxPlayer.StandardError.BaseStream);
while (sr.Peek() > 0)
{
var line = sr.ReadLine();
UpdatePlaybackTime(line);
}
}
private void SoxTimer_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (!SoxPlayer.HasExited)
{
SoxTimer.RunWorkerAsync();
}
}
When this BackgroundWorker completes, it checks if SoxPlayer.HasExited, and if it hasn't, it runs again. This has the same effect as my first attempt. PlaybackSeconds is only getting updated about every 5 seconds, at which point it updates to the right time, and then the rest of the code that acts based on the PlaybackSeconds value works as well.
I also tried achieving the same by creating a Thread to work the reading of the StandardError output. Every instance results in the same, a 5 second or so delay between when UpdatePlaybackTime() gets called. When it does, it iterates through all the output that was sent to StandardError since the last time we iterated through it, so it then updates the PlaybackSeconds value very quickly in small increments and leaves it at the current value at that time. But again, one update as far as the user is concerned every 5 seconds.
Sox creators are adamant that the problem is not on their end. When played in a console window, output is constant. According to sox creators, every 0.1 seconds. If I tell sox to output is standarderror to a text file, the same happens. There's a constant updating of the information on the text file. Yet reading the StandardError stream itself, I have now spent the better part of two days with no acceptable results.
Thank you for your help.
EDIT 2:
Following Peter's advice below, here's a brand new project. Didn't even change the default names for anything. Same behavior as described so far. So I'm going back to blame (ahem, discuss with) the SoX peeps.
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Process SoxPlayer;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var bin = Application.StartupPath + "\\bin\\";
SoxPlayer = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
RedirectStandardError = true,
UseShellExecute = false,
FileName = bin + "sox.exe",
Arguments = "song.ogg -c 2 -d",
WorkingDirectory = bin
}
};
SoxPlayer.Start();
var thread = new Thread(() =>
{
int cch;
var rgch = new char[1];
while ((cch = SoxPlayer.StandardError.Read(rgch, 0, rgch.Length)) > 0)
{
var cch1 = cch;
label1.Invoke(new MethodInvoker(() => label1.Text = new string(rgch, 0, cch1)));
}
});
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
SoxPlayer.Kill();
}
}
}
Here is a simple code example that does not reproduce the behavior you describe:
class Program
{
static void Main(string[] args)
{
Process process = new Process();
process.StartInfo.FileName = "SimpleStderrWriter.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.Start();
Thread thread = new Thread(() =>
{
int cch;
char[] rgch = new char[1];
Console.WriteLine("Reading stderr from process");
while ((cch = process.StandardError.Read(rgch, 0, rgch.Length)) > 0)
{
Console.Write(new string(rgch, 0, cch));
}
Console.WriteLine();
Console.WriteLine("Process exited");
});
Console.WriteLine("Press Enter to terminate process");
thread.Start();
Console.ReadLine();
process.StandardInput.WriteLine();
thread.Join();
}
}
Here is the code for the SimpleStderrWriter.exe program:
class Program
{
static void Main(string[] args)
{
bool exit = false;
Thread thread = new Thread(() =>
{
while (!Volatile.Read(ref exit))
{
Console.Error.Write('.');
Thread.Sleep(250);
}
});
thread.Start();
Console.ReadLine();
Volatile.Write(ref exit, true);
thread.Join();
}
}
This code example demonstrates clearly, by receiving and re-emitting the child process's stderr output as quickly as it's generated, that there is nothing in .NET that by default would cause the delay you are experiencing. The obvious conclusion is that either your advisors with respect to SoX are wrong and it does some buffering for some reason, or that you yourself have added something to your code that introduces the delay you are experiencing.
If you are positive the latter is not the case, then you need to go back to your SoX advisor and explain to them that they are mistaken. If you are positive that the SoX advisor is correct, then you need to post an example similar to the above, but which does reproduce the delay you are experiencing.
Related
I have a C# program that wants to interact with an external process written in C++. I believe this C++ process is using correct standard input. I just can't seem to get my C# code to not hang when trying to write to Process.StandardInput.
I've seen countless examples using Process.StandardInput.Close() when done writing. Every StackOverflow answer I found says to use this, and it does work. The problem is I can't close the StreamWriter because I'm not done interacting with the process. The process is a state machine that holds variables created using stdin, parses expressions, and returns an evaluation. I am expected to keep giving the process input after each output.
Does anyone have an example where Process.StandardInput.WriteLine is used more than once without closing or restarting the process?
This is how the C++ process is reading input. This example simply echos back the input and waits for another.
int main () {
std::string input;
while (getline(std::cin, input)) {
std::cout << input << std::endl;
}
}
My C# program tries to interact with this process using this wrapper class.
public class Expression {
System.Diagnostics.Process p;
public Expression () {
p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = "InputEcho.exe";
p.Start();
p.StandardInput.AutoFlush = true;
}
public void Run (in string input, out string output) {
p.StandardInput.WriteLine(input);
// p.StandardInput.Close();
output = p.StandardOutput.ReadToEnd();
}
}
This works when I uncomment p.StandardInput.Close() but then subsequent calls to Expression.Run() won't work because the writer is closed.
Main program
Expression expn = new();
string output;
Console.WriteLine("Expression start");
expn.Run("Hello", output);
Console.WriteLine(output);
expn.Run("Hi", output);
Console.WriteLine(output);
Expected output
Expression start
Hello
Hi
Actual output
Expression start
EDIT:
#Matthew Andrews provided a really good answer that works, but it's not quite what I'm after. I didn't think about using event delegates to receive output data, and I see why: It's hard to implement this into the wrapper that I want to use to build a process-relevant API. What I mean by this is that I want to write some method that communicates with the process, give it input, receive the output, and return this data to the caller before doing anything else. My Expression.Run method exemplifies this perfectly.
Here's an example of what the root caller would look like in a greater C# program.
bool GetConditionEval (string condition, SomeDataType data) {
// Makes another call to 'Run' that commands the C++ process to store a variable
// Input looks like this: "variableName = true" (aka key/value pairs)
Expression.SetVar(data.name, "true");
// Don't ask why I'm using an external process to set variables using string expressions.
// It's a company proprietary thing.
string output;
Expression.Run(in condition, out output);
if (output.ToLower() == "true") return true;
else if (output.ToLower() == "false") return false;
else throw new Exception("Output is something other than true or false.");
}
This is why I'd like for Run to immediately return the output it receives from the process.
If not, I guess I could find a way for a delegate method to store the output in a global container and the GetConditionEval can just reach into that. I worry about race conditions though.
Side note:
Since I do expect the API that is contained in this C++ process to eventaully take other forms, spinning this up as a standalone process and invoking the API via stdin is really a stopgap for now so I don't have to convert thousands of lines of C++ code into C#.
SOLUTION:
I figured out a solution using the asynchronous method Matthew suggested while having a linear process of sending input and working immediately off the output in the same sequence. I reconfigured my wrapper class to queue each output received from the event listener. This sets up a pattern where I can call one method to send input, and then call another method right after to pop output data off the queue if any. I compensated for the fact that output data might not be avaliable immediately by simply waiting if the queue is empty and then moving forward once something is there. This unfortuately makes it a blocking call if it does have to wait, but it's the best I have so far. I also implemented a failsafe so it doesn't wait indefinately.
public class Expression {
System.Diagnostics.Process p = new();
System.Collections.Generic.Queue<string> outputQ = new();
public Expression () {
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = "C2E2.exe";
p.OutputDataReceived += (s, e) => {
outputQ.Enqueue(e.Data);
};
p.Start();
p.BeginOutputReadLine();
}
/// Returns custom exception object if error is encountered.
public GRLib.Exception Run (in string input) {
if (p == null) return GRLib.Exception.New("Expression Evaluator not operational.");
try {
p.StandardInput.WriteLine(input);
}
catch (Exception e) {
return GRLib.Exception.New(e.Message);
}
return null;
}
/// Returns error code 1 if timeout occured.
/// Timeout is represented in milliseconds.
/// Blocking call.
public GRLib.Exception GetOutput (out string output, int timeout = 2000) {
/// Wait for something to show in the queue.
/// Waits indefinitely if timeout is 0.
/// If anyone knows a better way to implement this waiting loop,
/// please let me know!
int timeWaited = 0;
while (outputQ.Count == 0) {
System.Threading.Thread.Sleep(100);
if (timeout != 0 && (timeWaited += 100) > timeout) {
output = "ERR";
return GRLib.Exception.New(1, "Get timed out.");
}
}
output = outputQ.Dequeue();
return null;
}
...
}
Example usage
Expression expression = new();
var e = expression.Run("3 > 2");
if (e != null) // Handle error
string output;
e = expression.GetOutput(out output);
if (e != null) // Handle error
// 'output' should now be 'true' which can then be used in other parts of this program.
While the event listener in a standalone fashion works great, I need the output from the process to be returned in the same stack where the input is given because this is going to be part of a more complex call graph.
The problem you're observing is due to the synchronous nature of Process.StandardOutput.ReadToEnd(). Instead, you should listen for your output asynchronously by setting Process.BeginOutputReadLine() and utilizing the Process.OutputDataReceived event.
Here is a quick example to get you started:
var p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"ConsoleApplication1.exe";
p.OutputDataReceived += (s, e) =>
{
Console.WriteLine(e.Data);
};
p.Start();
p.BeginOutputReadLine();
while (true)
{
var readLine = Console.ReadLine();
p.StandardInput.WriteLine(readLine);
}
And here is the c++ I used for ConsoleApplication1.exe:
int main()
{
std::cout << "Hello World!\n";
std::string input;
while (std::getline(std::cin, input)) {
std::cout << input << std::endl;
}
}
Running my example will print Hello World! and then proceed to parrot whatever else you enter into the console.
The project makes many different calls to the command line for a variety of purposes. To make this easier, I wrote a method that simply requires a person to enter the command in as a parameter :
public string AsyncCommandCall(string sCommand, List<string> lOutput, int timeout)
{
if (!sCommand.ToLower().Substring(0, 5).Contains("/k"))
sCommand = "/k " + sCommand;
using(Process process = new Process())
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = sCommand;
startInfo.CreateNoWindow = true;
process.StartInfo = startInfo;
List<string> output = new List<string>();
List<string> error = new List<string>();
using(AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using(AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
if(!String.IsNullOrEmpty(e.Data))
output.Add(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if(e.Data == null)
{
errorWaitHandle.Set();
}
else
{
output.Add(e.Data);
}
};
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
if(process.WaitForExit(timeout) && outputWaitHandle.WaitOne(timeout) && errorWaitHandle.WaitOne(timeout))
{
m_sCmdOutput.Clear();
m_sCmdError.Clear();
m_sCmdOutput.AddRange(output);
m_sCmdError.AddRange(error);
if(lOutput != null)
{
lOutput.AddRange(output);
}
return AggregateList(output);
}
else
{
process.Close();
//a time out doens't necessarily mean that stuff didn't happen, it's likely that it didn't process.
if(error.Count > 0)
{
m_sCmdError.Clear();
m_sCmdError.AddRange(error);
}
Debug("Thread time out for " + sCommand);
if (output.Count > 0)
{
m_sCmdOutput.Clear();
m_sCmdOutput.AddRange(output);
if (lOutput != null)
{
lOutput.AddRange(output);
}
return (AggregateList(output));
}
else
{
Debug("Returning null");
return null;
}
}
}
}
}
The reason I am calling it asynchronously is that some of the commands I'm calling aren't guaranteed to work, so this ideally would allow me to try again if it times out.
When running my program, I noticed that one command, "time /t" would always timeout.
To investigate, I tried running the code independently in the main loop of my program, and surprisingly it ran.
I became curious why this exact same command executed in once place while failing to run in another place. I ran another test where I placed the command call into a while loop, and soon found that the command calls stopped working as expected after exactly 4 AsyncCommandCall method calls. Looking back through my code, there were exactly 4 command calls before I called "time /t". I'm wondering if this is a bug in the api or if I'm doing something else wrong
Before anyone suggests it, I should also note that I did write a synchronous command call method that does not contain a "using" statement, but running it causes a hang on "process.WaitForExit()". Any help would be greatly appreciated.
EDIT
I noticed during my testing that if I increase the timeout that I pass as a parameter, more iterations are successfully called. Is there some kind of buffer that can be cleared so that the process time doesn't increase with each call?
As it turns out, this problem was dependent on the /k parameter that the method added to each command call. The /k flag tells the console to keep the output open, causing things like consistent timeouts using this asynchronous method, clogging system memory, and preventing process.WaitForExit() from returning. Instead, I am now using the /c flag before each command call and am successfully reading the output from each command. In calling AsyncCommandCall(command, null, 100) 1000 times in a row looping through three commands (An echo, a dir, and a psexec), there were 0 timeouts and 0 failed reads.
This question is essentially about the suitability of Microsoft's Speech API (SAPI) for server workloads and whether it can be used reliably inside of w3wp for speech synthesis. We have an asynchronous controller that uses uses the native System.Speech assembly in .NET 4 (not the Microsoft.Speech one that ships as part of Microsoft Speech Platform - Runtime Version 11) and lame.exe to generate mp3s as follows:
[CacheFilter]
public void ListenAsync(string url)
{
string fileName = string.Format(#"C:\test\{0}.wav", Guid.NewGuid());
try
{
var t = new System.Threading.Thread(() =>
{
using (SpeechSynthesizer ss = new SpeechSynthesizer())
{
ss.SetOutputToWaveFile(fileName, new SpeechAudioFormatInfo(22050, AudioBitsPerSample.Eight, AudioChannel.Mono));
ss.Speak("Here is a test sentence...");
ss.SetOutputToNull();
ss.Dispose();
}
var process = new Process() { EnableRaisingEvents = true };
process.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"bin\lame.exe");
process.StartInfo.Arguments = string.Format("-V2 {0} {1}", fileName, fileName.Replace(".wav", ".mp3"));
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = false;
process.StartInfo.RedirectStandardError = false;
process.Exited += (sender, e) =>
{
System.IO.File.Delete(fileName);
AsyncManager.OutstandingOperations.Decrement();
};
AsyncManager.OutstandingOperations.Increment();
process.Start();
});
t.Start();
t.Join();
}
catch { }
AsyncManager.Parameters["fileName"] = fileName;
}
public FileResult ListenCompleted(string fileName)
{
return base.File(fileName.Replace(".wav", ".mp3"), "audio/mp3");
}
The question is why does SpeechSynthesizer need to run on a separate thread like that in order to return (this is reported elsewhere on SO here and here) and whether implementing a STAThreadRouteHandler for this request is more-efficient/scalable than the approach above?
Second, what are the options for running SpeakAsync in an ASP.NET (MVC or WebForms) context? None of the options I've tried seem to work (see update below).
Any other suggestions for how to improve this pattern (i.e. two dependencies that must execute serially to each other but each has async support) are welcome. I don't feel this scheme is sustainable under load, especially considering the known memory leaks in SpeechSynthesizer. Considering running this service on a different stack all together.
Update:
Neither of the Speak or SpeakAsnc options appear to work under the STAThreadRouteHandler. The former produces:
System.InvalidOperationException: Asynchronous operations are not
allowed in this context. Page starting an asynchronous operation has
to have the Async attribute set to true and an asynchronous operation
can only be started on a page prior to PreRenderComplete event. at
System.Web.LegacyAspNetSynchronizationContext.OperationStarted() at
System.ComponentModel.AsyncOperationManager.CreateOperation(Object
userSuppliedState) at
System.Speech.Internal.Synthesis.VoiceSynthesis..ctor(WeakReference
speechSynthesizer) at
System.Speech.Synthesis.SpeechSynthesizer.get_VoiceSynthesizer() at
System.Speech.Synthesis.SpeechSynthesizer.SetOutputToWaveFile(String
path, SpeechAudioFormatInfo formatInfo)
The latter results in:
System.InvalidOperationException: The asynchronous action method
'Listen' cannot be executed synchronously. at
System.Web.Mvc.Async.AsyncActionDescriptor.Execute(ControllerContext
controllerContext, IDictionary`2 parameters)
It seems like a custom STA thread pool (with ThreadStatic instances of the COM object) is a better approach: http://marcinbudny.blogspot.ca/2012/04/dealing-with-sta-coms-in-web.html
Update #2: It doesn't seem like System.Speech.SpeechSynthesizer needs STA treatment, seems to run fine on MTA threads so long as you follow that Start/Join pattern. Here's a new version that is able to correctly use SpeakAsync (issue there was disposing it prematurely!) and breaks up the WAV generation and the MP3 generation into two separate requests:
[CacheFilter]
[ActionName("listen-to-text")]
public void ListenToTextAsync(string text)
{
AsyncManager.OutstandingOperations.Increment();
var t = new Thread(() =>
{
SpeechSynthesizer ss = new SpeechSynthesizer();
string fileName = string.Format(#"C:\test\{0}.wav", Guid.NewGuid());
ss.SetOutputToWaveFile(fileName, new SpeechAudioFormatInfo(22050,
AudioBitsPerSample.Eight,
AudioChannel.Mono));
ss.SpeakCompleted += (sender, e) =>
{
ss.SetOutputToNull();
ss.Dispose();
AsyncManager.Parameters["fileName"] = fileName;
AsyncManager.OutstandingOperations.Decrement();
};
CustomPromptBuilder pb = new CustomPromptBuilder(settings.DefaultVoiceName);
pb.AppendParagraphText(text);
ss.SpeakAsync(pb);
});
t.Start();
t.Join();
}
[CacheFilter]
public ActionResult ListenToTextCompleted(string fileName)
{
return RedirectToAction("mp3", new { fileName = fileName });
}
[CacheFilter]
[ActionName("mp3")]
public void Mp3Async(string fileName)
{
var process = new Process()
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo()
{
FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"bin\lame.exe"),
Arguments = string.Format("-V2 {0} {1}", fileName, fileName.Replace(".wav", ".mp3")),
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = false
}
};
process.Exited += (sender, e) =>
{
System.IO.File.Delete(fileName);
AsyncManager.Parameters["fileName"] = fileName;
AsyncManager.OutstandingOperations.Decrement();
};
AsyncManager.OutstandingOperations.Increment();
process.Start();
}
[CacheFilter]
public ActionResult Mp3Completed(string fileName)
{
return base.File(fileName.Replace(".wav", ".mp3"), "audio/mp3");
}
I/O is very expensive on a server. how many multiple streams of wav writting do you think you can get on a server hard drive? Why not do it all in memory and only write the mp3 when it's fully processed? mp3's are much smaller and the I/O will be engaged for a small amount of time. You can even change the code to return the stream directly to the user instead of saving to an mp3 if you want.
How do can I use LAME to encode an wav to an mp3 c#
This question is a bit old now, but this is what I'm doing and it's been working great so far:
public Task<FileStreamResult> Speak(string text)
{
return Task.Factory.StartNew(() =>
{
using (var synthesizer = new SpeechSynthesizer())
{
var ms = new MemoryStream();
synthesizer.SetOutputToWaveStream(ms);
synthesizer.Speak(text);
ms.Position = 0;
return new FileStreamResult(ms, "audio/wav");
}
});
}
might help someone...
In my XNA game, I've got the game window and the console which is running a threaded Console.ReadLine() so the game doesn't hang while waiting for scripting input. I'm trying to get it where when the game window closes, the Console closes automatically, as well as the input to actually work (with the ability to print things out whilst waiting on input).
I've gotten it to close automatically now by using code from this question: How to add a Timeout to Console.ReadLine()?
However, when I press enter to the input, an ObjectDisposedException is thrown. Also, I'm stuck with using a timeout when I'd rather the thing be instant. How would I go about fixing this?
public class ConsoleInput
{
public bool running = true;
public void Run()
{
String input;
while (running)
{
input = ReadLine(500);
//stuff
}
}
string ReadLine(int timeoutms)
{
ReadLineDelegate d = Console.ReadLine;
IAsyncResult result = d.BeginInvoke(null, null);
result.AsyncWaitHandle.WaitOne(timeoutms);//timeout e.g. 15000 for 15 secs
if (result.IsCompleted)
{
string resultstr = d.EndInvoke(result);
Console.WriteLine("Read: " + resultstr);
return resultstr;
}
result.AsyncWaitHandle.Dispose();
return "";
}
delegate string ReadLineDelegate();
}
Which is called by:
LuaInput = new ConsoleInput();
LuaInputThread = new Thread(new ThreadStart(LuaInput.Run));
LuaInputThread.Start();
Thanks!
Have you tried setting the thread's
IsBackground = true; ? This will force it closed and will not allow the thread to block.
I want to create a LaTeX editor to produce pdf documents.
Behind the scene, my application uses pdflatex.exe executed through a Process instance.
pdflatex.exe needs an input file, e.g., input.tex as follows
\documentclass{article}
\usepackage[utf8]{inputenc}
\begin{document}
\LaTeX\ is my tool.
\end{document}
For the sake of simplicity, here is the minimal c# codes used in my LaTeX editor:
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Process p = new Process();
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(p_Exited);
p.StartInfo.Arguments = "input.tex";
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "pdflatex.exe";
p.Start();
p.WaitForExit();
}
static void p_Exited(object sender, EventArgs e)
{
// remove all auxiliary files, excluding *.pdf.
}
}
}
The question is
How to detect the pdflatex.exe whether it stops working due to an invalid input?
Edit
This is the final working solution:
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Process p = new Process();
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(p_Exited);
p.StartInfo.Arguments = "-interaction=nonstopmode input.tex";// Edit
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "pdflatex.exe";
p.StartInfo.RedirectStandardError = true;
p.Start();
p.WaitForExit();
//Edit
if (p.ExitCode == 0)
{
Console.WriteLine("Succeeded...");
}
else
{
Console.WriteLine("Failed...");
}
}
static void p_Exited(object sender, EventArgs e)
{
// remove all files excluding *.pdf
//Edit
Console.WriteLine("exited...");
}
}
}
The idea using -interaction=nonstopmode belongs to #Martin here.
Most command-line applications set an exit code to indicate success or failure. You test it thus:
p.WaitForExit();
if (p.ExitCode == 0) {
// Success
} else {
// Failure
}
I suppose that you can understand if pdflatex has stopped working by looking at its output (e.g. matching an error message, seeing that it doesn't output anything for more than 30 seconds, something like that).
To be able to perform such checks, you should redirect the standard output and standard error of pdflatex (you can find many examples just by searching in SO, the key is the ProcessStartInfo.RedirectStandardOutput property) to a stream that you can read/a callback to a function of yours; in this way you should be able to detect the condition whence you deduce that pdflatex is stuck, and then you can kill it with p.Kill().
If you have a means to detect your process has stopped working, you can use
p.Kill();
to terminate the process
One way to go about it is a timeout. If you have a separate thread to launch this proces, you can start the thread and use
if(processThread.Join(waitTime))
{
// worked
}
else
{
// Timeout. need to kill process
}
where waitTime is of type TimeSpan
Time-outs are better suited for a shelled application that performs background processing. The following code sample sets a time-out for the shelled application. The time-out for the example is set to 5 seconds. You may want to adjust this number (which is calculated in milliseconds) for your testing:
//Set a time-out value.
int timeOut = 5000;
//Start the process.
Process p = Process.Start(someProcess);
//Wait for window to finish loading.
p.WaitForInputIdle();
//Wait for the process to exit or time out.
p.WaitForExit(timeOut);
//Check to see if the process is still running.
if (p.HasExited == false)
{
//Process is still running.
//Test to see if the process is hung up.
if (p.Responding)
{
//Process was responding; close the main window.
p.CloseMainWindow();
}
else
{
//Process was not responding; force the process to close.
p.Kill();
}
}
//continue