C# File System Watchers is only keeping count of Copied files - c#

I am currently trying to keep a counter on c# on a local file folder for new files that are created.
I have two sub directories to CD and LP that I have to keep checking.
File System Watcher is only keeping track of my copied folders.
Basically I need to keep track of folders created starting with EM* but my code shows the counter increasing when I copy and paste folders and not when I create the EM* folders.
e.g EM1 EM2 only EM2-copy increases the counter and even then sometimes it increases +2
static int LPcounter { get; set; }
static int CDcounter { get; set; }
static int LPCreated;
static int CDCreated;
FileSystemWatcher CDdirWatcher = new FileSystemWatcher();
FileSystemWatcher LPdirWatcher = new FileSystemWatcher();
public Form1()
{
InitializeComponent();
while (true)
watch();
}
public void watch()
{
CDdirWatcher.Path = #"C:\Data\LotData\CD";
CDdirWatcher.Filter = "EM*";
CDdirWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.LastWrite;
CDdirWatcher.EnableRaisingEvents = true;
CDdirWatcher.Created += CDdirWatcher_Created;
LPdirWatcher.Path = #"C:\Data\LotData\LP";
LPdirWatcher.Filter = "EM*";
LPdirWatcher.NotifyFilter = NotifyFilters.DirectoryName;
LPdirWatcher.EnableRaisingEvents = true;
LPdirWatcher.Created += LPdirWatcher_Created;
}
private static void CDdirWatcher_Created(object sender, FileSystemEventArgs e)
{
CDCreated += 1;
}
private static void LPdirWatcher_Created(object sender, FileSystemEventArgs e)
{
LPCreated += 1;
}

Your code is right, try to create directory with console and MKDIR, it will work. If you create a directory from Explorer its first created as "New folder" and then renamed.
From Microsoft web: copy and paste is interpreteds as rename
https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.notifyfilter?view=netframework-4.8
The operating system and FileSystemWatcher object interpret a
cut-and-paste action or a move action as a rename action for a folder
and its contents
From the same docu, events can raise multipletimes:
Common file system operations might raise more than one event. For
example, when a file is moved from one directory to another, several
OnChanged and some OnCreated and OnDeleted events might be raised.

Related

Why does following Directory.Delete throw IOException The directory is not empty

