FileSystemWatcher event on adding a folder with subdirectories - c#

I'm using a FileSystemWatcher to monitor a directory for new folders created. This would work just fine, except that each of these folders can have one or more subdirectories inside of them.
The problem is that when I copy the top level folder (with one or more subdirectories in it) the FileSystemWatcher only catches the addition of the top level folder and not the subdirectories.
Ex: Drag folder 'foo' with subdirectory 'bar' into directory being watched
FileSystemWatcher notifies that new directory 'foo' was created, but says nothing about 'bar'
Am I missing something with the functionality of FileSystemWatcher or is there some other way around this?

You also need to handle OnRenamed, as well as IncludeSubdirectories.
From MSDN:
Copying and moving folders
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. If you cut and paste a folder with files into a
folder being watched, the FileSystemWatcher object reports only the
folder as new, but not its contents because they are essentially only
renamed.
To be notified that the contents of folders have been moved or copied
into a watched folder, provide OnChanged and OnRenamed event handler
methods...

Setting IncludeSubdirectories does not work in this specific case.
Here's a sample i set up:
static void Main(string[] args)
{
FileSystemWatcher fsw = new FileSystemWatcher(#"C:\Temp", "*.*");
fsw.EnableRaisingEvents = true;
fsw.Created += new FileSystemEventHandler(fsw_Created);
fsw.Renamed += new RenamedEventHandler(fsw_Renamed);
fsw.Changed += new FileSystemEventHandler(fsw_Changed);
fsw.IncludeSubdirectories = true;
Console.ReadLine();
}
static void fsw_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine("{0} was changed.", e.FullPath);
}
static void fsw_Renamed(object sender, RenamedEventArgs e)
{
Console.WriteLine("{0} was renamed.", e.FullPath);
}
static void fsw_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine("{0} was created", e.FullPath);
}
In this case, if i move foo that contains bar folder in it into C:\Temp, i will only be notified of C:\Temp\Foo created.
FileSystemWatcher is not sealed, if this functionality is vital, i would subclass it and extend it to check under created folders, and raise events for them as well (or something similar to this implementation).

You need to set IncludeSudirectories which will get or set a value whether the subdirectories will be monitored.

You need to set the IncludeSubdirectories property.
Take a look at this link
"Set IncludeSubdirectories to true when you want to watch for change notifications for files and directories contained within the directory specified"
watcher.IncludeSubdirectories = true;

As far as I know you there is no way to tell the FileSystemWatcher to notify you for every element in a directory that has been changed.
If the element modified is a directory, you have to do the browsing on your own (which is not terribly hard).
IncludeSubdirectories only tells you about modifications of files and folders within the element watched - but alas not if it has been copied in in one move. In your example, when you watch foo and somebody adds a file to bar (later on) and you have set IncludeSubdirectories you will be notified of that change.
hth
Mario

Related

FileSystemWatcher IncludeSubdirectories not working on network share

