How to check if file opened? - c#

I wrote program and need my own file watcher (loop that checks if file can be opened). Something like this:
while (loadedFiles.Count > 0 || isNeedReRead)
{
Thread.Sleep(1000);
if (isNeedReRead)
ReadFromRegistry();
foreach (var file in loadedFiles)
{
if (!IsFileLocked(file.Value))
{
// logic
}
}
}
Source: Is there a way to check if a file is in use?
Here is my solution:
try
{
using (Stream stream = new FileStream(
path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
stream.Close();
return false;
}
}
catch (IOException)
{
return true;
}
It works fine with Word, Excel. But if the process does not lock the file, this method doesn't help. For example if an open bitmap file is changing, IsFileLocked returns false.
Any ideas?

You can setup monitoring the file by using the System.IO.FileSystemWatcher you should be able to use that the NotifyFilter property (set to LastAccessTime) to detect when a particular file was last accessed.
void SetupWatcher()
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"C:\";
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess;
// 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);
}
Another option assuming this is windows is to enumerate the list of open file handles for each process. The code posted here has a decent implementation so all you have to do is call
DetectOpenFiles.GetOpenFilesEnumerator(processID);
However, if a process opens a file reads the contents into memory then closes the file, you will be stuck with the monitoring option (listed above), since the process does not actually have the file open any longer once it is read into memory.

Related

Proper way to Implement a FileWatcher in a multithreading program C#

