I made a small winforms application to monitor a certain folder for new pdf files, if a new pdf file is created in the particulair folder it will copy it to an other location.
The problem i'm having is that the filesystemwatcher creates double/multiple entries in my listbox, how can i solve this?
namespace Scanmonitor
{
public partial class Form1 : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
DateTime lastRead = DateTime.MinValue;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FileWatch();
}
public void FileWatch()
{
watcher.Path = #"C:\Scanner";
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite;
watcher.Filter = "*.pdf";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
public void OnChanged(object source, FileSystemEventArgs e)
{
scannerListBox.Items.Add(e.FullPath);
scannerListBox.SelectedIndex = scannerListBox.Items.Count - 1;
FileMove(scannerListBox.SelectedItem.ToString());
}
public void FileMove(string filePath)
{
try
{
System.IO.File.Copy(filePath, #"\\share\Data\Scans op OCE 600\" + Path.GetFileName(filePath));
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
}
}
}
}
Got it working.
public void OnChanged(object source, FileSystemEventArgs e)
{
try
{
watcher.EnableRaisingEvents = false;
FileInfo objFileInfo = new FileInfo(e.FullPath);
if (!objFileInfo.Exists) return;
System.Threading.Thread.Sleep(5000);
FileInfo fileinformatie = new FileInfo(e.FullPath);
string strCreateTime = fileinformatie.CreationTime.ToString();
string strCreateDate = fileinformatie.CreationTime.ToString();
strCreateTime = strCreateTime.Remove(strCreateTime.LastIndexOf(" "));
strCreateDate = strCreateDate.Remove(0,strCreateDate.LastIndexOf(" "));
ProcessAllFiles(e.FullPath, strCreateTime, strCreateDate);
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
finally
{
watcher.EnableRaisingEvents = true;
}
}
You need to keep track of files (in a collection or dictionary) that already raised an event by the FileSystemWatcher. According to MSDN
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. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
public void OnChanged(object source, FileSystemEventArgs e)
{
try
{
watcher.EnableRaisingEvents = false;
FileInfo objFileInfo = new FileInfo(e.FullPath);
if (!objFileInfo.Exists) return;
System.Threading.Thread.Sleep(5000);
FileInfo fileinformatie = new FileInfo(e.FullPath);
string strCreateTime = fileinformatie.CreationTime.ToString();
string strCreateDate = fileinformatie.CreationTime.ToString();
//Ignore this, only for my file information.
strCreateTime = strCreateTime.Remove(strCreateTime.LastIndexOf(" "));
strCreateDate = strCreateDate.Remove(0,strCreateDate.LastIndexOf(" "));
ProcessAllFiles(e.FullPath, strCreateTime, strCreateDate);
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
finally
{
watcher.EnableRaisingEvents = true;
}
}
Related
I posted a question here but deleted it after I found a rather tedious solution
I am trying to write an app that can monitor multiple folders. I looked at the following solutions:
FIleSystemWatcher multiple folders (Dynamically)
Create multiple instances of the same FileSystemWatcher
Monitor multiple folders using FileSystemWatcher
I tried all their solutions. But what would happen is that it only worked for local folders. Not network drives.
My last implementation was a combination of all three:
public static void StartMultipleWatchers(string path)
{
string[] paths = path.Split(',');
foreach (string folderPath in paths)
{
try
{
string folderPathtrim = folderPath.Trim();
WatchFile(folderPathtrim);
}
catch(Exception ex)
{
Logger.Error(ex);
}
}
}
private static void WatchFile(string monitoredDir)
{
FileSystemWatcher fsw = new FileSystemWatcher(monitoredDir, "*.gz");
fsw.Changed += new FileSystemEventHandler(OnChanged);
fsw.Created += new FileSystemEventHandler(OnCreated);
fsw.EnableRaisingEvents = true;
fsw.IncludeSubdirectories = true;
Logger.Info($"Started loop Monitor of Folder: {monitoredDir}");
}
private static void OnCreated(object sender, FileSystemEventArgs e)
{
FileInfo fileInfo = new FileInfo(e.FullPath);
string value = $"Created: {e.FullPath}";
Logger.Info(value);
}
Again this solution only worked for local folder. Or if I only made one watcher that watched one network folder.
Then I tried this very tedious solution:
public static void TestManualWatchers()
{
var fsw1 = new FileSystemWatcher(#"\\lap.org.com\tool_data_odp_ws\metrology\CIM\DATA_READY\");
var fsw2 = new FileSystemWatcher(#"C:\TestPath\");
fsw1.Changed += OnChanged;
fsw1.Created += OnCreated;
fsw1.EnableRaisingEvents = true;
fsw1.IncludeSubdirectories = true;
fsw2.Changed += OnChanged;
fsw2.Created += OnCreated;
fsw2.EnableRaisingEvents = true;
fsw2.IncludeSubdirectories = true;
Logger.Info($"Started watching manual double files");
}
Where I manually create each watcher and it's own properties. What is the difference between this tedious way and the dynamic above?
Is there a way to actually dynamically create individual watchers?
I wrote a little program which I run as a Windows-Service, which moves Files of an Inbox-Folder to another location on receive.
But randomly it stops working (Service still runing, process not crashing). If I just move them out the inbox and back, it starts again.
Any hints to improve?
//CIPCoyService.cs
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Threading;
namespace CopyCIP
{
public partial class CIPCopyService : ServiceBase
{
public CIPCopyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// start the triggers
initXML();
initPDF();
initCSV();
}
private static void initXML()
{
FileSystemWatcher WatcherXML = new FileSystemWatcher();
WatcherXML.Path = #"C:\xxx\baseDir\inbox";
WatcherXML.IncludeSubdirectories = false;
WatcherXML.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
WatcherXML.Filter = "*.xml";
WatcherXML.Created += new FileSystemEventHandler(Watcher_ChangedXML);
WatcherXML.EnableRaisingEvents = true;
}
private static void initPDF()
{
FileSystemWatcher WatcherPDF = new FileSystemWatcher();
WatcherPDF.Path = #"C:\xxx\baseDir\inbox";
WatcherPDF.IncludeSubdirectories = false;
WatcherPDF.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
WatcherPDF.Filter = "*.pdf";
WatcherPDF.Created += new FileSystemEventHandler(Watcher_ChangedPDF);
WatcherPDF.EnableRaisingEvents = true;
}
private static void initCSV()
{
FileSystemWatcher WatcherCSV = new FileSystemWatcher();
WatcherCSV.Path = #"C:\xxx\baseDir\inbox";
WatcherCSV.IncludeSubdirectories = false;
WatcherCSV.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
WatcherCSV.Filter = "*.csv";
WatcherCSV.Created += new FileSystemEventHandler(Watcher_ChangedCSV);
WatcherCSV.EnableRaisingEvents = true;
}
private static void Watcher_ChangedXML(object sender, FileSystemEventArgs e)
{
//move the XML stuff
string XMLPath = #"\\xxx\d$\CIP\Admin\Prod\SN\Import\Eingang\eSCHKG\";
if (File.Exists(e.FullPath))
// Ensure that the target does not exist.
if (File.Exists(Path.Combine(XMLPath,e.Name)))
File.Delete(Path.Combine(XMLPath, e.Name));
WaitReady(e.FullPath);
try
{
File.Move(e.FullPath, Path.Combine(XMLPath, e.Name));
}
catch (IOException ex)
{
eventWriteEx(ex, "XML");
}
}
private static void Watcher_ChangedPDF(object sender, FileSystemEventArgs e)
{
// move the PDF stuff
string PDFPath = #"\\xxx\d$\CIP\Admin\Prod\SN\Import\Eingang\eSCHKG\PDF\";
if (File.Exists(e.FullPath))
// Ensure that the target does not exist.
if (File.Exists(Path.Combine(PDFPath, e.Name)))
File.Delete(Path.Combine(PDFPath, e.Name));
WaitReady(e.FullPath);
try
{
File.Move(e.FullPath, Path.Combine(PDFPath, e.Name));
}
catch (IOException ex)
{
eventWriteEx(ex, "PDF");
}
}
private static void Watcher_ChangedCSV(object sender, FileSystemEventArgs e)
{
// move the CSV stuff
string CSVPath = #"\\xxx\d$\CIP\Admin\Prod\SN\Import\Eingang\eSCHKG\CSV\";
if (File.Exists(e.FullPath))
// Ensure that the target does not exist.
if (File.Exists(Path.Combine(CSVPath, e.Name)))
File.Delete(Path.Combine(CSVPath, e.Name));
WaitReady(e.FullPath);
try
{
WaitReady(e.FullPath);
File.Move(e.FullPath, Path.Combine(CSVPath, e.Name));
}
catch (Exception ex)
{
eventWriteEx(ex, "CSV");
}
}
private static void eventWriteEx(Exception ex, string what)
{
string eSource = "CIPCopyService";
if (!EventLog.SourceExists(eSource))
EventLog.CreateEventSource(eSource, "CIPEvents");
EventLog.WriteEntry("CIPCopy Exception" + what, ex.Message + "Trace" + ex.StackTrace, EventLogEntryType.Error);
}
private static void eventWriteInfo(string what)
{
string eSource = "CIPCopyService";
if (!EventLog.SourceExists(eSource))
EventLog.CreateEventSource(eSource, "CIPEvents");
EventLog.WriteEntry(eSource, what);
}
public static void WaitReady(string fileName)
{
while (true)
{
try
{
using (Stream stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
if (stream != null)
{
eventWriteInfo(string.Format("Output file {0} ready.", fileName));
break;
}
}
}
catch (FileNotFoundException ex)
{
eventWriteEx(ex, string.Format("Output file {0} not yet ready ({1})"));
}
catch (IOException ex)
{
eventWriteEx(ex, string.Format("Output file {0} not yet ready ({1})"));
}
catch (UnauthorizedAccessException ex)
{
eventWriteEx(ex, string.Format("Output file {0} not yet ready ({1})"));
}
Thread.Sleep(500);
}
}
protected override void OnStop()
{
}
}
}
FileSystemWatcher has a limitation if many files are touched in a short amount of time. The internal buffer of the FileSystemWatcher is full and any changes that occur until the buffer has free space can't get handled. You can increase the buffer to a maximum of 65536:
watcherXML.InternalBufferSize = 65536;
But even this has a limitation. If you expect really huge amounts of files you should probably use a Timer and move all existing files to the new location;
private static void initXML()
{
Timer timer = new Timer();
timer.Interval = 1000;
timer.Tick += (y,z) =>
{
foreach(string file in Directory.GetFiles(#"C:\xxx\baseDir\inbox")
{
MoveFile(file);
}
};
timer.Start();
}
private void MoveFile(string file)
{
string XMLPath = #"\\xxx\d$\CIP\Admin\Prod\SN\Import\Eingang\eSCHKG\";
string fileName = Path.GetFileName(file);
if (File.Exists(file))
// Ensure that the target does not exist.
if (File.Exists(Path.Combine(XMLPath,fileName )))
File.Delete(Path.Combine(XMLPath, fileName ));
WaitReady(file);
try
{
File.Move(file, Path.Combine(XMLPath, fileName));
}
catch (IOException ex)
{
eventWriteEx(ex, "XML");
}
}
Having a Form with only a comboBox:enter image description here
And a MyTest folder in drive D where you can find Folder1,Folder2,Folder3enter image description here
I want to watch any added .txt files in the folder MyTest and move them to the Folder1 if Folder1 is selected in the comboBox a.s.o.
public void CreateFileWatcher(string path)
{
FileSystemWatcher fsw = new FileSystemWatcher("D:\\MyTest");
fsw.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
fsw.Changed += new FileSystemEventHandler(OnChanged);
fsw.Created += new FileSystemEventHandler(OnChanged);
fsw.Deleted += new FileSystemEventHandler(OnChanged);
fsw.Error += new ErrorEventHandler(OnError);
fsw.EnableRaisingEvents = true;
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
}
private static void OnError(object source, ErrorEventArgs e)
{
Console.WriteLine("The FileSystemWatcher has detected an error");
if (e.GetException().GetType() == typeof(InternalBufferOverflowException))
{
Console.WriteLine(("The file system watcher experienced an internal buffer overflow: " + e.GetException().Message));
}
}
You can implement OnChanged event like below:
private void OnChanged(object sender, FileSystemEventArgs e)
{
string destFolder = Path.Combine(#"d:\", comboBox1.SelectedItem.ToString());
if (!Directory.Exists(destFolder))
{
Directory.CreateDirectory(destFolder);
}
string destFileName = Path.Combine(destFolder, new FileInfo(e.FullPath).Name);
try
{
File.Move(e.FullPath, destFileName);
}
catch (Exception ex)
{
Console.WriteLine("File move operation error:" + ex.Message);
}
}
this is how you move file as
string sourceFile = #"C:\Users\Public\public\test.txt";
string destinationFile = #"C:\Users\Public\private\test.txt";
// To move a file or folder to a new location:
System.IO.File.Move(sourceFile, destinationFile);
// To move an entire directory. To programmatically modify or combine
// path strings, use the System.IO.Path class.
System.IO.Directory.Move(#"C:\Users\Public\public\test\", #"C:\Users\Public\private");
}
I'm monitoring a Folder for File creation(Copied) event using FileSystem Watcher. I only want the program to process image Files.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(watcher_FileCreated);
watcher.Path = path;
So I try to create a Bitmap and avoid the file if an exception is thrown
private static void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
try
{
using (Bitmap test = new Bitmap(Bitmap.FromFile(e.FullPath)))
{
mytoprocesslist.add(e.FullPath);
}
//do my processing with image
Console.WriteLine(e.FullPath);
}
catch (Exception error)
{
Console.WriteLine("File Error");
}
}
This throws Out of Memory exception even when a valid image file is copied, which I think happens because the event was raised before the file was copied completely. How can I get over this? I only want to add the valid image files to a to do list and I will process these images one by one later.
A bit cleaner solution than a Try-Catch might be this one.
Im using this code without any exceptions raised.
private static bool IsImage(string path) {
try {
var result = false;
using (var stream = new FileStream(path, FileMode.Open)) {
stream.Seek(0, SeekOrigin.Begin);
var jpg = new List<string> { "FF", "D8" };
var bmp = new List<string> { "42", "4D" };
var gif = new List<string> { "47", "49", "46" };
var png = new List<string> { "89", "50", "4E", "47", "0D", "0A", "1A", "0A" };
var imgTypes = new List<List<string>> { jpg, bmp, gif, png };
var bytesIterated = new List<string>();
for (var i = 0; i < 8; i++) {
var bit = stream.ReadByte().ToString("X2");
bytesIterated.Add(bit);
var isImage = imgTypes.Any(img => !img.Except(bytesIterated).Any());
if (isImage) {
result = true;
break;
}
}
}
return result;
} catch (UnauthorizedAccessException) {
return false;
}
}
Usage of code
foreach (var file in Directory.EnumerateFiles(#"pathToFlowersFolder"))
{
Console.WriteLine($"File: {file} Result:{IsImage(file)}");
}
Edit
After playing around i got an IO-Exception (File already in use)
After reading this i'd offer you the following solution:
private void button1_Click(object sender, EventArgs e)
{
var watcher = new FileSystemWatcher();
watcher.Created += new FileSystemEventHandler(fileSystemWatcher1_Changed);
watcher.Path = #"c:\temp";
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
watcher.EnableRaisingEvents = true;
}
private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
Thread.Sleep(100); // <- give the Creator some time. Increase value for greate pause
if (IsImage(e.FullPath))
{
Console.WriteLine("success----------->" + e.FullPath);
}
}
Note
This piece of code properly works on my machine. My HDD is an SSD, so you might need to increase the thread-sleeping time. It properly works for all images (jpg, bmp, gif, png) up to a size of 7 Mb (im quite sure and greater).
If this code doesnt works for you, please post the exception rather than uploading your code.
For the first requirement: "I only want the program to process image files"
private static void fileSystemWatcher1_Changed(object sender, FileSystemEventArgs e)
{
string strFileExt = getFileExt(e.FullPath);
// filter file types
if (Regex.IsMatch(strFileExt, #"\.png|\.jpg", RegexOptions.IgnoreCase))
{
//here Process the image file
}
}
For the second requirement: "Out of Memory Exception"
Here what happens is, when the file is created (only file name and some attributes) the system is calling the created event. Then the file changed event is also called
So you have to do the processing in the changed event. Also to prevent duplicate calling you have to add a filter to your watcher.
The following is the complete code.
private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
FileInfo fileInfo = new FileInfo(e.FullPath);
string strFileExt = fileInfo.Extension;
// filter file types
if (Regex.IsMatch(strFileExt, #"\.png|\.jpg", RegexOptions.IgnoreCase))
{
//here Process the image file
try
{
using (Bitmap test = new Bitmap(Bitmap.FromFile(e.FullPath)))
{
//Do your code here.
}
}
catch (Exception error)
{
Console.WriteLine("File Error");
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
fileSystemWatcher1.Path = #"C:\Users\Christlin\Desktop\res";
//To Prevent duplicated calling of changed event
fileSystemWatcher1.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
}
I am trying to monitor a directory for new files in it with the FileSystemWatcher Class.
My Problem is that the event doesn't get triggered. My watcher class is:
public class Filewatcher
{
private FileSystemWatcher watcher;
public Filewatcher(string path, string filefilter)
{
this.watcher = new FileSystemWatcher();
this.watcher.Path = path;
this.watcher.NotifyFilter = NotifyFilters.FileName; //| NotifyFilters.LastWrite | NotifyFilters.LastAccess
this.watcher.Filter = filefilter;
this.watcher.Created += new FileSystemEventHandler(OnChanged);
}
public void start_watching()
{
//this.watcher.IncludeSubdirectories = true;
this.watcher.EnableRaisingEvents = true;
Console.WriteLine("Press Enter to quit\r\n");
Console.ReadLine();
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
//FileInfo objFileInfo = new FileInfo(e.FullPath);
//if (!objFileInfo.Exists) return;
Console.WriteLine("got the change\r\n");
global_functions.perform_action(Global_Vars.thereader.read(e.FullPath), Global_Vars.thepattern);
}
}
Operating System is Win 7 Prof x64
You don't make a call to start_watching(). Add a call to that and it may work better.