I'm attempting to watch a network share for folders where a file is added to the share to indicate the upload is complete. I wanted to use a FileSystemWatcher to monitor that share and its subdirectories for that trigger file, as shown below.
FileSystemWatcher fsw = new FileSystemWatcher(share, triggerFilePattern);
fsw.IncludeSubdirectories = true;
fsw.Created += new FileSystemEventHandler(OnCreated);
fsw.Renamed += new RenamedEventHandler(OnRenamed);
fsw.Error += new ErrorEventHandler(OnError);
If a file is created at the root of the share, the event triggers. If a file is created within a subdirectory of the share, the event does not trigger, and I don't get an error either.
If I create a new FileSystemWatcher for that subdirectory, then I do receive an event when a file is created there. But, similar to the top level FileSystemWatcher, I won't get events for files created in any further subdirectories.
If I change the network share to a local directory for testing, it does work as expected.
Any ideas? Could I have set up some wonky network share setting that blocks recursive FileSystemWatcher checks? I can work around this, but it would be nice to not have to complicate the code.
Edit: I saw that I didn't have Full Control under Properties->Security tab, so I thought that could be it. But I was able to get normal desired behavior on a different share with the same visible permissions, so I'm back to not knowing why this specific share isn't working.
Edit2: At a coworker's suggestion, I added a Changed handler too. Doesn't make sense to me how a file can be changed without getting Created first, but... I'm getting Changed events on the share in question when creating files in a subdirectory. (And I'm still getting nothing when renaming them.) This solves my immediate problem, but I'm going to leave the question open in case someone's able to answer why that's happening.
Try this
private static FileSystemWatcher fw;
static void Main(string[] args)
{
fw = new FileSystemWatcher(#"D:\Folder_Watch");
fw.IncludeSubdirectories = true;
fw.NotifyFilter = NotifyFilters.LastAccess |
NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
fw.Created += fw_Created;
fw.Changed += fw_Created;
fw.Renamed += fw_Created;
fw.Filter = "*.*";
fw.EnableRaisingEvents = true;
new System.Threading.AutoResetEvent(false).WaitOne();
}
static void fw_Created(object sender, FileSystemEventArgs e)
{
try
{
fw.EnableRaisingEvents = false;
//Your Code
}
finally
{
fw.EnableRaisingEvents = true;
}
}
I had same problem and I solved it by giving write permission to the shared network folder (it had only read permission so far). Shared folder is originally on Linux but I have it mounted on Windows Server 2008 R2 where I'm running program with FileSystemWatcher.

Is it possible to watch a file, to see when it is being read?

I have a file, it is large, reading it takes a long time and I'd like to have some monitoring to see when it's being written to.
I can solve this quite easily by chucking some logging into the application that it reading it, however I'd like something more agnostic. There are quite a few application that have a similar use case. Monitoring writes should be easy enough as I can watch the last modified date, but reading isn't so easy.
If there any way to passively monitor read and writes to a file?
To clarify: If it can do done in 100% C#, then great, if not then shelling out to some other 'thing', or even resorting to some other language is fine. I don't really mind what the watching bit is written in.
Trying Rahul's Solution
I have set up ithe following test code. It dumps events to the console:
public static void Main()
{
var taskFactory = new TaskFactory();
var setup = new Action(() =>
{
var setupWatcher =
new Action<NotifyFilters, string, FileSystemWatcher>((filters, s, watcher) =>
{
watcher.EnableRaisingEvents = true;
watcher.NotifyFilter = filters;
watcher.Changed += (sender, args) => System.Console.WriteLine(s, args.FullPath, args.ChangeType);
});
var lastAccessWatcher = new FileSystemWatcher(BASE_PATH);
setupWatcher(NotifyFilters.LastAccess,
"File: {0}\tFilter: LastAccess\tType: {1}", lastAccessWatcher);
var lastWriteWatcher = new FileSystemWatcher(BASE_PATH);
setupWatcher(NotifyFilters.LastWrite, "File: {0}\tFilter: LastWrite\tType: {1}",
lastWriteWatcher);
var fileNameWatcher = new FileSystemWatcher(BASE_PATH);
setupWatcher(NotifyFilters.FileName,
"File: {0}\tFilter: FileName\tType: {1}", fileNameWatcher);
var directoryNameWatcher = new FileSystemWatcher(BASE_PATH);
setupWatcher(NotifyFilters.LastWrite, "File: {0}\tFilter: DirectoryName\tType: {1}",
directoryNameWatcher);
});
taskFactory.StartNew(setup);
while (true)
{
Thread.Sleep(10);
}
}
However, when I open a text file in notepad, no event is thrown by the lastAccessWatcher, whereas, when I save, two events are thrown by the lastWriteWatcher and the directoryNameWatcher, as per below.
File: F:\FileMonitor\New Text Document.txt Filter: LastWrite Type: Changed
File: F:\FileMonitor\New Text Document.txt Filter: LastWrite Type: Changed
File: F:\FileMonitor\New Text Document.txt Filter: DirectoryName Type: Changed
File: F:\FileMonitor\New Text Document.txt Filter: DirectoryName Type: Changed
So...
What does trigger 'last access'
Can I actually have any trigger fired when a file is read?
For watching writes to the file, the FileSystemWatcher class is the right way to go. However if you want to know if a file is being written, what I would do is get a list of all the open files by process, and monitor when the file is opened and then closed.
There is a CodeProject article that shows how to get the open files by process here.
One solution would be to use a Microsoft tool called Process Monitor. It's able to list all CreateFile/ReadFile/WriteFile calls any process does. There are several command line options available:
You can use the FileSystemWatcher class
Listens to the file system change notifications and raises events when
a directory, or file in a directory, changes.
You can use the different events
Changed
Created
Deleted
Renamed
and also read this FileSystemWatcher Tips
An example:
public void MyFileWatcher(string path)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "myFile.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine(e.FullPath + " " + e.ChangeType);
}
Similarly you can add other event handlers definition.
Also if you want to check the read then you can use the FileInfo.LastAccessTime and FileInfo.Refresh() in a polling loop to get the information of when your file is being read.
In code your task is handled with a filesystem filter driver. Process Monitor application (already mentioned in another answer) includes such driver in pre-built form.
You can write a kernel-mode driver yourself, yet this would probably be an overkill. Then there exists our CallbackFilter - a class library with a pre-created kernel-mode driver included. You write code in C# and catch all filesystem operations before or after they are performed. If your need is short-term, then the evaluation license will work.

Create a folder without read-only using c# [duplicate]