I am creating a directory and setting up a FileSystemWatcher on it.
Then I create a file. All this is done in the form constructor. In a button event handler I delete the directory created above. Sometimes it throws a IOException: The directory is not empty. After this I cannot even access the child directory in explorer. I keep getting Access is Denied Errors. This directory gets deleted after my process exits.
AFAIK FileSystemWatcher should not a lock a directory.
string alphaBeta = #"Alpha\Beta";
public Form1()
{
InitializeComponent();
Directory.CreateDirectory(alphaBeta);
FileSystemWatcher watcher = new FileSystemWatcher()
{
Path = alphaBeta,
Filter = "*.dat",
NotifyFilter = NotifyFilters.FileName
};
watcher.EnableRaisingEvents = true;
File.WriteAllText(alphaBeta + #"\Gamma.dat", "Delta");
}
private void btnDelete_Click(object sender, EventArgs e)
{
Directory.Delete("Alpha", true);//Recursively Delete
}
How do I properly delete the directory without getting the directory stuck?
UPDATE:
Above is minimal reproducible example
My actual scenario involves a addin loaded in explorer.exe that monitors a config directory for changes. It hooks into create and rename events of FSW.
The delete code runs in the uninstaller. Uninstaller is not supposed to kill explorer. We thought FileSystemWatcher will not lock the Folder and will silently stop monitoring changes once the directory is deleted. But this doesn't seem to be the case.
The problem is caused because the FSW keeps a handle to the directory open as long as events are enabled. The source code shows that disabling events also closes the handle. To delete the directory, the FSW must be disabled.
The second problem though is that FileSystemWatcher watcher is defined as a variable inside the Form1() constructor, which means it's orphaned and immediatelly available for garbage collection before the form is even displayed. The garbage collector runs infrequently though, which means there's no guarantee it will run before Delete is clicked. Since the watcher isn't stored anywhere, iIt's no longer possible to disable it.
At the very least, the FSW needs to be stored in a field and events disabled before deletion. We should also ensure the watcher is disposed when the form itself gets disposed :
public class Form1
{
FileSystemWatcher _watcher;
public Form1()
{
...
_watcher=CreateDormantWatcher(path,pattern);
_watcher.EnableRaisingEvents=true ;
}
private void btnDelete_Click(object sender, EventArgs e)
{
_watcher.EnableRaisingEvents =false;
Directory.Delete("Alpha", true);//Recursively Delete
}
protected override void Dispose (bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_watcher.Dispose();
}
_watcher=watcher;
}
FileSystemWatcher CreateDormantWatcher(string path,string pattern)
{
//Don't store to the field until the FSW is
//already configured
var watcher=new FileSystemWatcher()
{
Path = path,
Filter = "pattern,
NotifyFilter = NotifyFilters.FileName
};
watcher.Changed += OnChanged;
watcher.Created += OnCreated;
watcher.Deleted += OnChanged;
watcher.Renamed += OnRenamed;
return watcher;
}
Easy fix: Add it as a component
Perhaps a better idea though, would be to add the FileSystemWatcher on the form as a component. FileSystemWatcher inherits from Component, which means that placing it on the form add its creation and configuration code in InitializeComponents(). It will also get disposed when all other components get disposed.
If we do that, we'd just have to toggle EnableRaisingEvents when appropriate.
Assuming the path and pattern are set as properties, and the component's name is the imaginative FileSystemWatcher1 this brings the code down to :
public Form1()
{
InitializeComponent();
Directory.CreateDirectory(FileSystemWatcher1.Path);
FileSystemWatcher1.EnableRaisingEvents = true;
File.WriteAllText(alphaBeta + #"\Gamma.dat", "Delta");
}
private void btnDelete_Click(object sender, EventArgs e)
{
FileSystemWatcher1.EnableRaisingEvents = false;
Directory.Delete("Alpha", true);//Recursively Delete
}
This appears to be an issue with the FileSystemWatcher. If the code for this is commented out then the Exception does not occur.
It would appear that the FileSystemWatcher is not necessarily disposed before the Directory.Delete method is called.
It would be appropriate to handle the FileSystemWatcher in a way that causes it to be disposed before deleting resources that it may have depended upon, but in your example code it's easily achieved by simply adding watcher.Dispose();:
string alphaBeta = #"Alpha\Beta";
public Form1()
{
InitializeComponent();
Directory.CreateDirectory(alphaBeta);
FileSystemWatcher watcher = new FileSystemWatcher()
{
Path = alphaBeta,
Filter = "*.dat",
NotifyFilter = NotifyFilters.FileName
};
watcher.EnableRaisingEvents = true;
watcher.Dispose();
File.WriteAllText(alphaBeta + #"\Gamma.dat", "Delta");
}
private void button1_Click(object sender, EventArgs e)
{
Directory.Delete("Alpha", true);//Recursively Delete
}

FileSystemWatcher - Reading last line of file after update?

I'm trying to write a simple console application that waits for a change to a file then reads just the last line of that file. The file watcher works and the change event fires. But I'm struggling to work out how read from the file.
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Path = "E:\\myFilePath";
watcher.Filter = "";
watcher.EnableRaisingEvents = true;
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
Console.ReadKey();
}
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine(File.ReadLines(e.FullPath).Last());
}
when testing (editing the file in notepad) it will work once, then after a second edit I get the error...
System.IO.IOException: 'The process cannot access the file '' because
it is being used by another process.'
I still ran into problems using lines.GetEnumerator().Dispose();
You can add a pause before opening the file, just chose your poison for doing so.
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
for (int x = 0; x <= 500000; x++)
{
int t = x;
}
Console.WriteLine(File.ReadLines(e.FullPath).Last());
}
I tried using SreamReader too but still ran into the same problem.
// Does not work
using (StreamReader r = new StreamReader(e.FullPath))
{
while (r.EndOfStream == false)
{
m = r.ReadLine();
}
r.Close();
}
Console.WriteLine("{0}\n", m);
The problem is due to an already open handle to the file. File.ReadLines uses an iterator internally which is Disposable. Use this to ensure Disposal during the lifetime of your event handler.
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
var lines = File.ReadLines(e.FullPath);
Console.WriteLine(lines.Last());
lines.GetEnumerator().Dispose();
}
This should guarantee disposal before the end of event handler.
Edit: In the case that a different process is holding a write lock to the file, use Thread.Sleep(ms) with a safe time as inspecting and releasing file handles is not feasible in C#.

