I have a small application that watches a specific file and whenever it changes, my application should do the actions in the loop, but something is firing the function more than once!! here's my code
private void OnChanged(object source, FileSystemEventArgs e)
{
if (e.FullPath == #"C:\test.txt")
{
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//actions here
}
}
}
catch (Exception)
{
}
}
}
So I'm guessing in the loop when streamreader do File.OpenText somehow is firing the function again?! any ideas?
From MSDN:
The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, ...
So yes, opening (actually: closing) the file will raise the Changed event again.
You can use the NotifyFilter to limit the actions your watcher triggers on.
SOLUTION
So I did one small thing that controlled the issue, I added a counter and always check if it's not the first time, skip and reassign it to 0.
private int fireCounter = 0;
private void OnChanged(object source, FileSystemEventArgs e)
{
fireCounter++;
if (fireCounter == 1)
{
delete();
if (e.FullPath == #"C:\test.txt")
{
Thread.Sleep(2000);
//I added Sleep for two seconds because without it sometimes it wont work
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//Actions Here
}
}
}
catch (Exception)
{
}
}
}
else
{
fireCounter = 0;
}
}
Related
The BackgroundWorker's progressbar is not updated while doing some tasks. What I would like to reach is progressbar moving while iterating through each file in DirectoryInfo. Suppose we have 20 files of ".sql" while first file completed it should be 5%, 10% and etc.
Here is my code.
private void CSV_Click(object sender, RoutedEventArgs e)
{
try
{
btnExtract.IsEnabled = false;
workerextract.RunWorkerAsync();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
DirectoryInfo di = new DirectoryInfo(txtQueryfolder.Text);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
});
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBarExtract.Value = e.ProgressPercentage;
}
private void workerextract_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnExtract.IsEnabled = true;
System.Windows.MessageBox.Show("CSV Data extraction finished!");
}
I found that
private void workerextract_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
is called once at the end when 100%.
Also,
private void workerextract_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
never called as I do not see Message Box at the end.
So, I think I am doing something wrong here, could you please direct me on right way?
The problem was in wrapping whole DoWork inside Dispatcher.Invoke.
I need to wrap only those code where it is interacting with UI.
So I changed the code appropriately and it works.
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
di = new DirectoryInfo(txtQueryfolder.Text);
});
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
this.Dispatcher.Invoke(() =>
{
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
});
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
Thanks to all for showing the direction.
Using this.Dispatcher.Invoke in the BackgroundWorker's DoWork event you are executing the whole operation in the UI thread; which is what BackgroundWorker born to avoid to do.
Also, you get an error unwrapping your code from the dispatcher because you are accessing an UI object, which is txtQueryfolder.
Just use:
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string queryFolder = e.Argument.ToString();
try
{
DirectoryInfo di = new DirectoryInfo(queryFolder);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
// ExtractToCSV shouldn't access to a UI object.
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch (Exception ex)
{
// Don't use MessageBox in a thread different from the UI one. Just set the result (e.Result) and get that in the RunWorkerCompleted event.
// System.Windows.MessageBox.Show(ex.Message);
}
}
When you call the RunWorkerAsync method just add the parameter like below:
workerextrac.RunWorkerAsync(txtQueryfolder.Text);
I recently attempted to thread a foreach loop because it was freezing the UI when it ran. This loop looks through a bunch of log files and retrieves information from them line by line, then adds it to a listView(also prints something to a richtextBox). The issue I'm having is that after threading this loop, it seems that neither the listView nor the richTextBox update anymore. I know for a fact that the method is being called, because when I put a MessageBox after that 'if statement', it opens just fine but everything else refuses to work.
public void searchForAll(object sender, DoWorkEventArgs e)
{
if (//argument)
{
listViewEx1.Items.Clear();
int logcount = 0;
richTextBoxEx1.Text += "Print something";
richTextBoxEx1.Text += "\n";
richTextBoxEx1.SelectionStart = richTextBoxEx1.Text.Length;
richTextBoxEx1.ScrollToCaret();
foreach (User user in s.Friends)
{
foreach (string log in Directory.GetFiles(path, "*.log"))
{
string[] fileToRead = File.ReadAllLines(log);
foreach (string line in fileToRead)
{
if (line.Contains(user.Handle) && line.Contains("-r"))
{
if (!isDuplicate(c))
{
listViewEx1.Items.Add(new ListViewItem(user.Handle)
{
SubItems = { c }
});
dupCheck.Add(c);
logcount++;
}
}
}
}
dupCheck.Clear();
Thread.Sleep(1000);
}
richTextBoxEx1.Text += "Print something";
}
}
}
It isn't recommended to directly change parts of the UI from within a BackgroundWorker.DoWork event, instead I recommend using BackgroundWorker.ReportProgress and editing the UI inside THAT method.
Here is an example of how I would implement changing a textbox's value to 'x':
public void searchForAll(object sender, DoWorkEventArgs e)
{
//Define background worker
var MyBack = (BackgroundWorker)sender;
for(...)
{
//Send some data to ReportProgress
MyBack.ReportProgress(0, "any object of any form goes here");
}
}
Then in the report progress method:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
textBox1.Text = e.UserState.ToString();
}
Please note that you have to set Backgroundworker1.WorkerReportsProgress = true; otherwise the worker won't be able to report the progress.
I'm creating a memory game (matching game) and I'm trying to save the time the player gets when he/she has matched all the tiles. My streamwriter does work, and I'm curios why. Here's my code for the save method and the method that find if all tiles are matched:
private void Save(string time)
{
StreamWriter write = new StreamWriter(path, true);
write.WriteLine(time);
write.Close();
}
private void CheckForWinner()
{
foreach (Control control in tableLayoutPanel1.Controls)
{
Label iconLabel = control as Label;
{
if(iconLabel != null)
{
if (iconLabel.ForeColor == iconLabel.BackColor)
{
return;
}
}
}
}
MessageBox.Show("You finished the game, your time was: " + timeLabel.Text);
Save();
//Close(); is outcommented because I want to see if it works.
}
You don't seem to be specifying the parameter time when calling Save.
Add the timeLabel.Text to your function call.
EDIT: A good practice for using StreamWriters is to make use of the using command that is available. Since StreamWriter is Disposable you can wrap the usage of it inside a using and not have to worry about closing it. See the updated Save function.
private void Save(string time)
{
using(StreamWriter write = new StreamWriter(path, true)){
write.WriteLine(time);
}
}
private void CheckForWinner()
{
foreach (Control control in tableLayoutPanel1.Controls)
{
Label iconLabel = control as Label;
{
if(iconLabel != null)
{
if (iconLabel.ForeColor == iconLabel.BackColor)
{
return;
}
}
}
}
MessageBox.Show("You finished the game, your time was: " + timeLabel.Text);
Save(timeLabel.Text);
//Close(); is outcommented because I want to see if it works.
}
Save should looks like:
private void Save(string time)
{
File.WriteAllText(path, time); // Or AppendAllText, depends on what you want.
}
And inside CheckForWinner you have to call not Save() but Save(timeLabel.Text).
When the code is working so laggy it would be very good so that it is not laggy when it works.
How the code works:
It searches the computer for a file is then when it find it to change the file but if the file is running line will loop until it manages to do its job.
Main class
public Form1(string[] Args)
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000); // One second.Thread.Sleep(1000); // One second.
MessageBox.Show("Testing");
Fille mc = new Fille();
mc.Search();
}
Fille clss
private static ArrayList list2 = new ArrayList();
private static ArrayList listRemove = new ArrayList();
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", file);
}
catch (IOException)
{
// log errors
if (list2.Count == 0)
{
list2.Add(file);
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Abort();
list2.Add(file);
thread.Join();
}
}
}
}
}
catch (Exception ex)
{
// log errors
}
}
private void Test2()
{
if (list2.Count == 0)
{
}
else
{
foreach (string _item in list2)
{
try
{
//Run
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", _item);
listRemove.Add(_item);
}
catch (IOException)
{
//error
}
}
foreach (var Remove in listRemove)
{
list2.Remove(Remove);
}
listRemove.Clear();
if (list2.Count == 0)
{
}
else
{
Thread thread = new Thread(new ThreadStart(Test2));
thread.Start();
}
}
}
I made a new thread because I found the problem. But now it's just that it lags.
I suspect the reason it's "lagging" is because you have the system in a very convoluted but rather processor intensive and I/O intensive loop. If a file fails the first test, your code starts a thread that tries it again. And if that fails then you start another thread to try it again, lather, rinse, repeat.
That's going to absolutely kill performance. You're basically doing this:
while (forever)
{
if I can overwrite the file
{
break;
}
}
Except if you have multiple files that you're trying to write, then you're doing that loop for every file. Concurrently. And you're not just using a loop. Instead, you're starting and stopping threads like nobody's business.
Yeah, that's going to slow down your computer.
A more reasonable way to do this would be with a thread to do the first check, and a timer that will limit how often you do the other checks. Communication is with a simple queue, because only one thread will be accessing it at a time.
Here's what I would recommend:
private static Queue<string> filesToCheck = new Queue<string>();
private System.Timers.Timer copyTimer;
public void Search()
{
try
{
foreach (string file in Directory.EnumerateFiles(#"C:\Users\user\Downloads\MCFILE\trrtrt\", "*.exe", SearchOption.AllDirectories))
{
// Display file path.
if (SHA1Hash.GetSHA1Hash(file) == "1233456") // fake SHA1Hash
{
if (!TryToCopy(file)) // See function below
{
filesToCheck.Enqueue(file);
}
}
}
// Checked all the files once.
// If there are any in the queue, start the timer.
if (filesToCheck.Count > 0)
{
copyTimer = new System.Timers.Timer(CopyTimerProc, null, 1000, Timeout.Infinite);
}
}
catch (Exception)
{
// do your error handling
}
}
private void CopyTimerProc(object state)
{
string filename = filesToCheck.Dequeue();
if (TryToCopy(filename))
{
// success. If the queue is empty, kill the timer.
if (filesToCheck.Count == 0)
{
copyTimer.Dispose();
}
}
else
{
// File still locked.
// Put it back on the queue and reset the timer.
filesToCheck.Enqueue(filename);
copyTimer.Change(1000, 0);
}
}
private bool TryToCopy(string filename)
{
try
{
COPYWithReplace(#"C:\Users\user\Downloads\MCFILE\Fake2\Test.exe", filename);
return true;
}
catch (IOException)
{
// log error
return false;
}
}
The timer is a one-shot that is reset after each time it ticks. The reason I did it this way is to prevent another tick coming along while the previous tick is still processing. After all, it takes time to copy a file.
There's no reason to do this with a bunch of threads. The file system can only do one thing at a time, anyway, and it's not like an extra second or two while you wait for a file to become available is going to hurt anything.
I have a small problem, which might me a stupid mistake on my side.
Here is my code to create a zipfile when needed and the method to add a file to the archive.
Adding a file works without problem but for some reason the Event is not fired after saving.
I set a breakpoint at zipFile_SaveProgress, the event is not fired.
class CoverArchive
{
private ZipFile zipFile;
private String coverArchivePath;
public CoverArchive()
{
coverArchivePath = "Archive\\Covers";
if (!File.Exists("Archive\\Covers"))
{
CreateZipFile();
}
using (zipFile = ZipFile.Read(coverArchivePath))
{
//zipFile.AddProgress += zipFile_AddProgress;
//zipFile.ExtractProgress += zipFile_ExtractProgress;
//zipFile.ZipError += zipFile_ZipError;
zipFile.SaveProgress += zipFile_SaveProgress;
}
}
private void CreateZipFile()
{
zipFile = new ZipFile();
zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zipFile.UseUnicodeAsNecessary = true;
if (!Directory.Exists("Archive"))
Directory.CreateDirectory("Archive");
zipFile.Save(coverArchivePath);
}
public void AddCover(List<String> directories, String coverName, Stream fileStream)
{
try
{
using (zipFile = ZipFile.Read(coverArchivePath))
{
String filePath = createPath(directories, coverName);
ZipEntry entry = zipFile.AddEntry(filePath, fileStream);
zipFile.Save();
}
}
catch (Exception ex)
{
Console.WriteLine("Error adding File" + ex);
}
}
private void zipFile_SaveProgress(Object sender, SaveProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Saving_Completed)
Console.WriteLine("Save Done");
}
}
Thanks a lot in advance :)
You're adding the event handling after calling Save().
Therefore, when it raised the SaveProgress event, there weren't any handlers.