I'm trying to code a program which will watch a directory/disk and move deleted files/folders to recycle bin when they are deleted from network shared folder.
I have this code which is working when a file/folder is deleted but I need to cancel delete and move file to Recycle Bin. I found how to move file/folder to Recycle Bin here: How to Move files to the recycle bin
FileSystemWatcher fsw = new FileSystemWatcher();
private void frmMain_Load(object sender, EventArgs e)
{
StartWatcher("X:\\Virtuals\\");
}
private void StopWatcher()
{
if (fsw != null)
try
{
fsw.EnableRaisingEvents = false;
fsw.Dispose();
fsw = null;
}
catch { }
}
private void StartWatcher(string path)
{
StopWatcher();
FileSystemWatcher fsw = new FileSystemWatcher();
fsw.Path = Path.GetDirectoryName(path);
fsw.Filter = Path.GetFileName(path);
fsw.NotifyFilter = NotifyFilters.LastAccess |
NotifyFilters.LastWrite | NotifyFilters.FileName
| NotifyFilters.DirectoryName;
fsw.Deleted += new FileSystemEventHandler(OnDeleted);
fsw.EnableRaisingEvents = true;
}
private void OnDeleted(object source, FileSystemEventArgs e)
{ MessageBox.Show("File deleted: " + e.FullPath); }
FileSystemWatcher won't help you in your task. Proper way to do this is employ a filesystem filter driver which will intercept file deletion requests and move files to the recycle bin instead. You can create your own filter driver (requires knowledge of low-level kernel-mode development), or we offer CallbackFilter product which lets you do the job in user mode (even in .NET).
The FileSystemWatcher raises events after the file is actually deleted, modified or created, so it´s to late to do a copy after the delete event is raised. And there is no possibility to cancel an operation with the FileSystemWatcher. Possibly it can be solved by using some sort of low level APIs (but I don´t know which and how).
Related
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
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Detecting moved files using FileSystemWatcher
I was looking for a solution to watch a directory and notify my app whenever a new file is moved into the directory. The Obvious solution was to use FileSystemWatcher class of the .NET.
But the problem is , it raises a event a new file is created/deleted in the folder but does not raises event when a file is moved/copied into the folder.
Can anyone tell me what could be the cause of such behavior.
my code is
static void Main(string[] args)
{
Run();
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Run()
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"D:\New 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;
// 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);
}
I used FileSystemWatcher in one of my Home application. But FileSystemWatcher do not have any move or copy detection events as per my knowledge.
As per 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.
For more information click here.
What I did was monitoring the parent folder and sub folders and logged every changes in it.
To include sub directories I used the following property.
watcher.IncludeSubdirectories=true;
Some googling suggesting to use timer to detect changes. But I don't know how effective it is.
We have users that rename the shortcut file on their desktop to our application. What is the best way to delete/modify the shortcut based on targetpath if the icon changes for the application? In other words, I'm having a hard time locating the file name because it keeps getting changed.
It's a nice question and I surprised that nobody answered it correctly in 10 years.
The code below iterate through all links in given folder and finds one with TargetPath pointing to the currently executing WinForms app.
Add COM reference to your project to Windows Script Host Object Model.
using IWshRuntimeLibrary;
private static void DeleteShortcuts(string path)
{
// Example for path: Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
foreach (var fileName in Directory.GetFiles(path, "*.lnk"))
{
WshShell shell = new WshShell();
IWshShortcut link = (IWshShortcut)shell.CreateShortcut(fileName);
if (link.TargetPath == Application.ExecutablePath)
{
System.IO.File.Delete(fileName);
}
}
}
Note: Application.ExecutablePath works for WinForms to get the current exe path, for Console app you should use Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]). Refer to documentation for other types of projects.
You should use FileSystemWatcher class:
Listens to the file system change notifications and raises events when
a directory, or file in a directory, changes.
Infact you can exploit FileSystemWatcher.Changed, FileSystemWatcher.Created, FileSystemWatcher.Renamed , FileSystemWatcher.Deleted events to keep control on your file.
Here is an example by MSDN:
public static void Main()
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "mypath";
/* 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);
}
Renaming a shortcut does not modify the target path, however, the best way I know to work with shortcuts in c# is with the IwshRuntimeLibrary.
To delete a file please use System.IO.File.Delete method
To modify the file you can use System.IO.File.AppendText method
Update after comments from below :
Please use the ShellClass to create or modify shortcuts Also you will
need to get the special directory from desktop using
Environment.SpecialFolder.DesktopDirectory
A very good example showing step by step can be found here http://www.codeproject.com/Articles/146757/Add-Remove-Startup-Folder-Shortcut-to-Your-App
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.
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.