I have a multi-threaded program (3-4 threads). All the threads depend on a couple of parameters which are specified in an XML file.
Since the parameters in the XML file may be changed at any time by a user therefore, the different threads need to be notified about it and need to get the updated copy of parameters.
To monitor the changes in the XML file, I am using a FileWatcher as per the MSDN documentation.
clas ReadXML
{
//parameters
private static string Param1 = "";
private static string Param2 = "";
public static void ReadXmlParameters()
{
XmlDocument xDoc = new XmlDocument();
try
{
xDoc.Load(_ParameterFileDirrectory + #"\" + _ParameterFileDirrectory);
//parameters
Param1 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/IpAddress")).InnerText;
Param2 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/Username")).InnerText;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void CreateXMLWatcher()
{
try
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = _ParameterFileDirrectory;
/* 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 .xml files.
watcher.Filter = _ParameterFileFilename; // "ParameterFile.xml";
// 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;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 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);
if (e.ChangeType.ToString() == "Changed")
{
ReadXmlParameters(); //Read the Parameters from XML again
MyThreadClass1._waitTillParametersChange.Set(); //Notifying the thread that the parameters might have chnaged
}
}
}
The above implementation is working fine for me. I have to start the FileWatcher from the Main() using the following lines:
public static void Main()
{
ReadXml.ReadXmlParameters();
ReadXml.CreateXMLWatcher();
// Start other threads now
}
and then I start my other threads.
QUESTION: Since with the above-mentioned implementation, I have got Static methods and variables in my program so, I am wondering if this is the proper (at least acceptable) implementation of a FileWatcher or should I try to get rid of these static things by implementing ReadXml as a singleton class (or providing the same object to all the thread classes).

Delete file using file watcher not allowing me to delete second time

My task is to delete file once the processing is completed . I am using FileWatcher to complete this task. It is watching specific folder . Suppose If i copy one file and put that in filewatcher folder it is deleting. Second time when i copy the same file and paste that in the same watching folder. This time it says that Another process is using that file . and exception is throwing . I think i am missing something. Here is my code
private static void Main(string[] args)
{
var fw = new FileSystemWatcher(EmailSetting.DataFolder)
{
IncludeSubdirectories = false
,
EnableRaisingEvents = true
};
fw.Created += (sender, e) =>
{
File.Delete(e.FullPath);
};
Console.ReadLine();
}
You receive the Created event when the file was created (hence the name). But at this point in time the other process that is actually creating it, didn't finish writing content into that file. So the file might be already there, but the other is still working on it (imagine you would copy a 8 GB file).
It would be wiser to simply write the path of the file into a list within the event and let another thread regularly check this concurrent bag (e.g. once a second). First it checks if the file exists and if yes, try to delete it. If succeeded, remove it from the bag, otherwise try again next time.
Code example
private static readonly ConcurrentQueue<FileInfo> _FileCandidates = new ConcurrentQueue<FileInfo>();
private static void Main(string[] args)
{
var watcher = new FileSystemWatcher
{
Path = #"R:\TestFolder",
IncludeSubdirectories = false,
Filter = "*.*",
};
Console.WriteLine("Start watching folder... " + watcher.Path);
watcher.Created += OnFileCreated;
watcher.EnableRaisingEvents = true;
var timer = new Timer
{
AutoReset = true,
Interval = 1000,
};
timer.Elapsed += OnTimerElapsed;
timer.Enabled = true;
Console.ReadKey();
}
static void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
FileInfo file;
var stillInUseFiles = new List<FileInfo>();
Console.WriteLine("Check for file candidates...");
while (_FileCandidates.TryDequeue(out file))
{
try
{
Console.WriteLine("Delete " + file.FullName);
if (file.Exists)
file.Delete();
}
catch (IOException)
{
Console.WriteLine("Could not delete file, try again next time.");
stillInUseFiles.Add(file);
}
}
foreach (var unhappyFile in stillInUseFiles)
{
_FileCandidates.Enqueue(unhappyFile);
}
}
static void OnFileCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Found new file candidate " + e.FullPath);
_FileCandidates.Enqueue(new FileInfo(e.FullPath));
}

C# FileSystemWatcher Copy folder complete

I am using FileSystemWatcher to monitor a folder that will be used to do some file renaming.
The only thing that will be copied will be folders containing files. There will not be single files put into the monitored folder. This is the code for setting up the FileSystemWatcher
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
watcher.IncludeSubdirectories = true;
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Renamed += new RenamedEventHandler(watcher_Renamed);
watcher.EnableRaisingEvents = true;
There doesn't seem to be any issues with this setup..
The folders being copied can be between 50-200mb big. Is there a way to check/make sure that all the files have completed copying before starting the renaming process.
I tried this thinking that i would get an IOException if the copying was still happening when the GetFiles() was called.
bool finishedCopying = false;
while (!finishedCopying)
{
try
{
List<FileInfo> fileList = directoryInfo.GetFiles().ToList();
AlbumSearch newAlbum = new AlbumSearch(directoryInfo);
return newAlbum;
}
catch (IOException)
{
finishedCopying = false;
}
}
If anymore information is required, just ask an i can provide.
Ta.
I gave this a go using a timer. It may not be the prettiest solution out there but at first testing it seems to be working so far. Essentially what this does is when a folder is copied to the monitored folder it will add the folder path to the AlbumList. The files in that folder will trigger the Created event. This waits for the file to finish copying. Once finished it starts a timer. If a new Created event gets triggered the timer will reset itself.
When the timer.elapsed event is triggered it assumes (and I know assumption is the mother of all f*&k ups) that there are no more files to be copied and can start to process the fully copied folder..
System.Timers.Timer eventTimer = new System.Timers.Timer();
List<string> AlbumList = new List<string>();
private void watcher_Created(object sender, FileSystemEventArgs e)
{
if (Directory.Exists(e.FullPath))
{
AlbumList.Add(e.FullPath);
}
if (File.Exists(e.FullPath))
{
eventTimer.Stop();
FileInfo newTrack = new FileInfo(e.FullPath);
while (IsFileLocked(newTrack))
{
// File is locked. Do Nothing..
}
eventTimer.Start();
}
}
private void eventTimer_Elapsed(object sender, ElapsedEventArgs e)
{
List<string> ItemToRemove = new List<string>();
foreach (var item in AlbumList)
{
DirectoryInfo di = new DirectoryInfo(item);
AlbumSearch newAlbum = new AlbumSearch(di);
if (DoSomethingMethod(newAlbum))
{
ItemToRemove.Add(item);
}
else
{
// why did it fail
}
}
foreach (var path in ItemToRemove)
{
AlbumList.Remove(path);
}
}
private bool DoSomethingMethod(AlbumSearch as)
{
// Do stuff here
return true;
}
This is a small demo app that check files at the beginning, and then uses two hashsets to track copied files. This will only work if the source directory is known. There is no way to know if a file was created from a file copy or from a direct creation, so you can only compare two known directories with Directory.GetFiles. And, as already said in comments, you will still have to check if during the copy process, other files were added / removed / renamed in the old directory
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static HashSet<string> oldDirFiles = new HashSet<string>();
static HashSet<string> newDirFiles = new HashSet<string>();
static string oldDir = "C:\\New Folder";
static string newDir = "C:\\New Folder 2";
static System.Threading.ManualResetEvent resetEvent = new System.Threading.ManualResetEvent(false);
static void Main(string[] args)
{
System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher();
watcher.Path = newDir;
watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
watcher.IncludeSubdirectories = true;
watcher.Filter = "*.*";
watcher.Created += watcher_Created;
watcher.Changed += watcher_Changed;
watcher.Renamed += watcher_Renamed;
watcher.EnableRaisingEvents = true;
//get all files in old directory
var oldFiles = Directory.GetFiles(oldDir, "*.*", SearchOption.AllDirectories);
foreach (var file in oldFiles)
oldDirFiles.Add(file);
resetEvent.WaitOne();
//now launch the directory copy
//then you have to check if in the meaning time, new files were added or renamed
//that could be done also with a watcher in the old directory
}
static void watcher_Renamed(object sender, RenamedEventArgs e)
{
throw new NotImplementedException();
}
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
throw new NotImplementedException();
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
//check if the copied file was in the old directory before starting
if (oldDirFiles.Contains(e.FullPath.Replace(newDir, oldDir)))
{
newDirFiles.Add(e.FullPath);
//if all the files have been copied, the file count will be the same in the two hashsets
//the resetevent.Set() signal the waiting thread and the program can proceed
if (newDirFiles.Count == oldDirFiles.Count)
resetEvent.Set();
}
}
}
}

i want to check file exist in folder continously in c# [duplicate]

This question already has answers here:
Notification when a file changes?
(3 answers)
Closed 8 years ago.
I have requirement to process file as soon as someone put the file in ftp location
and i want to create c# code on windows server
Thanks in advance
You need to use FileSystemWatcher.
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);
}
}
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
http://www.codeproject.com/Articles/26528/C-Application-to-Watch-a-File-or-Directory-using-F

How come FileSystemWatcher doesn't fire events, but does with SMSTrace on?

I create a FileSystemWatcher object to monitor a log file. It's initialized to listen to all possible events (lastwrite, lastaccess, etc..) but it doesn't fire events when the file is being written to.
however, if I open SMSTrace and listen to that file with it (and clearly see that the file is updated constantly) the filesystemwatcher DOES fire events.
What does SMSTrace do to the file?
how can this be explained and how can I fix it?
This is the code:
private FileSystemWatcher fileWatcher;
private FileStream fileStream;
private StreamReader streamReader;
private String fullPath = null;
private String dir = null;
private String fileName = null;
private void selectLogFile()
{
// TODO: try to restore previous settings before asking the user for the log file
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "C:\\";
openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
fullPath = openFileDialog.FileName;
dir = Path.GetDirectoryName(fullPath);
fileName = Path.GetFileName(fullPath);
}
}
// TODO: what to do when file erases? (reboot) - clear the window?
public LogListener()
{
try
{
Thread selectFileThread = new Thread(new ThreadStart(selectLogFile));
selectFileThread.SetApartmentState(ApartmentState.STA);
selectFileThread.Start();
selectFileThread.Join();
// The user did not select a file - nothing to do
if (fullPath == null)
{
return;
}
// Create file listener to listen on changes to log
fileWatcher = new FileSystemWatcher(dir);
// Create a file stream to read the data from the log file
fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Create a stream reader from the fileStream to read text easily
streamReader = new StreamReader(fileStream);
// Watch for changes in LastWrite
fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
// Only watch the log file.
fileWatcher.Filter = fileName;
// Add event handlers.
fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
// Initial syncing of the file
readFile();
// Begin watching for events
fileWatcher.EnableRaisingEvents = true;
Log.Add("Started");
}
catch (Exception e)
{
Log.Add("Exception: " + e.Message);
}
}
void OnChanged(object source, FileSystemEventArgs e)
{
readFile();
}
public void readFile()
{
String line;
String bytesString = "";
Log.Add(DateTime.Now+":readFile()...");
// Some more code here...
}
This is a long shot, since you haven't provided any code, but...
Did you remember to set the EnableRaisingEvents property to true on your FileSystemWatcher? e.g.:
var w = new FileSystemWatcher("path");
w.Created += DoSomething;
w.Changed += DoSomething;
w.EnableRaisingEvents = true; // No events raised until you do this!
EDIT: OK, you're already doing this. Please disregard this answer, I have no idea what's going on :P
Something to look for is your code removing, then re-adding the folder you're watching after you created the flesystemwatcher. If it does that, then the watcher becomes invalid and you don't get any events raised.
I had a bug in my code doing just that today and figured it out after reading this post.

Categories

Resources