I was successfully able to remove read only attribute on a file using the following code snippet:
In main.cs
FileSystemInfo[] sqlParentFileSystemInfo = dirInfo.GetFileSystemInfos();
foreach (var childFolderOrFile in sqlParentFileSystemInfo)
{
RemoveReadOnlyFlag(childFolderOrFile);
}
private static void RemoveReadOnlyFlag(FileSystemInfo fileSystemInfo)
{
fileSystemInfo.Attributes = FileAttributes.Normal;
var di = fileSystemInfo as DirectoryInfo;
if (di != null)
{
foreach (var dirInfo in di.GetFileSystemInfos())
RemoveReadOnlyFlag(dirInfo);
}
}
Unfortunately, this doesn't work on the folders. After running the code, when I go to the folder, right click and do properties, here's what I see:
The read only flag is still checked although it removed it from files underneath it. This causes a process to fail deleting this folder. When I manually remove the flag and rerun the process (a bat file), it's able to delete the file (so I know this is not an issue with the bat file)
How do I remove this flag in C#?
You could also do something like the following to recursively clear readonly (and archive, etc.) for all directories and files within a specified parent directory:
private void ClearReadOnly(DirectoryInfo parentDirectory)
{
if(parentDirectory != null)
{
parentDirectory.Attributes = FileAttributes.Normal;
foreach (FileInfo fi in parentDirectory.GetFiles())
{
fi.Attributes = FileAttributes.Normal;
}
foreach (DirectoryInfo di in parentDirectory.GetDirectories())
{
ClearReadOnly(di);
}
}
}
You can therefore call this like so:
public void Main()
{
DirectoryInfo parentDirectoryInfo = new DirectoryInfo(#"c:\test");
ClearReadOnly(parentDirectoryInfo);
}
Try DirectoryInfo instead of FileInfo
DirectoryInfo di = new DirectoryInfo(#"c:\temp\content");
di.Attributes = FileAttributes.Normal;
To clean up attrbutes on files-
foreach (string fileName in System.IO.Directory.GetFiles(#"c:\temp\content"))
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
fileInfo.Attributes = FileAttributes.Normal;
}
The dialog just works in a fairly bizarre way. It always shows up the way you see it in your screen shot, whatever the state of the ReadOnly attribute. The checkbox is in the 'indetermined' state. You have to click it and either clear or check it to make it perform its action. And in spite of the prompt text (but not the hint next to the checkbox), it only changes the ReadOnly attribute on the files in the directory, not the directory itself.
Use the attrib command line command to see what is really going on. In all likelihood, your code fails because the directory contains files that have their ReadOnly attribute set. You'll have to iterate them.
The read-only flag on directories in Windows is actually a misnomer. The folder does not use the read-only flag. The issue is going to be with the customization. The flag is used by Windows to identify that there are customizations on the folder.
This is an old post, with an issue that is sunsetting, but, figured people might still run into it, as it is pretty annoying when you hit it.
Microsoft's Explanation
IEnumerable / Lambda solution for recursively removing readonly attribute from directories and files:
new DirectoryInfo(#"some\test\path").GetDirectories("*", SearchOption.AllDirectories).ToList().ForEach(
di => {
di.Attributes &= ~FileAttributes.ReadOnly;
di.GetFiles("*", SearchOption.TopDirectoryOnly).ToList().ForEach(fi => fi.IsReadOnly = false);
}
);
Set the Attributes property on the original dirInfo:
dirInfo.Attributes = FileAttributes.Normal;
FileSystemInfo[] sqlParentFileSystemInfo = dirInfo.GetFileSystemInfos();
foreach (var childFolderOrFile in sqlParentFileSystemInfo)
{
RemoveReadOnlyFlag(childFolderOrFile);
}
Just in case any one happens across this later...
ALL of the other answers posted before mine are either wrong or use unnecessary recursion.
First of all the "Read Only" check box in the property dialog of windows always has the tri-state marker for folders. This is because the folder itself is not read only but the files inside can be.
If you want to set/unset read only flag for ALL files. you can do it simply as follows:
void SetReadOnlyFlagForAllFiles(DirectoryInfo directory, bool isReadOnly)
{
// Iterate over ALL files using "*" wildcard and choosing to search all directories.
foreach(FileInfo File in directory.GetFiles("*", SearchOption.All.Directories))
{
// Set flag.
File.IsReadOnly = isReadOnly;
}
}
I see that #DotnetDude said in comments that solutions of guys don't work. To my mind it is happens because guys don't mentioned that need to use File.SetAttributes method to apply new attributes.
This may or may not be directly related, but the root issue in your case may be caused by the underlying files. For example, I ran into this issue trying to delete a directory:
System.IO.Directory.Delete(someDirectory, true)
This results in "Access to the path 'blah' is denied". To resolve this underlying problem, I removed the read-only attribute on sub-files and was then able to remove the parent directory. In my case, I was using powershell, so you can use the .NET equivalent.
dir -r $PrePackageDirectory |% {if ($_.PSIsContainer -ne $true){$_.IsReadOnly = $false}}
Shell("net share sharefolder=c:\sharefolder/GRANT:Everyone,FULL")
Shell("net share sharefolder= c:\sharefolder/G:Everyone:F /SPEC B")
Shell("Icacls C:\sharefolder/grant Everyone:F /inheritance:e /T")
Shell("attrib -r +s C:\\sharefolder\*.* /s /d", AppWinStyle.Hide)
this code is working for me.. to share a folder to every one with read and write permission

FIleSystemWatcher last access (read) for media files is not working

I am delivering media contents to a client system (download from internet) and want to acknowledge if the media file was opened (and viewed) by the operator.
I am using a file system watcher (C# 3.5) to check for the last access time of file (so whenever the media file is played I should get the event) and I send an acknowledgement.
I have enabled the last access time in registry of my Windows 7 machine here and rebooted my system. File system watcher fires events on directory opened but not on media play.
Here is my code :
private FileSystemWatcher fsWatcher = null;
private static Object objLock = new Object();
private void StartAccessWatcher(string _folderPath)
{
fsWatcher = new FileSystemWatcher(_folderPath, "*.*");
fsWatcher.Changed += new FileSystemEventHandler(fsWatcher_Changed);
fsWatcher.IncludeSubdirectories = true;
fsWatcher.NotifyFilter = NotifyFilters.LastAccess;
fsWatcher.EnableRaisingEvents = true;
}
private void fsWatcher_Changed(object sender, FileSystemEventArgs e)
{
lock (objLock)
{
fsWatcher.EnableRaisingEvents = false;
DisplayMessage(string.Concat(e.ChangeType, " ", e.FullPath));
fsWatcher.EnableRaisingEvents = true;
}
}
private void btn_StartWatcher_Click(object sender, EventArgs e)
{
StartAccessWatcher(#"E:\Surewaves\Media-Store\MP1 Videos");
}
You may also download my code sample from here.
Please tell me how best can I achieve it ? I need to get the the last access time (when ever the video or audio or image or .swf file was played by any player) ?
As an update to this three year old question, the code given works correctly now* with videos played on the local machine.
The machine must have lastAccess successfully enabled with the following command
fsutil behavior set DisableLastAccess 0
as noted in the question.
*- at the time of this post, using Windows Server 2012 R2
The FileSystemWatcher is used to watch for changes in a specified directory. You can watch for changes in files and subdirectories of the specified directory. In your case there is no change being made on media files. I would suggest try extending FileSystemWatcher class and create new events for media file reading and then fire those events.

FileNotFoundException in FileSystemWatcher

I am using a FileSystemWatcher on a directory and added its event handlers, set its EnableRaisingEvents=true; and IncludeSubdirectories=false; and added NotifyFilters.
While running the application if I create new folders in the specified directory sometime I get
FileNotFoundException : "An error occurred while reading a directory".
System.IO.FileSystemWatcher.StartRaisingEvents()
System.IO.FileSystemWatcher.set_EnableRaisingEvents(Boolean value)
What can be root cause of the problem?
What is StartRaisingEvents()?
Just out of stupidity, I googled it before thinking.
In my case, Path was being defined after EnableRaisingEvents.
e.g. won't throw an exception:
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"C:\";
//...
watcher.EnableRaisingEvents = true;
This will:
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.EnableRaisingEvents = true;
//...
watcher.Path = #"C:\";
So. since I like to fail-fast instead of letting the next one figure out what the hell is happening, I modified that after path declaration:
var watcher = new FileSystemWatcher();
watcher.Path = #"C:\Users\me";
if (string.IsNullOrWhiteSpace(watcher.Path))
throw new InvalidOperationException($"You must define a path.");
if (!Directory.Exists(watcher.Path))
throw new InvalidOperationException($"Directory {watcher.Path} does not exist.");
watcher.EnableRaisingEvents = true;
Stupid issue, but at least I gave some quirky fail-fast solution.
This is typically because the FileSystemWatcher can be unreliable. The folder may not "fully" exist when you get the events. You may need to retry with sufficient pauses and do various Directory.Exists() checks before actually performing IO operations.
I've got quite the same problem and finally I found out that the problem was with the path.
The Directory.Exist() give answer that the directory exist... even if the path got a empty char in the end of the string but the FileSystemWatcher couldn't manage it.
So obviously the Directory.Exist() trim the path but the Watcher don't. In my case removing of the empty chars solve the problem.
Hopefully it could help somebody.
For me it was some mystery where, I guess a directory was named with some invalid character(s). Directory.Exists(watchDirectory) would return true, but the FileWatcher would blow up with the error above.
The "fix" was to delete the directory and recreate it with the same name :)

Categories