Threads only run once in FileSystemWatcher changed - c#

Each time a file is changed some forms need to be created but the FileSystemWatcher_Changed method only works once and I don't know why.
First time a txt is changed it work correctly but if i make a second change the FileSystemWatcher_Changed method is not called again.
private void button1_Click(object sender, EventArgs e)
{
RunFileSystemWatcher();
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public void RunFileSystemWatcher()
{
FileSystemWatcher fsw = new FileSystemWatcher();
fsw.Path = "C:/";
fsw.NotifyFilter = NotifyFilters.LastAccess;
fsw.NotifyFilter = NotifyFilters.LastWrite;
//fsw.Created += FileSystemWatcher_Created;
fsw.Changed += FileSystemWatcher_Changed;
fsw.Filter = "*.txt";
fsw.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// ThreadStart delegado = new ThreadStart(showForms);
Thread hilo = new Thread(()=> showForms(e));
hilo.Start();
}
private void showForms(FileSystemEventArgs e)
{ //leer fichero
foreach (DataRow dr in r.asientosByTxt(e.FullPath).Rows)
{
//MessageBox.Show(dr[1].ToString());
Thread hilo2 = new Thread(() => Formsss(new Form2());
hilo2.Start();
}
}
private void Formsss(Form2 jugador)
{
Application.Run(jugador);
}

Related

Background worker not starting

I've implemented a background worker onLoad of my window. The code in the Progress_Load is reached but after that the DoWork function is not called. The function excel.Read() reads a quite big excel tabel into a list, this takes about 1.5 min and that's why i want to do it a-syn.
public List<Part> partList = new List<Part>() { };
//boolean that will be set when the backgroundworker is done
public bool listRead = false;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_Excel excel = new _Excel();
partList = excel.Read();
backgroundWorker1.ReportProgress(100);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Message m;
if (e.Cancelled == true)
{
m = new Message("The operation has been canceld!", "Canceled");
this.Close();
}
else if (e.Error != null)
{
Error er = new Error("Error: " + e.Error.Message, "Error!");
this.Close();
}
else
{
listRead = true;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
//Set the text.
this.Text = e.ProgressPercentage.ToString();
}
private void Progress_Load(object sender, EventArgs e)
{
if (backgroundWorker1 == null)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync();
}
Probably it's not null when loading the form. You may have added the BackgroundWorker via the designer. If so it's never null, you can hook up the event handlers also from its Properties/Events.
Try this
private void Progress_Load(object sender, EventArgs e)
{
if (backgroundWorker1 == null)
{
backgroundWorker1 = new BackgroundWorker();
}
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync();
}
In WPF Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); is a another option because report progress is not much useful in your scenario. Here example for Dispatcher and Background worker comparison.

Read file when updated. Error The process cannot access the file because it is being used by another process

I'm want to read the last line of a CSV file when updated. When i save the updates the app brakes with this error "The process cannot access the file '' because it is being used by another process." My question is, the "another process" is the watcher? If it's the watcher how can i read the file when updated?
public partial class Auto_Window : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
private int i = 0;
public Auto_Window()
{
InitializeComponent();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
i++;
var data = File.ReadAllLines(e.FullPath);
string last = data[data.Length - 1];
if (i == 1)
{
tb_art1.Invoke(new Action(() => tb_art1.Text = last));
}
if (i == 2)
{
tb_art2.Invoke(new Action(() => tb_art2.Text = "ART2"));
}
if (i == 3)
{
tb_art3.Invoke(new Action(() => tb_art3.Text = "ARTO3"));
MessageBox.Show("com?");
tb_art1.Invoke(new Action(() => tb_art1.Text = ""));
tb_art2.Invoke(new Action(() => tb_art2.Text = ""));
tb_art3.Invoke(new Action(() => tb_art3.Text = ""));
i = 0;
}
}
private void btn_auto_Click(object sender, EventArgs e)
{
this.Close();
}
private void btn_conf_Click(object sender, EventArgs e)
{
if ((Control.ModifierKeys & Keys.Shift) != 0 && (Control.ModifierKeys & Keys.Control) != 0)
{
Config cfgform = new Config();
cfgform.ShowDialog();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
}
}
private void btn_clear_Click(object sender, EventArgs e)
{
tb_art1.Text = "";
tb_art2.Text = "";
tb_art3.Text = "";
i = 0;
}
}
Update: Code updated
I solved the problem adding watcher.EnableRaisingEvents = false; at the start of the event and watcher.EnableRaisingEvents = true; at the end to prevent duplicated modifications ins seconds.
The other problem was the way i was reading the file. I solved in this way:
var fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var reader = new StreamReader(fs);
await Task.Delay(500);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
searchList.Add(line);
}

Background Worker In Winforms Not Updating TextBox

