Writing to textbox inside an unselected tab - c#

I have a program that can read and write on serial, GPIB, USB, and Ethernet. It has a tab for each method of communication with a textbox inside that displays communication on the port. One of the tabs is listed as All Comms and that text box has data from all communication methods. I am currently working on the serial port portion of the code and my program keeps freezing. Half the time I run my code it functions without issue writing to both tabs. The other half it freezes up when it tries to write to the text box inside the tab that is not selected(found by stepping through the code a line at a time).
I pulled the text boxes outside the tab control and this fixes the freezing issue. When the program freezes it does not display an error message and does not crash so no crash report(left it running over the weekend and it never finished crashing).
I would think that I need to select the other tab and then write to it, but why would the code work correctly half the time I run it?
Image of the program
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
rxString = rxString + serialPort1.ReadExisting();
if (rxString == "\b")
{
//Removes 1 character when backspace is the key pressed
rxSerialTextBox.ReadOnly = false;
rxSerialTextBox.Select(rxSerialTextBox.TextLength - 1, rxSerialTextBox.TextLength);
rxSerialTextBox.SelectedText = String.Empty;
rxSerialTextBox.ReadOnly = true;
rxString = "";
}
while (rxString.Length != 0)
{
try
{
while (rxString.Length != 0)
{
if (rxString.IndexOf("\r\n") == 0)
{
//Adds a newline when newline is the next characters in the string
rxString = rxString.Substring(rxString.IndexOf("\r\n") + 2);
rxAllCommsTextBox.AppendText(Environment.NewLine);
rxSerialTextBox.AppendText(Environment.NewLine);
}
//Adds a new character to the text box
rxAllCommsTextBox.AppendText(rxString.Substring(0, 1));
rxSerialTextBox.AppendText(rxString.Substring(0, 1));
rxString = rxString.Substring(1, rxString.Length - 1);
}
}
catch
{
//rxString = "";
}
}
}

A quck look at the SerialPort.DataReceived event documentation brings into attention the following Remarks section paragraph:
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception. If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
According to this, your code that touches UI elements (text boxes) inside that event handler is incorrect. What the documentation doesn't say is that when you do so, the behavior is undefined - sometimes it may work, another time hang, yet another time throw exception.
So, instead of asking why your incorrect code sometimes work, you'd better concentrate on making it correct, and only then if something is not working, ask why and seek for a solution.
P.S. I'm not going to address how the concrete issue can be solved - there are a tons of posts, explanations and examples of how to marshal the calls to the UI thread, and in that regard there is nothing special in your case.

It was a little confusing your question ...
you can try, it is understood correctly, try to keep the value you want to assign the memory context, and assign the value based on another type of iteration.
At the moment the application freezes, which shows the breakpoint? The expected behavior does it work?
I would try a configuration object, such as a list, with various configurations, and certain state, the amount you need to pass the list to the tabcontrol. Obviously, it needs to check what's not working and why is not ...

Related

Delay between text messages in WPF

I am currently working on a user form and having issues getting any sort of delay to work as I intend it to. I have the program check through multiple text boxes and display a message in a listbox. The program sets the background color of the text box to match the color of the message. This all works great, but all the messages are displayed at once. I would like to pause between each message before displaying the next one. I have tried using a timer, which would wait prior to displaying the first message, but then would display all messages at once after the timer expired. I tried using a delay, but again the messages would all appear at once. I also tried sleep, but since this is all one thread it will sleep the entire thread and then wake and display all messages at once. No matter what I attempt the messages all display simultaneously.
The messages are being called from a void I created:
public void writeMessage(TextBox messageBox, string message, Color messageColor)
{
Brush colorBrush = new SolidColorBrush(messageColor);
messageBox.Background = colorBrush;
if (statusList.Items.Count == 0)
{
list = new List<listColor>();
list.Add(new listColor(message, new SolidColorBrush(messageColor)));
}
else
{
list = ((IEnumerable<listColor>)statusList.ItemsSource).ToList();
if (!list[list.Count - 1].StatusItem.Equals(message))
{
list.Add(new listColor(message, new SolidColorBrush(messageColor)));
}
}
statusList.ItemsSource = list;
statusList.Items.Refresh();
statusList.ScrollIntoView(statusList.Items[statusList.Items.Count - 1]);
}
On my button click I call the above void multiple times i.e.
writeMessage(textBox1, "Error!", Colors.Red);
writeMessage(textBox2, "Warning!", Colors.Blue);
I tried adding the timer and delay into the writeMessage void, but it would not space out the messages. Anyone have any ideas or tips on how I could add a 2 second pause in the writeMessage void such that each time it is called it will wait for 2 seconds before continuing?
It's not entirely clear what's going on in your user interface, but... if you're on .NET 4.5, you should just be able to do something like this:
writeMessage(textBox1, "Error!", Colors.Red);
await Task.Delay(1000);
writeMessage(textBox2, "Warning!", Colors.Blue);
The containing method will have to be marked async.
You may also benefit from following the MVVM pattern here. Consider looking into how to use bindings, observable collections, and data templates.