FileSystemWatcher not firing events when file written using fprintf or printf

Yes there are many similar questions but none of them address my unique situation.
There is a separate c++ process writing the file using c++ printf and fprintf.
filename i am trying to watch is info_20160525.log
My fileSystemWatcher in winform C# application gets notification when the writer process writes to the file AND I physically access the file i-e F5 the folder or have it open in textpad and click the opened file or right click the file but I never get any event notification when I dont physically interact with the file.
Also, when I shutdown the writer application I do get the notification.
Here is my code.
public bool StartUp(string fullfilepath, int linenumber)
{
if (!File.Exists(fullfilepath))
return false;
if (!LogClass.CheckPathExists(m_applicationPath))
return false;
try
{
FileInfo info = new FileInfo(fullfilepath);
m_filename = fullfilepath;
m_fileWatcher = new FileSystemWatcher(info.DirectoryName, info.Name);
m_fileWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.LastAccess
| NotifyFilters.LastWrite | NotifyFilters.Size ;
m_fileWatcher.Changed += m_fileWatcher_Changed;
m_fileWatcher.Error += m_fileWatcher_Error;
m_fileWatcher.Created += m_fileWatcher_Created;
m_fileWatcher.Deleted += m_fileWatcher_Deleted;
m_fileWatcher.Disposed += m_fileWatcher_Disposed;
m_fileWatcher.Renamed += m_fileWatcher_Renamed;
m_linesRead = linenumber;
m_fileWatcher.EnableRaisingEvents = true;
}
catch(Exception e)
{
LogClass.LogError(e, "Trouble accessing the file" + fullfilepath, m_applicationPath);
}
return true;
}
These are the handlers. I have breakpoints in each one of them but I never get a trigger unless ofcourse I physically interact with the file.
void m_fileWatcher_Renamed(object sender, RenamedEventArgs e)
{
string S = "";
}
void m_fileWatcher_Disposed(object sender, EventArgs e)
{
string S = "";
}
void m_fileWatcher_Deleted(object sender, FileSystemEventArgs e)
{
string S = "";
}
void m_fileWatcher_Created(object sender, FileSystemEventArgs e)
{
string S = "";
}
void m_fileWatcher_Error(object sender, ErrorEventArgs e)
{
string S = "";
}
void m_fileWatcher_Changed(object sender, FileSystemEventArgs args)
{
if (args.ChangeType == WatcherChangeTypes.Changed)
{
while (ParseFile(args.FullPath))
{
}
}
}
I bet this thread has your answer --> FileSystemWatcher changed event (for “LastWrite”) is unreliable
The FileSystemWatcher uses an update to the LastWrite attribute of a file to fire events, however, the LastWrite is not updated in real time and should not be relied upon as trigger for an event.
If you have enough time and resources on your hands then you probably want to look into File System Filters and the simpler approach of a Filter Manager and Minifilter Driver. It is driver type development, however, it is a sure file way to accomplish your objective.
It is dug down a little deeper by system policy but gives you a wide array of events to latch onto. If I was doing this for anything like pci compliance or similar tasks then I would not use the FileSystemWatcher.
Make sure you set IncludeSubdirectories to true.
https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx

How to implement file processing in Windows Service?