I am attempting to use a Background Worker in order to add a Cancel button to my winform.
I have added the below syntax, but the textbox is never updated with any progress as it was before I tried to implement the background worker.
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void btnQuery_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Iterating the array
foreach (string s in sxc)
{
txt_ProgressDetails.Visible = true;
AppendTextBoxLine("Getting Data For " + s);
//Put data into DataTable
PopulateDT(s);
}
//If the cancel button was pressed
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
private void AppendTextBoxLine(string statusMessage)
{
if (txt_ProgressDetails.Text.Length > 0)
txt_ProgressDetails.AppendText(Environment.NewLine);
txt_ProgressDetails.AppendText(statusMessage);
}
The Do_Work event of BackgroundWorker runs in a non-STA thread and you can't update UI from there. You can rewrite your code to update UI elements from the thread that created them with InvokeRequred and Invoke method.
Add this to your Form:
delegate void UpdateUICallback(string statusMessage);
And Change your AppendTextBoxLine method to this:
if (InvokeRequired)
{
UpdateUICallback d = new UpdateUICallback(AppendTextBoxLine);
this.Invoke(d, new object[] {statusMessage});
}
else
{
if (txt_ProgressDetails.Text.Length > 0)
txt_ProgressDetails.AppendText(Environment.NewLine);
txt_ProgressDetails.AppendText(statusMessage);
}
so your code will look like this:
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void btnQuery_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Iterating the array
foreach (string s in sxc)
{
txt_ProgressDetails.Visible = true;
AppendTextBoxLine("Getting Data For " + s);
//Put data into DataTable
PopulateDT(s);
}
//If the cancel button was pressed
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
delegate void UpdateUICallback(string statusMessage);
private void AppendTextBoxLine(string statusMessage)
{
if (InvokeRequired)
{
UpdateUICallback d = new UpdateUICallback(AppendTextBoxLine);
this.Invoke(d, new object[] {statusMessage});
}
else
{
if (txt_ProgressDetails.Text.Length > 0)
txt_ProgressDetails.AppendText(Environment.NewLine);
txt_ProgressDetails.AppendText(statusMessage);
}
}

Exit File Watcher When a file is created

I am using a file watcher. And when a file is created the file watcher should stop until it is called again. I have got the following code which is watching and the loop doesn't stop.
public void startwatch()
{
string path = "C:\\testwatcher\\";
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.EnableRaisingEvents = true;
watcher.IncludeSubdirectories = false;
watcher.Filter = "*.*";
watcher.Created += watcher_Created;
while (true) ;
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
string FileName = ("C:\\testfilescrated\\");
string text = File.ReadAllText(e.FullPath);
File.WriteAllText(FileName, text);
}
What i want to do is while loop should end when a file is created.
Don't use a busy loop, this will unnecessarily eat CPU. You could use a wait handle, for example using a ManualResetEvent's WaitOne() method:
ManualResetEvent _resetEvent;
public void StartWatch()
{
// Start watching...
// Wait until signaled.
_resetEvent = new ManualResetEvent(false);
_resetEvent.WaitOne();
}
void watcher_Created(object sender, FileSystemEventArgs e)
{
// Handle your file...
// And signal.
_resetEvent.Set();
}

cross-thread calls?

This is mt first time trying to write a not web based program, and my first time writing anything in C#.
I need a program that monitors folders, but I can't get it to work.
I have used the example from this post Using FileSystemWatcher with multiple files but is trying to make it a form.
My current problem comes in the ProcessQueue function where fileList apparently is defined in another thread.
Whenever a file is actually submitted to the watched folder I get an error that using fileList is a cross thread call
Can anyone explain this error to me, and how to fix it?
namespace matasWatch
{
public partial class Form1 : Form
{
private int n = 1;
private bool isWatching = false;
private List<string> filePaths;
private System.Timers.Timer processTimer;
private string watchedPath;
private FileSystemWatcher watcher;
public Form1()
{
filePaths = new List<string>();
watchedPath = "C:\\Users\\username\\Desktop\\test";
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!isWatching)
{
button1.Text = "Stop";
isWatching = true;
watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += Watcher_FileCreated;
watcher.Error += Watcher_Error;
watcher.Path = watchedPath;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
else {
button1.Text = "Watch";
isWatching = false;
watcher.EnableRaisingEvents = false;
watcher.Dispose();
watcher = null;
}
}
private void Watcher_Error(object sender, ErrorEventArgs e)
{
// Watcher crashed. Re-init.
isWatching = false;
button1_Click(sender, e);
}
private void Watcher_FileCreated(object sender, FileSystemEventArgs e)
{
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new System.Timers.Timer(2000);
processTimer.Elapsed += ProcessQueue;
processTimer.Start();
}
else{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
private void ProcessQueue(object sender, ElapsedEventArgs args)
{
try
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
}
}
}
}
}
I assume that fileList is a windows forms control. The ProcessQueue method is called from a timer thread which is by default a background thread. The fileList control resides in the UI thread. You need to use the Invoke() method of the form passing it in a delegate the updates the fileList control.
Invoke(new Action(() =>
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}));
Try using System.Windows.Forms.Timer instead of System.Timers.Timer so the timer tick event is executed on the UI thread.
See here for more details.

Categories