Force loop containing asynchronous task to maintain sequence

Something tells me this might be a stupid question and I have in fact approached my problem from the wrong direction, but here goes.
I have some code that loops through all the documents in a folder - The alphabetical order of these documents in each folder is important, this importance is also reflected in the order the documents are printed. Here is a simplified version:
var wordApp = new Microsoft.Office.Interop.Word.Application();
foreach (var file in Directory.EnumerateFiles(folder))
{
fileCounter++;
// Print file, referencing a previously instantiated word application object
wordApp.Documents.Open(...)
wordApp.PrintOut(...)
wordApp.ActiveDocument.Close(...)
}
It seems (and I could be wrong) that the PrintOut code is asynchronous, and the application sometimes gets into a situation where the documents get printed out of order. This is confirmed because if I step through, or place a long enough Sleep() call, the order of all the files is correct.
How should I prevent the next print task from starting before the previous one has finished?
I initially thought that I could use a lock(someObject){} until I remembered that they are only useful for preventing multiple threads accessing the same code block. This is all on the same thread.
There are some events I can wire into on the Microsoft.Office.Interop.Word.Application object: DocumentOpen, DocumentBeforeClose and DocumentBeforePrint
I have just thought that this might actually be a problem with the print queue not being able to accurately distinguish lots of documents that are added within the same second. This can't be the problem, can it?
As a side note, this loop is within the code called from the DoWork event of a BackgroundWorker object. I'm using this to prevent UI blocking and to feedback the progress of the process.
Your event-handling approach seems like a good one. Instead of using a loop, you could add a handler to the DocumentBeforeClose event, in which you would get the next file to print, send it to Word, and continue. Something like this:
List<...> m_files = Directory.EnumerateFiles(folder);
wordApp.DocumentBeforeClose += ProcessNextDocument;
...
void ProcessNextDocument(...)
{
File file = null;
lock(m_files)
{
if (m_files.Count > 0)
{
file = m_files[m_files.Count - 1];
m_files.RemoveAt(m_files.Count - 1);
}
else
{
// Done!
}
}
if (file != null)
{
PrintDocument(file);
}
}
void PrintDocument(File file)
{
wordApp.Document.Open(...);
wordApp.Document.PrintOut(...);
wordApp.ActiveDocument.Close(...);
}
The first parameter of Application.PrintOut specifies whether the printing should take place in the background or not. By setting it to false it will work synchronously.

Terminating a Form while COM DataReceived Event keeps fireing. C#

