I have a few functions in a Solidworks Addin which call on a VBA macro (Via the runMacro2 method) a co-worker has been working on for the last few weeks. In his code he calls a Solidworks function which, under certain, unknown conditions, hangs for a long period of time. How long seems to depend upon the size and quantity of bodies in the part. Considering at least one of the functions we want to run this from i automatic, this just wont do.
I have tried using the Thread.Join(int) method (shown below) but it doesnt work. I also tried modifying the code from this answer Close a MessageBox after several seconds with the same results. Is there anything I can do either in C# or VBA to handle a timeout for this without re-writing his entire macro?
public void runBB()
{
Stopwatch testStop = new Stopwatch();
Thread workerThread = new Thread(bbRun);
testStop.Start();
workerThread.Start();
if (!workerThread.Join(50))
{
workerThread.Abort();
testStop.Stop();
MessageBox.Show("Unable to generate Bounding Box after " + testStop.ElapsedMilliseconds/1000 + " seconds. Please enter data manually.", "Solidworks Derped Error.");
}
return;
}//Still uses Macro (2-5-16)
public static void bbRun()
{
iSwApp.RunMacro2(macroPath + "BOUNDING_BOX.swp", "test11", "main", 0, out runMacroError);
return;
}
I was getting this same exact issue with SOLIDWORKS hanging on an open of a file. Almost all reference on SO was that you should never do this, but in this scenario, you either have to close it or wait forever. In C# I created a callWithTimeout method:
private void callWithTimeout(Action action, int timeoutMilliseconds, String errorText) {
Thread threadToKill = null;
Action wrappedAction = () =>
{
threadToKill = Thread.CurrentThread;
action();
};
IAsyncResult result = wrappedAction.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) {
wrappedAction.EndInvoke(result);
} else {
threadToKill.Abort();
throw new TimeoutException(errorText);
}
}
Then the code that was hanging put in a block as such:
bool timedOut = false;
try {
callWithTimeout(delegate() {
// code that hangs here
}, 60000, "Operation timed out. SOLIDWORKS could not open the file. This file will be processed later.");
} catch (TimeoutException){
timedOut = true;
} finally {
if(timedOut) {
Process[] prs = Process.GetProcesses();
foreach (Process p in prs) {
if (p?.ProcessName.Equals("SLDWORKS") ?? false)
p?.Kill();
}
}
}
Related
Im trying to setup a named pipes connection between a server-software and a client.
This is my server code:
class Program
{
private static NamedPipeServerStream server = new NamedPipeServerStream("p", PipeDirection.InOut);
static void Main(string[] args)
{
Action<NamedPipeServerStream> a = callBack;
a.BeginInvoke(server, ar => { }, null);
}
private static void callBack(NamedPipeServerStream pipe)
{
pipe.WaitForConnection();
var line = "";
while (line != "exit")
{
try
{
StreamReader reader = new StreamReader(pipe);
while (line != "exit")
{
line = reader.ReadLine();
switch (line)
{
default:
foo();
break;
}
}
break;
}
catch (IOException)
{
pipe.Disconnect();
continue;
}
}
}
}
But when I try to run this application it exits directly after the start.
I have used the debugger to reach the WaitForConnection-line and then it exits without an error or an exception. And there is nothing running but the server.
It quits without getting handled by the catch-part. Am I missing anything?
The execution is blocked on WaitForConnection and will not continue until a NamedPipeClientStream object establishes a connection to the NamedPipeServerStream object.
Please have a look here (The example should help you out): https://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream(v=vs.110).aspx
EDIT: I misunderstood the problem and originally thought that the question had to do with the execution blocking from the WaitForConnection method. The code below is the addition to the actual quesiton as well as my explanation.
The problem is the way you invoke your action. As you are calling BeginInvoke it is invoked asynchronously, on a threadpool thread. Therefor you need to check if the action has finished or not otherwise the main thread keeps running and reaches the end. I have updated my answer on how you can do this very easily
Hopefully this code helps you understand why it exists! :)
Action<NamedPipeServerStream> a = callBack;
var result = a.BeginInvoke(server,ar => { },null);
while (!result.IsCompleted)
{
}
In the constructor i'm calling WatchDirectory method:
private void WatchDirectory()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = userVideosDirectory;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
watcher.Filter = "*.mp4";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
Then the event OnChanged:
private void OnChanged(object source, FileSystemEventArgs e)
{
try
{
var info = new FileInfo(e.FullPath);
fileforupload = info.FullName;
if (e.ChangeType == WatcherChangeTypes.Changed)
{
var theSize = info.Length;
label2.BeginInvoke((Action)(() =>
{
label2.Text = theSize.ToString();
}));
}
dirchanged = true;
}
catch (Exception ee)
{
string err = ee.ToString();
}
}
Then i'm using a while loop to check when dirchange flag is true:
WatchDirectory();
while (dirchanged == false)
{
if (dirchanged == true)
{
Youtube_Uploader youtubeupload = new
Youtube_Uploader(fileforupload);
break;
}
}
The problem is that sometimes it's never changes the dirchanged to true on the OnChanged event. Not sure why. It seems to fire the OnChanged event but sometimes it doesn't execute the dirchanged = true;
Therefore inside the while loop dirchanged flag remains false all the time.
I added now a new method i called it IsFileLocked:
protected virtual bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
if (stream != null)
stream.Close();
}
return false;
}
And i use this in the event OnChanged:
private void OnChanged(object source, FileSystemEventArgs e)
{
try
{
var info = new FileInfo(e.FullPath);
fileforupload = info.FullName;
IsFileLocked(info);
if (e.ChangeType == WatcherChangeTypes.Changed)
{
var theSize = info.Length;
label2.BeginInvoke((Action)(() =>
{
label2.Text = theSize.ToString();
}));
}
dirchanged = true;
}
catch (Exception ee)
{
string err = ee.ToString();
}
}
And in the method IsFileLocked i'm getting exception:
The process cannot access the file 'C:\Users\bout0_000\Videos\My Great Game - My Great Capture - 2015-08-10 14-22-52.mp4' because it is being used by another process.
I'm using external program that create the file and since the program still working on creating the file the watcher can't get to it.
So i have a confilct here from one side i want to know to watch when the file is ready finished created but on the other side i can't know since the external program still working on it.
So how can i find out when the external program finished working on the file and the file is ready ?
This is the whole part of the code of the while:
if (request.QueryString[0] == "stop")
{
dirchanged = false;
StartRecrod();
result = "Recording stopped and preparing the file to be shared on youtube";
WatchDirectory();
while (dirchanged == false)
{
if (dirchanged == true)
{
string ttttt = "ok";
break;
}
}
}
I added a string ttttt just for testing.
Sometimes it's getting to the string ttttt when using a break point and sometimes not.
In my program when i touch my android screen it send command to the pc web server and it's getting here but someting is wrong with the while loop and the flag dirchanged sometimes it does enter the while and the IF and does the string ttttt and sometimes it dosen't.
This is what i did now with the await:
TaskCompletionSource<bool> sy;
public async void SendResponse(HttpListenerRequest request)
{
string result = "";
string key = request.QueryString.GetKey(0);
if (key == "cmd")
{
if (request.QueryString[0] == "nothing")
{
return "Connection Success";
}
if (request.QueryString[0] == "start")
{
StartRecrod();
result = "Recording started";
}
if (request.QueryString[0] == "stop")
{
dirchanged = false;
StartRecrod();
result = "Recording stopped and preparing the file to be shared on youtube";
sy = new TaskCompletionSource<bool>();
WatchDirectory();
await sy.Task;
Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);
}
}
else
{
result = "Nothing have been done";
}
if (Youtube_Uploader.fileuploadedsuccess != null && Youtube_Uploader.fileuploadedsuccess != "")
{
result = Youtube_Uploader.fileuploadedsuccess;
}
return result;
}
But some problems.
First i'm getting errors over all the returns.
Error 2 Since 'Automatic_Record.Form1.SendResponse(System.Net.HttpListenerRequest)' returns void, a return keyword must not be followed by an object expression
And error when init my web server:
WebServer ws = new WebServer(SendResponse, "http://+:8098/");
On the SendResponse i'm getting:
Error 1 'void Automatic_Record.Form1.SendResponse(System.Net.HttpListenerRequest)' has the wrong return type
This errors happen now when changed the method to async.
This is my WebServer method that i get error when init it since it should get something else then async:
public WebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
: this(prefixes, method) { }
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("Webserver running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
string rstr = _responderMethod(ctx.Request);
System.Diagnostics.Trace.Write(ctx.Request.QueryString);
//ctx.Request.QueryString
byte[] buf = Encoding.UTF8.GetBytes(rstr);
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
}
catch { } // suppress any exceptions
finally
{
// always close the stream
ctx.Response.OutputStream.Close();
}
}, _listener.GetContext());
}
}
catch { } // suppress any exceptions
});
}
This code is horribly broken. Yes, dirchanged is always false inside the while loop, because if it becomes true you won't be in the while loop any longer.
In addition, your code blocks events from occurring, which may block the file watcher event itself, and also is not optimization safe. Use proper synchronization, here's an example:
TaskCompletionSource<bool> sy;
private void OnChanged(object source, FileSystemEventArgs e)
{
sy.SetResult(true);
}
and wait with
sy = new TaskCompletionSource<bool>();
WatchDirectory();
await sy.Task; // or sy.Task.Wait()
(You'll need to use the async keyword on the method containing that code).
This fixes all the problems you had before -- it doesn't burn CPU cycles, it continues processing Windows messages, and it won't break if the compiler chooses to cache variables in registers.
dirchanged could be getting set to true just after evaluating the inner if block. Then, next loop it breaks out without ever running your uploader.
So you have two main questions?
1.) Why is dirchanged not being set to true?
and the apparent cause...
2.) How do you use FileSystemWatcher to only act on a file that's available for edit?
FileSystemWatcher is known for being a little touchy, and I agree with your diagnosis that file access is probably the culprit. An unpredictable file access error is exactly what I would expected from a FileSystemWatcher trying to do something with a file that was just modified. Can you edit the code that's creating the file? If so, one method I've used with FileSystemWatcher is to have it only watch for file creation of a fictitious file type such as ".fsw". The program creating the file will then rename it to ".fsw" whenever it is done editing it, that way the FileSystemWatcher only gets called when it has a file available to act upon, and it can then rename the file to it's actual type. Also, if you can edit the creation code, make sure that you are doing everything you can to release the file from there. I've seen this behavior before because I forgot to close a TextWriter.
Also, I would move the line
dirchanged = true;
Outside of the try statement. Why have it in there since it definitely wont throw an error? Also, your catch statement isn't really doing error handling. Any error in your try statement and you get ejected before reaching the dirchanged = true line without being alerted that this is what happened. Have you tested your delegate code on its own? Is there a need to have the if statement for type = changed right there? If you're troubleshooting, I would consider limiting your try statement content or moving it to after the while loop as much as possible.
Also, wouldn't this be a lot more simple for your while statement?
while (dirchanged == false){}
Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);
It's not the most elegant solution, but one work around is to simply wait if you know the program creating/editing the file is going to close it very soon...
while (dirchanged == false){}
System.Threading.Thread.Sleep(1000);
Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);
EDIT: Better yet, rather than a while statement use Ben Voigt's suggestion of a TaskCompletionSource. You'll still have to deal with the file being locked but you should be able to do that after the "task" has been flagged as completed.
Read through most (all?) of the answered questions regarding the C# BackgroundWorker but none seemed to apply to this situation. If I missed one, please point me in that direction!
Anyway, I having troubles getting the Ping process to run as a background process. I made a simple form application to send pings and report back. That worked fine but it would only results results to the user after the pings were complete -- thus the need to a background process. I am somewhat new to C# and was unfamiliar with the particulars of BackgroundWorker. However found a helpful walkthrough from Microsoft here: http://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
I am now attempting to get the same process to apply to a System.Net.NetworkInformation object instead of a System.IO.StreamReader object. I think I am really close (read: I can get the app to build and run) but I consistently get an error at runtime (see below).
This is the Microsoft code for their sample app. It works like a champ:
The method in MainForm.cs that calls the Words.cs class referenced in the walkthrough
void backgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
Words WC = (Words)e.Argument;
WC.CountWords(worker, e);
}
The relevant method in the 'Words.cs' class
public void CountWords(
System.ComponentModel.BackgroundWorker worker,
System.ComponentModel.DoWorkEventArgs e)
{
// Initialize the variables.
CurrentState state = new CurrentState();
string line = "";
int elapsedTime = 20;
DateTime lastReportDateTime = DateTime.Now;
if (CompareString == null ||
CompareString == System.String.Empty)
{
throw new Exception("CompareString not specified.");
}
// Open a new stream.
using (System.IO.StreamReader myStream = new System.IO.StreamReader(SourceFile))
{
// Process lines while there are lines remaining in the file.
while (!myStream.EndOfStream)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
line = myStream.ReadLine();
WordCount += CountInString(line, CompareString);
LinesCounted += 1;
// Raise an event so the form can monitor progress.
int compare = DateTime.Compare(
DateTime.Now, lastReportDateTime.AddMilliseconds(elapsedTime));
if (compare > 0)
{
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
}
// Uncomment for testing.
System.Threading.Thread.Sleep(5);
}
// Report the final count values.
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(0, state);
}
}
When I try a similar process (sending a Ping instead of a reading a file) I get this error:
Error: Object reference not set to an instance of an object.
Details: System.Collections.ListDictionaryInternal //This is defined in the MyApp namespace as: using System.Collections
Source: MyApp
StackTrack: at MyApp.MainForm.Bw01DoWork(Object sender, DoWorkEventArgs e) in
[path]\MainForm.cs:line 152
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
Target: Void Bw01DoWork(System.Object, System.ComponentModel.DoWorkEventArgs)
Here is my method. Line 152 referenced in the error is the very last line of the last method in MainForm.cs (the var names are different, but you get the idea):
void Bw01DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
PTResults PR = (PTResults)e.Argument;
PR.SendPings(worker, e); // Line 152
}
And the relevant portion of the PTResults.cs class:
using (Ping newPing = new Ping())
{
PingReply reply = newPing.Send([Target Site],[Timeout]);
if(reply.Status == IPStatus.Success)
{
state.PingOK = true;
}
else if(reply.Status == IPStatus.TimedOut)
{
state.PingOK = false;
state.PingUpdateState = " Timed Out";
}
else if(reply.Status != IPStatus.Success)
{
state.PingOK = false;
state.PingUpdateState = " FAILED";
}
else
{
state.PingOK = false;
state.PingUpdateState = " UNKNOWN";
}
worker.ReportProgress(0, state.PingOK);
}
I am thinking the System.Net.NetworkInformation.Ping component cannot be invoked the same way System.IO.StreamReader is. Thoughts?
I doubt it makes a difference but FWIW I am coding in SharpDevelop on a Windows 8.1 system.
Take a look at the Ping SendAsync, you may be able to eliminate most of your code - just call PingAsync, and handle the result being sure to dispatch it to the UI thread and then re-queue another call.
http://msdn.microsoft.com/en-us/library/ms144961(v=vs.110).aspx
I am trying to build a c# console app that will monitor about 3000 urls (Just need to know that HEAD request returned 200, not necessarily content, etc.)
My attempt here was to build a routine the checks the web URLS, looping and creating threads each executing the routine. What's happening is if i run with <20 threads, it executes ok most of the time, but if i use >20 threads, some of the url's time out. I tried increasing the Timeout to 30 seconds, same occurs. The network I am running this on is more than capable of executing 50 HTTP HEAD requests (10MBIT connection at ISP), and both the CPU and network run very low when executing the routine.
When a timeout occurs, i test the same IP on a browser and it works fine, I tested this repeatedly and there was never a case during testing that a "timed out" url was actually timing out.
The reason i want to run >20 threads is that i want to perform this test every 5 minutes, with some of the URL's taking a full 10sec (or higher if the timeout is set higher), i want to make sure that its able to run through all URLs within 2-3 minutes.
Is there a better way to go about checking if a URL is available, or, should I be looking at the system/network for an issue.
MAIN
while (rdr.Read())
{
Thread t = new Thread(new ParameterizedThreadStart(check_web));
t.Start(rdr[0]);
}
static void check_web(object weburl)
{
bool isok;
isok = ConnectionAvailable(weburl.ToString());
}
public static bool ConnectionAvailable(string strServer)
{
try
{
strServer = "http://" + strServer;
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
reqFP.Timeout = 10000;
reqFP.Method = "HEAD";
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
Console.WriteLine(strServer + " - OK");
rspFP.Close();
return true;
}
else
{
Console.WriteLine(strServer + " Server returned error..");
rspFP.Close();
return false;
}
}
catch (WebException x)
{
if (x.ToString().Contains("timed out"))
{
Console.WriteLine(strServer + " - Timed out");
}
else
{
Console.WriteLine(x.Message.ToString());
}
return false;
}
}
Just remember, you asked.
Very bad implementation.
Do not go creating threads like that. It does very little good to have more threads than processor cores. The extra threads will pretty much just compete with each other, especially since they're all running the same code.
You need to implement using blocks. If you throw an exception (and chances are you will), then you will be leaking resources.
What is the purpose in returning a bool? Do you check it somewhere? In any case, your error and exception processing are a mess.
When you get a non-200 response, you don't display the error code.
You're comparing against the Message property to decide if it's a timeout. Microsoft should put a space between the "time" and "out" just to spite you.
When it's not a timeout, you display only the Message property, not the entire exception, and the Message property is already a string and doesn't need you to call ToString() on it.
Next Batch of Changes
This isn't finished, I don't think, but try this one:
public static void Main()
{
// Don't mind the interpretation. I needed an excuse to define "rdr"
using (var conn = new SqlConnection())
{
conn.Open();
using (var cmd = new SqlCommand("SELECT Url FROM UrlsToCheck", conn))
{
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
// Use the thread pool. Please.
ThreadPool.QueueUserWorkItem(
delegate(object weburl)
{
// I invented a reason for you to return bool
if (!ConnectionAvailable(weburl.ToString()))
{
// Console would be getting pretty busy with all
// those threads
Debug.WriteLine(
String.Format(
"{0} was not available",
weburl));
}
},
rdr[0]);
}
}
}
}
}
public static bool ConnectionAvailable(string strServer)
{
try
{
strServer = "http://" + strServer;
var reqFp = (HttpWebRequest)WebRequest.Create(strServer);
reqFp.Timeout = 10000;
reqFp.Method = "HEAD";
// BTW, what's an "FP"?
using (var rspFp = (HttpWebResponse) reqFp.GetResponse()) // IDisposable
{
if (HttpStatusCode.OK == rspFp.StatusCode)
{
Debug.WriteLine(string.Format("{0} - OK", strServer));
return true; // Dispose called when using is exited
}
// Include the error because it's nice to know these things
Debug.WriteLine(String.Format(
"{0} Server returned error: {1}",
strServer, rspFp.StatusCode));
return false;
}
}
catch (WebException x)
{
// Don't tempt fate and don't let programs read human-readable messages
if (x.Status == WebExceptionStatus.Timeout)
{
Debug.WriteLine(string.Format("{0} - Timed out", strServer));
}
else
{
// The FULL exception, please
Debug.WriteLine(x.ToString());
}
return false;
}
}
Almost Done - Not Tested Late Night Code
public static void Main()
{
using (var conn = new SqlConnection())
{
conn.Open();
using (var cmd = new SqlCommand("", conn))
{
using (var rdr = cmd.ExecuteReader())
{
if (rdr == null)
{
return;
}
while (rdr.Read())
{
ThreadPool.QueueUserWorkItem(
CheckConnectionAvailable, rdr[0]);
}
}
}
}
}
private static void CheckConnectionAvailable(object weburl)
{
try
{
// If this works, it's a lot simpler
var strServer = new Uri("http://" + weburl);
using (var client = new WebClient())
{
client.UploadDataCompleted += ClientOnUploadDataCompleted;
client.UploadDataAsync(
strServer, "HEAD", new byte[] {}, strServer);
}
}
catch (WebException x)
{
Debug.WriteLine(x);
}
}
private static void ClientOnUploadDataCompleted(
object sender, UploadDataCompletedEventArgs args)
{
if (args.Error == null)
{
Debug.WriteLine(string.Format("{0} - OK", args.UserState));
}
else
{
Debug.WriteLine(string.Format("{0} - Error", args.Error));
}
}
Use ThreadPool class. Don't spawn hundreds of threads like this. Threads have such a huge overhead and what happens in your case is that your CPU will spend 99% time on context switching and 1% doing real work.
Don't use threads.
Asynch Call backs and queues. Why create a thread when the resource that they are all wanting is access to the outside world. Limit your threads to about 5, and then implement a class that uses a queue. split the code into two parts, the fetch and the process. One controls the flow of data while the other controls access to the outside world.
Use whatever language you like but you won't got wrong if you think that threads are for processing and number crunching and async call backs are for resource management.
I am working with C# and having trouble understanding how to read stdout asynchronously from a child process. What I want to do is create a child process that executes an application and then present whatever is received from that process' stdout in a textbox. I need to see every output character from the child process immediately and can not wait for a line to complete, therefore I don't think that the Process.OutputDataReceived event suits my purpose. Can you tell me a sane way to accomplish this?
I have tried calling Process.StandardOutput.BaseStream.BeginRead() and passing a call-back function to it, but in this call-back function I receive an exception from Process.StandardOutput.BaseStream.EndRead().
My code looks like this (the child process is a script engine - abbreviated "SE" - for verifying the functionality of an external device. Scripts are executed in sequence and each script requires one instance of the SE application)
private bool startScript()
{
// Starts the currently indexed script
if (null != scriptList)
{
if (0 == scriptList.Count || scriptListIndexer == scriptList.Count)
{
// If indexer equals list count then there are no active scripts in
// the list or the last active script in the list has just finished
return false; // ## RETURN ##
}
if (ScriptExecutionState.RUNNING == scriptList[scriptListIndexer].executionState)
{
return false; // ## RETURN ##
}
if (0 == SE_Path.Length)
{
return false; // ## RETURN ##
}
SE_Process = new Process();
SE_Process.StartInfo.FileName = SE_Path;
SE_Process.StartInfo.CreateNoWindow = true;
SE_Process.StartInfo.UseShellExecute = false;
SE_Process.StartInfo.RedirectStandardError = true;
SE_Process.StartInfo.RedirectStandardOutput = true;
SE_Process.EnableRaisingEvents = true;
SE_Process.StartInfo.Arguments = scriptList[scriptListIndexer].getParameterString();
// Subscribe to process exit event
SE_Process.Exited += new EventHandler(SE_Process_Exited);
try
{
if (SE_Process.Start())
{
// Do stuff
}
else
{
// Do stuff
}
}
catch (Exception exc)
{
// Do stuff
}
// Assign 'read_SE_StdOut()' as call-back for the event of stdout-data from SE
SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null);
return true; // ## RETURN ##
}
else
{
return false; // ## RETURN ##
}
}
private void read_SE_StdOut(IAsyncResult result)
{
try
{
int bytesRead = SE_Process.StandardOutput.BaseStream.EndRead(result); // <-- Throws exceptions
if (0 != bytesRead)
{
// Write the received data in output textbox
...
}
// Reset the callback
SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null);
}
catch (Exception exc)
{
// Do stuff
}
}
void SE_Process_Exited(object sender, EventArgs e)
{
// Keep track of whether or not the next script shall be started
bool continueSession = false;
switch (SE_Process.ExitCode)
{
case 0: // PASS
{
// Do stuff
}
...
}
SE_Process.Dispose(); // TODO: Is it necessary to dispose of the process?
if (continueSession)
{
ts_incrementScriptListIndexer();
if (scriptListIndexer == scriptList.Count)
{
// Last script has finished, reset the indexer and re-enable
// 'scriptListView'
...
}
else
{
if (!startScript())
{
// Do stuff
}
}
}
else
{
ts_resetScriptListIndexer();
threadSafeEnableScriptListView();
}
}
What happens is that after one SE process finishes I get an exception of type InvalidOperationException that says
StandardOut has not been redirected or
the process hasn't started yet.
from the call to SE_Process.StandardOutput.BaseStream.EndRead(). I do not understand why because I have set SE_Process.StartInfo.RedirectStandardOutput before start of every new process. It appears to me as if the stdout stream of an exited process calls my read_SE_StdOut() function after the process has been disposed, is that possible?
Thank you for reading!
The exception you get is normal. One BeginRead() call can never succeed: the last one, just after the process terminates. You'd normally avoid calling BeginRead() again if you know the process is completed so you don't get the exception. However, you'd rarely know. Just catch the exception. Or use BeginOutputReadLine() instead, it will catch it for you.
I'm guessing that you also redirect stderr and that the tool uses it to output "X". There is no way to keep output on both stderr and stdout synchronized after it is buffered and redirected.