I am building a WPF application that checks whether a file has been modified within a certain period of time and if so, the application exits.
I am having an issue that the method that ultimately triggers the call of App.Current.Shutdown() is an event handler attached to a FileSystemWatcher event and causes a TaskCanceledException to be thrown.
A minimal working example is as follows:
Create a new WPF application. In App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
FileSystemWatcher w = new FileSystemWatcher();
w.Path = /*path to suitable test folder*/
w.Filter = "Test.txt";
w.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
w.Changed += (s, a) =>
{
//Shutdown(); --Throws InvalidOperationException (Calling thread cannot access this object)
Application.Current.Dispatcher.Invoke(() => Shutdown());
};
w.EnableRaisingEvents = true;
}
}
Edit the file Test.txt and the application should close.
In order to prevent the exception showing, I can surround the shutdown call with a empty try-catch block
try
{
Application.Current.Dispatcher.Invoke(() => Shutdown())
}
catch(TaskCanceledException)
{
}
but this seems to be a hack and doesn't solve the underlying issue.
It seems to me that C# is trying to tell me I am going about this the wrong way. Is there a better way?
There is a special method in the dispatcher to invoke a shutdown: InvokeShutdown
Calling Application.Current.Dispatcher.InvokeShutdown(); from the Changed event handler avoids the exception.
This shuts down the dispatcher, which in turn leads to the shutdown of the application. There's some discussion on the difference between this and Application.Shutdown here.
Related
First of all my Main is STAThread and i am not able to change this without facing problems with the rest of my code.
So, I am currently using Rapi2 To pull and push files between my Pda and Computer. Now since there is quite a bit of number crunching i would like to do this on a separate thread. First wat i do is create an RemoteDeviceManager and then make an Event Handler for when a device connects.
public void Initialize()
{
_deviceManager = new RemoteDeviceManager();
_deviceManager.DeviceConnected += DeviceConnected;
}
As you can see when my device connects it triggers DeviceConnected.
This is the class that i end up pulling and pushing a database and do some number work.
private void DeviceConnected(object sender, RemoteDeviceConnectEventArgs e)
{
if (e.Device == null) return;
... (unimportant code)
}
Now the problem here is that i would want to run the code inside DeviceConnected in a new thread but i am unable to access e inside the new thread since it was initialized outside that thread
So now wat i tried was make a new thread before calling Initialize.
public Watcher()
{
_dataThread = new Thread(Initialize);
_dataThread.IsBackground = true;
_dataThread.Name = "Data Thread";
_dataThread.SetApartmentState(ApartmentState.MTA);
_dataThread.Start();
}
But the thread dies and thus never fires my event handler.
I tried many different ways to make it work or keep my thread alive but without any success. I hope someone here is able to give me some hints.
i writed a program with Serial Port as this:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialLabel.BackColor = Color.Red;
storage = serialPort1.ReadExisting();
if (storage.Contains("CMGL"))
{
if (storage.Length > 65)
{
processUnreadedMessages(storage);
}
else
{
Thread.Sleep(500);
}
}
else if (storage.Contains("CMTI"))
{
serialPort1.Write("AT+CMGL\r");
Thread.Sleep(500);
}
storage = "";
serialLabel.BackColor = Color.Lime;
}
in visual studio when i run program works good!
but when i'l create setup for my program or run exe file, data don't receive to serialPort, and i don't get any error. but when i send data with this program it's work!
can you help Me?
the problematic lines are probably those:
serialLabel.BackColor = Color.Red;
and
serialLabel.BackColor = Color.Lime;
there's a slim chance it will still work in developent environment, but:
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.
[msdn]
try instead:
serialLabel.Invoke(new EventHandler(delegate
{
serialLabel.BackColor = Color.Red;
}));
Also:
make sure you don't touch GUI or anything that should be accessed from the thread it was created on without invoking (e.g. you also shouldn't write data to EventLog without Invoking) in your method processUnreadedMessages()
Check if there's no First Chance exceptions when you debug your application
check Application EventLog for messages generated by your application.
log data you received in serialPort1_DataReceived event to a file before you do anything else (this will check if DataReceived event is raised at all when it should)
subscribe to SerialPort.ErrorReceived event
Please excuse me if this is redundant, however all the questions related to this seem to point in different directions, also I am new to multithreaded programming.
I have a FileSystemWatcher class in my code, which watches the created event. It looks like a created event of file system watcher starts it's own thread. So sometimes the calling thread continues it's work before the work initiated in called thread of FileSystemWatcher created event finishes. I don't want this. My workflow needs to be single-threaded, so what I want to achieve is wait for for created event to finish it's work before the calling thread gets an opportunity to resume.
pesudo code:
main() {
FileSystemWatcher fsw = new FileSystemWatcher()
fsw.Path = ini.location;
fsw.Created += new FileSystemEventHandler(OnFileCreation);
fsw.EnableRaisingEvents = true;
main_engine.processDataToFile();
main_engine.processCreatedFile();
}
void OnFileCreation(object sender, FileSystemEventArgs e) {
// do some file processing
// takes time based on size of file and whether file is locked or not etc.
}
void processDataToFile() {
// do some data processing on received data and output to a file.
}
void processCreatedFile() {
// do not want this method to be called, unless OnFileCreation() finish it's work.
}
The reason choose to use FileSystemWatcher was because sometimes files are directly placed for processing instead of main_engine getting the data first and it works on multiple locations, so did not want to roll out a homegrown solution when FileSystemWatcher was available.
If the event fires in the separate thread you cant make it single-threaded. because this is not your code. end of story.
however it is quite simple to wait on:
...
me.WaitOne();
main_engine.processCreatedFile();
}
...
void OnFileCreation(object sender, FileSystemEventArgs e) {
// do some file processing
// takes time based on size of file and whether file is locked or not etc.
...
me.Set();
}
ManualResetEventSlim me = new ManualResetEventSlim(false);
Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.
I have:
using System.IO;
using System.Windows.Forms;
namespace myNamespace1
{
public partial class Form1 : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
public Form1()
{
InitializeComponent();
watcher.Path = #"c:\users\Me\desktop\z";
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.EnableRaisingEvents = true;
}
void watcher_Created(object sender, FileSystemEventArgs e)
{
Text = e.Name + " " + e.ChangeType.ToString();
}
}
}
When I add a folder or file to the folder (-z) – the program closes. Why?
I'm running in Debug mode. And I don't get any exception from VS.
EDIT:
The answer:
jon-skeet's answer
+(in a comment)
In Visual Studio, can you go to the Debug Menu -> Exceptions. On this dialog, make sure that next to Common Language Runtime Exceptions, make sure both 'Thrown' and 'User Unhandled' are ticked, click OK and try debugging again. – dash
Assuming Text is trying to change a UI property, you're changing the UI from the wrong thread. FileSystemWatcher raises events on thread-pool threads, but you're only meant to access the UI from the UI thread. That's probably throwing an exception in the thread-pool thread, which is bringing down the process.
Try this instead:
void watcher_Created(object sender, FileSystemEventArgs e)
{
Action action = () => Text = e.Name + " " + e.ChangeType;
// Or Dispatcher.Invoke - it depends on your application type
Invoke(action);
}
Without knowing the details of what kind of program you created, I'm guessing you have an unhandled exception occurring somewhere in your code that is causing your application to close.
Update
After looking at your edit, it looks like Jon is correct (as usual). Your application is trying to update the Text property on the wrong thread. Your handler should really be:
Action a = () => e.Name + " " + e.ChangeType.ToString();
Invoke(a);
In addition to Justin's answer, you can only specifiy a directory for the Path property.
To monitor changes on a specific file, use the Filter property.
Also try adding some exception handling or stepping through the code in debug mode to see what is happening. Pay close attention to how you are setting the label, although I'd expect to see a cross thread exception.
Are you seeing any exceptions at all?
What's Text, and what's done with it when the event fires?
Bear in mind the FileSystemWatcher creates its own thread from the threadpool to handle events. Also, there is a good deal of unmanaged code under the hood of this class, which can result in unhandled exceptions being silently swallowed if you have "Just My Code" turned on in the debugger options.
You may want to create a producer/consumer model to handle this -- see After FileSystemWatcher fires - Thread Pool or Dedicated thread? for more details.
Is it a windows service?
Generally, if the file watcher couldnt find the folder or has no access to a folder, it dies.
Did u check ur EventLog?