I'm currently having a problem, which seems to be related to closing a Form, while a scale, which is connected through a Serial Connection keeps sending data (about 3 packages per sek).
I handle new data over the DataReceived-Event (handling itself might be uninteresting for this issue, since I'm just matching data) Keep an eye on the COM_InUse variable and the allowFireDataReceived check.):
private void COMScale_DataReceived(object sender, EventArgs e)
{
if (allowFireDataReceived)
{
//set atomar state
COM_InUse = true;
//new scale:
if (Properties.Settings.Default.ScaleId == 1)
{
strLine = COMScale.ReadTo(((char)0x2).ToString());
//new scale:
Regex reg = new Regex(Constants.regexScale2);
Match m = reg.Match(strLine);
if (m.Success)
{
strGewicht = m.Groups[1].Value + m.Groups[2];
double dblComWeight;
double.TryParse(strGewicht, out dblComWeight);
dblScaleActiveWeight = dblComWeight / 10000;
//add comma separator and remove zeros
strGewicht = strGewicht.Substring(0, 1) + strGewicht.Substring(1, 2).TrimStart('0') + strGewicht.Substring(3);
strGewicht = strGewicht.Insert(strGewicht.Length - 4, ",");
//write to textbox
ThreadSafeSetActiveScaleText(strGewicht);
COMScale.DiscardInBuffer();
//MessageBox.Show(dblScaleActiveWeight.ToString(), "dblScaleActiveWeight");
}
}
//free atomar state
COM_InUse = false;
}
}
The COM_InUse variable is a global bool and "tells" if there is a current process of handling data.
The allowFireDataReceived is also a global bool and if set to false will lead to no extra handling of the data which has been sended.
My problem now is the following:
It seems that Eventhandling is a separate Thread, which leads to a deadlock on klicking the Cancel-Button since the COM_InUse will never turn to false, even if the Event was handled (see end of COMScale_DataReceived, where COM_InUse is set to false).
While setting allowFireDataReceived = false works perfectly (no handling any more), as I said: the while loop will not terminate.
private void bScaleCancel_Click(object sender, EventArgs e)
{
allowFireDataReceived = false;
while (COM_InUse)
{
;
}
if (!COM_InUse)
{
ret = 1;
SaveClose();
}
}
When I comment out the while-block I have to click twice on the button, but it works without a crash. Since this very user unfriendly, I'm searching for an alternative way to safely close the window.
Info:
Simply closing (without checking if the COM-Data was processed) lead to a fatal crash.
So, maybe someone can explain to me what exactly causes this problem or can provide a solution to this. (Maybe one would be to trigger the Cancel-Clicking Event again, but that is very ugly)
Greetings!
I count on you :)
//edit:
Here is the current code of
private void ThreadSafeSetActiveScaleText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.lScaleActive.InvokeRequired)
{
SafeActiveScaleTextCallback d = new SafeActiveScaleTextCallback(ThreadSafeSetActiveScaleText);
this.Invoke(d, new object[] { text });
}
else
{
this.lScaleActive.Text = text;
}
}
ThreadSafeSetActiveScaleText(strGewicht);
Yes, the DataReceived event runs on a threadpool thread. You already knew that, you wouldn't have called it "ThreadSafe" otherwise. What we can't see is what is inside this method. But given the outcome, it is highly likely that you are using Control.Invoke().
Which is going to cause deadlock when you loop on COM_InUse in code that runs on the UI thread. The Control.Invoke() method can only complete when the UI thread has executed the delegate target method. But the UI thread can only do that when it is idle, pumping the message loop and waiting for Windows messages. And invoke requests. It cannot do this while it looping inside the Click event handler. So Invoke() cannot complete. Which leaves the COM_InUse variable for ever set to true. Which leaves the Click event handler forever looping. Deadlock city.
The exact same problem occurs when you call the SerialPort.Close() method, the port can only be closed when all events have been processed.
You will need to fix this by using Control.BeginInvoke() instead. Make sure the data is still valid by the time the delegate target starts executing. Pass it as an argument for example, copying if necessary.
Closing the form while the scale is unrelentingly sending data is in general a problem. You'll get an exception when you invoke on a disposed form. To fix this, you'll need to implement the FormClosing event handler and set e.Cancel to true. And unsubscribe the DataReceived event and start a timer. Make the Interval a couple of seconds. When the timer Ticks, you can close the form again, now being sure that all data was drained and no more invokes can occur.
Also note that calling DiscardInBuffer() is only good to randomly lose data.

program stuck because of missing thrown events

