I am trying to continuosly watch a log file that is being deleted and rewritten every it gets updated.
My current approach was to use the FileSystemWatcher. This works great when modifying the file but if I delete the file and make a new one with the same name it stops tracking it.
My current approach:
namespace LogReader
{
class Program
{
static void Main(string[] args)
{
Watch();
while (true)
{
}
}
public static void Watch()
{
var watch = new FileSystemWatcher();
watch.Path = #"C:\TEMP\test";
watch.Filter = "test.txt";
watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
watch.Changed += new FileSystemEventHandler(OnChanged);
watch.EnableRaisingEvents = true;
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
if (e.FullPath == #"C:\TEMP\test\test.txt")
{
Console.Clear();
Stream stream = File.Open(#"C:\TEMP\test\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader streamReader = new StreamReader(stream);
var lines = streamReader.ReadToEnd();
Console.Out.WriteLine(lines);
streamReader.Close();
stream.Close();
}
}
}
}
This is because Create and Delete operations will not Trigger OnChanged event of FileSystemWatcher. So you need to register those events and assign the same event handler OnChanged It will be like the following:
watch.Created += new FileSystemEventHandler(OnChanged);
watch.Deleted += new FileSystemEventHandler(OnChanged);
You can go through This For more information regarding FileSystemWatcher.
I was only watching for file changes not file creations. Changing the watch function to this fixed it.
public static void Watch()
{
var watch = new FileSystemWatcher();
watch.Path = #"C:\TEMP\test";
watch.Filter = "test.txt";
watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.CreationTime; //more options
watch.Created += new FileSystemEventHandler(OnChanged);
watch.Changed += new FileSystemEventHandler(OnChanged);
watch.EnableRaisingEvents = true;
}
Related
I have been using fileSystemWatcher for c# to track when a zip file has been downloaded, the problem is that the name of the file never gets output to result.Name. Why is this??
public ResultsDetails ExportCallsToCsv()
{
var downloadsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + #"\Downloads";
ExportToCsvButton.Click();
using (var watcher = new FileSystemWatcher(downloadsDirectory))
{
watcher.Filter = "*.zip";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
CsvFilePath = $#"{downloadsDirectory}\{result.Name}".Replace("_", "-");
}
return new ResultsDetails(ActionResult.Passed, "Csv file created succesfully.");
}
Why does result.Name never get filled? I've attempted to put an endless while loop inside the using block which breaks when result.Name gets filled, but to still no avail. It just halts forever when I do that.
FileSystemWatcher.WaitForChanged is a synchronous/blocking method and is unable to capture the file change that was triggered on the same thread.
Here's an example of your code in a small test program. This will not work (as you pointed out).
static void Main(string[] args)
{
var directory = "C:\\test";
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
using (var watcher = new FileSystemWatcher(directory))
{
watcher.Filter = "*.txt";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
if (result.Name is null)
{
// This is always printed.
Console.WriteLine("No name");
}
}
Console.ReadLine();
}
Here's a small sample program that does work. This version handles the FileSystemWatcher events asynchronously.
static void Main(string[] args)
{
var directory = "C:\\test";
using (var watcher = new FileSystemWatcher(directory, "*.txt"))
{
watcher.EnableRaisingEvents = true; // *** Be sure to include this!
watcher.Created += Watcher_Created; // *** This is different, too.
File.Delete("C:\\test\\new_file.txt");
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
}
Console.WriteLine("All done.");
Console.ReadLine();
}
private static void Watcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Detected {e.Name} is {e.ChangeType}.");
}
I'm working on a project where i have to observe a directory. The files will be deployed in the directory at the same time. This means there could be 7000 files that will be moved to the directory at once. I'm using the FileSystemWatcher to trigger a thread if a new file ist added to the directory. If I'm moving a small amount of files (1-150 Files, 20 KB each) there are the right amount of threads starting. For each file one thread. As soon as I paste in a larger amount of these files, it's showing that there were more threads started than the directory contains. As you can see, I'm printing out "test" and a counter for each thread started. In the end, the counter is a higher number than the amount of pasted files. The more files I paste, the bigger is the difference between counter and pasted files. Hope you can tell me what I'm doing wrong.
public static void Main(string[] args)
{
Queue queue = new Queue();
queue.Watch();
while (true)
{
}
}
public void Watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "directorypath\\";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.*";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
public void OnChanged(object source, FileSystemEventArgs e)
{
WaitForFile(e.FullPath);
thread = new Thread(new ThreadStart(this.start));
thread.Start();
thread.Join();
}
private static void WaitForFile(string fullPath)
{
while (true)
{
try
{
using (StreamReader stream = new StreamReader(fullPath))
{
stream.Close();
break;
}
}
catch
{
Thread.Sleep(1000);
}
}
}
public void start()
{
Console.WriteLine("test" +counter);
counter++;
}
Following this article MSDN, Created event can solve your problem
The
M:System.IO.FileSystemWatcher.OnCreated(System.IO.FileSystemEventArgs)
event is raised as soon as a file is created. If a file is being
copied or transferred into a watched directory, the
M:System.IO.FileSystemWatcher.OnCreated(System.IO.FileSystemEventArgs)
event will be raised immediately
public void Watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "directorypath\\";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
I was wondering if there was a way to detect if a process is deleting or encrypting a file. I am trying to make an anti-ransomware application in C# so I was wondering if anyone could help.
Any suggestions?
You want to take a look at the FileSystemWatcher class.
From the MSDN page:
using System;
using System.IO;
using System.Security.Permissions;
public class Watcher
{
public static void Main()
{
Run();
}
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
public static void Run()
{
string[] args = System.Environment.GetCommandLineArgs();
// If a directory is not specified, exit program.
if(args.Length != 2)
{
// Display the proper way to call the program.
Console.WriteLine("Usage: Watcher.exe (directory)");
return;
}
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[1];
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while(Console.Read()!='q');
}
// Define the event handlers.
private static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}
}
FileSystemWatcher: how to rise events only for new files in directory?
I have a directory, which my service scan. And I use FileSystemWatcher:
constructor:
if(Directory.Exists(_dirPath))
{
_fileSystemWatcher = new FileSystemWatcher(_dirPath);
}
Then, I subscribes on Directory:
public void Subscribe()
{
try
{
//if (_fileSystemWatcher != null)
//{
// _fileSystemWatcher.Created -= FileSystemWatcher_Created;
// _fileSystemWatcher.Dispose();
//}
if (Directory.Exists(_dirPath))
{
_fileSystemWatcher.EnableRaisingEvents = true;
_fileSystemWatcher.Created += FileSystemWatcher_Created;
_fileSystemWatcher.Filter = "*.txt";
}
}
But, the problem is that i want to get events when new files create (or copy).
Instead, i get events from all files in this directory already exists.
How to get event only from new files?
Thank you!
By setting NotifyFilter to NotifyFilters.FileName | NotifyFilters.CreationTime | NotifyFilters.LastWrite you can watch if new files are created.
You also need to check e.ChangeType == WatcherChangeTypes.Created in the raised event after any change occurred.
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher();
string filePath = #"d:\watchDir";
watcher.Path = filePath;
watcher.EnableRaisingEvents = true;
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime | NotifyFilters.LastWrite;
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
watcher.Created += new FileSystemEventHandler(OnFileCreated);
new System.Threading.AutoResetEvent(false).WaitOne();
}
private static void OnFileCreated(object sender, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Created)
// some code
}
From experience I have noticed that the events that get raised when editing a file can wildly differ depending on the application that edits the the file.
Some applications overwrite, others append.
I found that polling every now and then and keeping a list of files that already existed on the previous poll was more reliable than trying to get the events right.
I have a question: How do I determine whether a folder has finished copying from one location to another?
At the moment my FileSystemWatcher triggers several events as soon as a file within the directory being copied. What I want though, is one single event to be triggered when all the files within that folder has been successfully copied. My code right now looks like this:
static void Main(string[] args)
{
String path = #"D:\Music";
FileSystemWatcher mWatcher = new FileSystemWatcher();
mWatcher.Path = path;
mWatcher.NotifyFilter = NotifyFilters.LastAccess;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
mWatcher.IncludeSubdirectories = true;
mWatcher.Created += new FileSystemEventHandler(mLastChange);
mWatcher.Changed += new FileSystemEventHandler(mLastChange);
mWatcher.EnableRaisingEvents = true;
Console.WriteLine("Watching path: " + path);
String exit;
while (true)
{
exit = Console.ReadLine();
if (exit == "exit")
break;
}
}
private static void mLastChange(Object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + " " + e.FullPath);
}
Unfortunately FileSystemWatcher doesn't tell you when a file is finished writing. So your options are...
Set a timeout after last write when it is assumed there are no more changes coming
Have the writing application put a lock file of some variety that tells any other program that it's done.
After re-reading your question... it doesn't sound like you have any control over the other application.
So you will need some kind of timeout value that determines when all the writing is done. Basically create a timer that resets after each filesystemwatcher event... when it times out then you fire the single event that says it's done.
Here is how you could add it to your code...
static void Main(string[] args)
{
Timer.Interval = 5000; // 5 seconds - change to whatever is appropriate
Timer.AutoReset = false;
Timer.Elapsed += TimeoutDone;
String path = #"D:\Music";
FileSystemWatcher mWatcher = new FileSystemWatcher();
mWatcher.Path = path;
mWatcher.NotifyFilter = NotifyFilters.LastAccess;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
mWatcher.IncludeSubdirectories = true;
mWatcher.Created += new FileSystemEventHandler(mLastChange);
mWatcher.Changed += new FileSystemEventHandler(mLastChange);
mWatcher.EnableRaisingEvents = true;
Console.WriteLine("Watching path: " + path);
Timer.Start();
String exit;
while (true)
{
exit = Console.ReadLine();
if (exit == "exit")
break;
}
}
private static Timer Timer = new Timer();
private static void TimeoutDone(object source, ElapsedEventArgs e)
{
Console.WriteLine("Timer elapsed!");
}
private static void mLastChange(Object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + " " + e.FullPath);
if (Timer != null)
{
Timer.Stop();
Timer.Start();
}
}
It's horribly cheesy, but in the past I have dealt with this problem by creating a custom decorator for the FileSystemWatcher class. Internally it creates a FileSystemWatcher and registers for the Created and Changed events, wraps them, and throws its own Created and Changed events after the files are finished, similar to this:
private void Watcher_Changed(Object sender, FileSystemEVentArgs e)
{
while (true)
{
FileStream stream;
try
{
stream = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
// If this succeeds, the file is finished
Changed();
}
catch (IOException)
{
}
finally
{
if (stream != null) stream.Close();
}
}
}
Some of this is drawn from the answer here. In reality, you shouldn't use an infinite loop. You probably want to add timeouts, sleep in between checks, etc. but this is the general idea.
If the destination is a local folder, you can use the filesystem filter driver to track file create and file close events. Knowing when all files, previously created, are closed will inform you that copying is complete.
I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done.
Download FileSystemSafeWatcher and add it to your project.
Then use it as a normal FileSystemWatcher and monitor when the events are triggered.
var fsw = new FileExamSystemWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;
Unfortunatly there is no ready solution. but I designed a simple tricky solution to trigger (copy finished) event.
you should use timer.
FileSystemWatcher fsw = new FileSystemWatcher();
string fullPath = "";
DateTime tempTime;
fsw.Path = #"C:\temp";
private void startwatching()
{
timer1.Start();
}
fsw.EnableRaisingEvents = true;
fsw.Created += Fsw_Created;
private void Fsw_Created(object sender, FileSystemEventArgs e)
{
tempTime = DateTime.Now.AddSeconds(-4);
fullPath = e.FullPath;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (fullPath!=string.Empty)
{
timer1.Stop();
if (tempTime >= Directory.GetLastAccessTime(fullPath))
{
DirectoryInfo di = new DirectoryInfo(fullPath);
listBox1.Items.Add("Folder " + di.Name + " finished copying");
fullPath = string.Empty;
}
else
{
tempTime = DateTime.Now;
}
timer1.Start();
}
}