How do I create a monitoring Windows Service (C#) for several directories to scan for new files and (if there are any) edit, rename, move them somewhere else? I have created a WorkerTask() but it works only for files that are in directory when I start the service, not for the ones I put there later. It has to run 24/7.
private void WorkerTask() {
while (running) {
// only 1 input dir in this case
string[] filePaths = Directory.GetFiles(input_dir, "*.jpg");
if (filePaths.Lenght > 0)
{
foreach (String file_path in filePaths)
{
// some other operations before moving
File.Move(file_path, output_file_path);
}
}
}
}
How can I constantly scan for new (only complete!) files that are being uploaded to this folder? It has to run with like a maximum of 2-3 seconds delay between scans, so that as soon as the file lands in folder it's processed and moved. I've seen FileSystemWatcher() but I think trying to implement it for multiple input folders might not be a good idea.
public void Start()
{
FileSystemWatcher fsw = new FileSystemWatcher();
fsw.Path = "\\server\share"; //or use your inputdir
fsw.NotifyFilter = NotifyFilters.Size; //(several others available)
fsw.Filter = "*.jpg";
fsw.Changed += new FileSystemEventHandler(OnChanged);
fsw.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
//do stuff.
}
Use the FileSystemWatcher class, that's what it's for.
It has several events that you can subscribe for - when files are created, updated and deleted.

Monitor multiple folders using FileSystemWatcher

Whats the best way to monitor multiple folders (not subdirectories) using FileSystemWatcher in C#?
I don't think FSW supports monitoring multiple folders, so just instantiate one per folder you want to monitor. You can point the event handlers at the same methods, though, which should end up working like I think you want.
The easiest way is to create multiple instances of the FileSystemWatcher object.
http://www.c-sharpcorner.com/UploadFile/mokhtarb2005/FSWatcherMB12052005063103AM/FSWatcherMB.aspx
You'll have to make sure the you handle events between the two folders correctly:
Although some common occurances, such
as copying or moving a file, do not
correspond directly to an event, these
occurances do cause events to be
raised. When you copy a file, the
system raises a Created event in the
directory to which the file was copied
but does not raise any events in the
original directory. When you move a
file, the server raises two events: a
Deleted event in the source directory,
followed by a Created event in the
target directory.
For example, you create two instances
of FileSystemWatcher.
FileSystemWatcher1 is set to watch
"C:\My Documents", and
FileSystemWatcher2 is set to watch
"C:\Your Documents". Now, if you copy
a file from "My Documents" into "Your
Documents", a Created event will be
raised by FileSystemWatcher2, but no
event is raised for
FileSystemWatcher1. Unlike copying,
moving a file or directory would raise
two events. From the previous example,
if you moved a file from "My
Documents" to "Your Documents", a
Created event would be raised by
FileSystemWatcher2 and a Deleted event
would be raised by FileSystemWatcher
Link to FileSystemEventArgs
Out of the box, FileSystemWatcher only supports monitoring a single parent directory. To monitor multiple sibling directories, you would need to create multiple instances of FileSystemWatcher.
You can try cheating this behavior, however, by taking advantage of FileSystemWatcher's ability to include subdirectories. You can create an NTFS junction point (aka symbolic link) as a subdirectory from the directory you are watching. Mark Russinovich of Sysinternals fame has a utility called Junction to simplify creation and management of symlinks.
Note that you can only create symlinks to directories on your local machine.
Although this is an old question I decided to answer, because I couldn't find a good answer anywhere.
So, the aim was to monitor multiple folders (not subdirectories) using FileSystemWatcher? Here's my suggestion:
using System;
using System.IO;
using System.Security.Permissions;
using System.Collections.Generic;
namespace MultiWatcher
// ConsoleApplication, which monitors TXT-files in multiple folders.
// Inspired by:
// http://msdn.microsoft.com/en-us/library/system.io.filesystemeventargs(v=vs.100).aspx
{
public class Watchers
{
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 PATH [...] [PATH]";
return;
}
List<string> list = new List<string>();
for (int i = 1; i < args.Length; i++)
{
list.Add(args[i]);
}
foreach (string my_path in list)
{
Watch(my_path);
}
// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
private static void Watch(string watch_folder)
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = watch_folder;
/* 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;
}
// 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);
}
}
}
Can you simply use multiple instances of the FileSystemWatcher, one for each directory?
You would have to instantiate multiple instances of the FileSystemWatcher object. Though you can bind the Events to the same method and use the sender object to determine which FileSystemWatcher triggered the event.
var fsw1 = new FileSystemWatcher();
var fsw2 = new FileSystemWatcher();
FileSystemEventHandler fsw_changed = delegate(object sender, FileSystemEventArgs e)
{
Console.WriteLine("{0} - {1}", (sender as FileSystemWatcher).Path, e.ChangeType);
};
fsw1.Changed += fsw_changed;
fsw2.Changed += fsw_changed;
or you can pass the paths in-code, to mark a certain range of the domain watched like in this :
multiple monitor link
hope this helps.

Categories