This is the case:
A business has several sites, each site holds several cameras that take alt of pictures daily (about a thousand pictures each). These pictures are then stored in a folder (one folder for a day) on one computer.
The business own an image analyzing program that gets an "in.xml" file as input and returns an "out.xml" file, for analyzing of one picture. This program must used and cannot be changed.
I wrote a UI for that program that runs on that folder and processes each camera from each site, sending pic after pic to that program which runs as a separate process.
Because this processing is async I have used events at the start and end of every pic's handling, and the same for sites and cameras on sites.
The program run on that business greatly, but sometimes it gets stuck after handling a pic, like it has missed the end_pic_analizing event, and is still waiting for it to be thrown.
I tried putting a timer for every picture, that moves to the next pic in such cases, but it still got stuck again, acting like is was missing the timer event as well.
This bug happens too many times, even when running almost as single process at that computer, and has got stuck even at the start of the process (happened at the third picture once). this bug doesn't depend on specific pictures either, because it can be stuck at different pics or not be stuck at all, while running repeatedly on the same folder.
Code samples:
on the Image class:
static public void image_timer_Elapsed(object sender, ElapsedEventArgs e)
{
//stop timer and calculate the how much time left for next check for file.
_timer.Stop();
_timerCount += (int)_timer.Interval;
int timerSpan = (int)(Daily.duration * 1000) - _timerCount;
//daily.duration is the max duration for seekin the "out.xml" file before quiting.
if (timerSpan < _timer.Interval) _timer.Interval = timerSpan + 1;
//check for file and analize it.
String fileName = Daily.OutPath + #"\out.xml";
ResultHandler.ResultOut output = ResultHandler.GetResult(ref _currentImage);
//if no file found and there is time left wait and check again
if (output == ResultHandler.ResultOut.FileNotFound && timerSpan > 0)
{
_timer.Start();
}
else //file found or time left
{
if (MyImage.ImageCheckCompleted != null)
MyImage.ImageCheckCompleted(_currentImage); //throw event
// the program is probably got stuck here.
}
On camera class:
static public void Camera_ImageCheckCompleted(MyImage image)
{
//if this is not the last image. (parent as Camera )
if (image.Id + 1 < image.parent.imageList.Count)
{
image.parent.imageList[image.Id + 1].RunCheck(); //check next image
}
else
{
if (Camera.CameraCheckCompleted != null)
Camera.CameraCheckCompleted(image.parent); // throw event
}
}
You don't appear to have any error handling or logging code, so if an exception is thrown your program will halt and you might not have a record of what happened. This is especially true since your program is processing the images asynchronously, so the main thread may have already exited by the time an error occurs in one of your processing threads.
So first and foremost, I would suggest throwing a try/catch block around all the code that gets run in the separate thread. If an exception gets thrown there, you will want to catch that and either fire ImageCheckCompleted with some special event arguments to indicate there was an error or fire some other event that you create specifically for when errors occur. That way your program can continue to process even if an exception is thrown inside your code.
try
{
//... Do your processing
// This will happen if everything worked correctly.
InvokeImageCheckCompleted(new ImageCheckCompletedEventArgs();
}
catch (Exception e)
{
// This will happen if an exception got thrown.
InvokeImageCheckCompleted(new ImageCheckCompletedEventArgs(e);
}
For the sake of simplicity, I'd suggest using a for loop to process each image. You can use a ManualResetEvent to block execution until the ImageCheckCompleted event fires for each check. This should make it easier to log the execution of each loop, catch errors that may be preventing the ImageCheckCompleted event from firing, and even possibly move on to process the next image if one of them appears to be taking too long.
Finally, if you can make your image processing thread-safe, you might consider using Parallel.ForEach to make it so that multiple images can be processed at the same time. This will probably significantly improve the overall speed of processing the batch.

BackgroundWorker & Timer, reading only new lines of a log file?

My application writes a log file (currently using log4net). I'd like to setup a timer and a background worker to read the log file and print its content into some control in my form, while it's being written.
I can't use the FileSystemWatcher class because seems broken: sometimes the event "changed" fires, sometimes do not. And it has an extremely low "pooling rate".
So I created a Timer and a FileSystemWatcher. On the "tick" event of the timer, the background worker does its job.
The question is: how to read only the lines that are added since the last check of the worker?
public LogForm()
{
InitializeComponent();
logWatcherTimer.Start();
}
private void logWatcherTimer_Tick(object sender, EventArgs e)
{
FileInfo log = new FileInfo(#"C:\log.txt");
if(!logWorker.IsBusy) logWorker.RunWorkerAsync(log);
}
private void logWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Read only new lines since last check.
FileInfo log = (FileInfo) e.Argument;
// Here is the main question!
}
EDIT: Code Solution (maybe there is a more elegant way?):
private void logWatherWorker_DoWork(object sender, DoWorkEventArgs e)
{
// retval
string newLines = string.Empty;
FileInfo log = (FileInfo) e.Argument;
// Just skip if log file hasn't changed
if (lastLogLength == log.Length) return;
using (StreamReader stream = new StreamReader(log.FullName))
{
// Set the position to the last log size and read
// all the content added
stream.BaseStream.Position = lastLogLength;
newLines = stream.ReadToEnd();
}
// Keep track of the previuos log length
lastLogLength = log.Length;
// Assign the result back to the worker, to be
// consumed by the form
e.Result = newLines;
}
Check and store the file size each time you read the log, then start your text reader (or whatever you're using) at that location the next time you read.
You could keep track of the index of the last character read from the stream, and subsequently seek to that position.
Edit: see http://dotnetperls.com/seek for examples.
If all you want is to view you log file on a form as it is being written, why not do something simple like write your own Appender that is backed by a TextBox, RichTextBox, or whatever.
Here are some links that I found just doing a quick Google search for "log4net textbox appender":
http://www.nimblecoder.com/blog/archive/2009/01/30/using-a-delegate-and-custom-appender-with-log4net-to-display.aspx (This one looks pretty cool because it allows you to specify a delegate to execute on every log message, so you would not even be tied to a TextBox. You could write different delegates depending on where you wanted your log output to go).
http://www.l4ndash.com/Log4NetMailArchive%2Ftabid%2F70%2Fforumid%2F1%2Fpostid%2F15133%2Fview%2Ftopic%2FDefault.aspx
http://weblogs.asp.net/psteele/archive/2010/01/25/live-capture-of-log4net-logging.aspx
http://www.l4ndash.com/Log4NetMailArchive%2Ftabid%2F70%2Fforumid%2F1%2Fpostid%2F14923%2Fview%2Ftopic%2FDefault.aspx (This one is an Appender that raises an event for every message that is logged).
http://markmail.org/message/ma62bdjpmab3cn7y (relatively recent - posted in 2008 - uses RichTextBox to generated ColoredConsoleAppender-style output)
http://www.claassen.net/geek/blog/2005/06/log4net-scrollingtextbox.html (This one uses the MemoryAppender to capture the log messages and then writes those messages to a TextBox)
http://code.google.com/p/devdefined-tools/source/browse/trunk/projects/common/DevDefined.Common/Appenders/TextBoxAppender.cs?r=90
I have not tried any of these, so I can't vouch for their quality. But, I think that the approach of using a custom Appender backed by a TextBox seems like a much better approach than trying to watch the log file, read it, and then put the messages in a TextBox.
Some common themes that I noticed while looking briefly over these Appenders:
When you write to the TextBox from the Appender, you might need to use BeginInvoke.
One tricky part seems to be telling the Appender which TextBox to write to. In most cases, the Appender is configured via the config file and then the TextBox is added to the Appender programmatically AFTER the logging system has been initialized (I think you have to either retrieve at least one logger or log at least one message to force all of the lazy initialization to happen).
Be careful about constantly adding lines to the TextBox. You could use up a lot of memory, cause performance issues, or exceed the limit on the TextBox (if there is one). Several of these Appenders include code that removes "old" lines from the TextBox periodically.

Categories

Resources