Monitor when a file is in use - c#

I need to monitor when a file (*.wav) is being executed.
I know about the filesystemwatcher class. However does this class notify when a file is being used? I also came across Monitor when an exe is launched. While I realise that this has to do with an exe, is there a way to be notified that a wav file has been executed?

No, you can't get an Event when non executable files such as mp3, wav, txt and etc are opened.
But actually can check if file is opened write now...
static void Main(string[] args)
{
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.DoWork += Bw_DoWork;
bw.RunWorkerAsync();
}
Console.WriteLine("Press Q to exit");
while (Console.ReadKey(true).Key!=ConsoleKey.Q){}
}
private static bool[] opened;
private static void Bw_DoWork(object sender, DoWorkEventArgs e)
{
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.mp3");
opened = new bool[files.Length];
while (true)
for (int i = 0; i < files.Length; i++)
try
{
using (var fs = File.Open(files[i], FileMode.Open, FileAccess.Read, FileShare.None))
opened[i] = false;
}
catch{ opened[i] = true; }
}
P.S. I know it's ugly :D

Related

fileSystemWatcher doesn't work when watching for created zip files

I have been using fileSystemWatcher for c# to track when a zip file has been downloaded, the problem is that the name of the file never gets output to result.Name. Why is this??
public ResultsDetails ExportCallsToCsv()
{
var downloadsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + #"\Downloads";
ExportToCsvButton.Click();
using (var watcher = new FileSystemWatcher(downloadsDirectory))
{
watcher.Filter = "*.zip";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
CsvFilePath = $#"{downloadsDirectory}\{result.Name}".Replace("_", "-");
}
return new ResultsDetails(ActionResult.Passed, "Csv file created succesfully.");
}
Why does result.Name never get filled? I've attempted to put an endless while loop inside the using block which breaks when result.Name gets filled, but to still no avail. It just halts forever when I do that.
FileSystemWatcher.WaitForChanged is a synchronous/blocking method and is unable to capture the file change that was triggered on the same thread.
Here's an example of your code in a small test program. This will not work (as you pointed out).
static void Main(string[] args)
{
var directory = "C:\\test";
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
using (var watcher = new FileSystemWatcher(directory))
{
watcher.Filter = "*.txt";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
if (result.Name is null)
{
// This is always printed.
Console.WriteLine("No name");
}
}
Console.ReadLine();
}
Here's a small sample program that does work. This version handles the FileSystemWatcher events asynchronously.
static void Main(string[] args)
{
var directory = "C:\\test";
using (var watcher = new FileSystemWatcher(directory, "*.txt"))
{
watcher.EnableRaisingEvents = true; // *** Be sure to include this!
watcher.Created += Watcher_Created; // *** This is different, too.
File.Delete("C:\\test\\new_file.txt");
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
}
Console.WriteLine("All done.");
Console.ReadLine();
}
private static void Watcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Detected {e.Name} is {e.ChangeType}.");
}

How to determine whether a folder has finished copying c#

I have a question: How do I determine whether a folder has finished copying from one location to another?
At the moment my FileSystemWatcher triggers several events as soon as a file within the directory being copied. What I want though, is one single event to be triggered when all the files within that folder has been successfully copied. My code right now looks like this:
static void Main(string[] args)
{
String path = #"D:\Music";
FileSystemWatcher mWatcher = new FileSystemWatcher();
mWatcher.Path = path;
mWatcher.NotifyFilter = NotifyFilters.LastAccess;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
mWatcher.IncludeSubdirectories = true;
mWatcher.Created += new FileSystemEventHandler(mLastChange);
mWatcher.Changed += new FileSystemEventHandler(mLastChange);
mWatcher.EnableRaisingEvents = true;
Console.WriteLine("Watching path: " + path);
String exit;
while (true)
{
exit = Console.ReadLine();
if (exit == "exit")
break;
}
}
private static void mLastChange(Object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + " " + e.FullPath);
}
Unfortunately FileSystemWatcher doesn't tell you when a file is finished writing. So your options are...
Set a timeout after last write when it is assumed there are no more changes coming
Have the writing application put a lock file of some variety that tells any other program that it's done.
After re-reading your question... it doesn't sound like you have any control over the other application.
So you will need some kind of timeout value that determines when all the writing is done. Basically create a timer that resets after each filesystemwatcher event... when it times out then you fire the single event that says it's done.
Here is how you could add it to your code...
static void Main(string[] args)
{
Timer.Interval = 5000; // 5 seconds - change to whatever is appropriate
Timer.AutoReset = false;
Timer.Elapsed += TimeoutDone;
String path = #"D:\Music";
FileSystemWatcher mWatcher = new FileSystemWatcher();
mWatcher.Path = path;
mWatcher.NotifyFilter = NotifyFilters.LastAccess;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
mWatcher.IncludeSubdirectories = true;
mWatcher.Created += new FileSystemEventHandler(mLastChange);
mWatcher.Changed += new FileSystemEventHandler(mLastChange);
mWatcher.EnableRaisingEvents = true;
Console.WriteLine("Watching path: " + path);
Timer.Start();
String exit;
while (true)
{
exit = Console.ReadLine();
if (exit == "exit")
break;
}
}
private static Timer Timer = new Timer();
private static void TimeoutDone(object source, ElapsedEventArgs e)
{
Console.WriteLine("Timer elapsed!");
}
private static void mLastChange(Object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + " " + e.FullPath);
if (Timer != null)
{
Timer.Stop();
Timer.Start();
}
}
It's horribly cheesy, but in the past I have dealt with this problem by creating a custom decorator for the FileSystemWatcher class. Internally it creates a FileSystemWatcher and registers for the Created and Changed events, wraps them, and throws its own Created and Changed events after the files are finished, similar to this:
private void Watcher_Changed(Object sender, FileSystemEVentArgs e)
{
while (true)
{
FileStream stream;
try
{
stream = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
// If this succeeds, the file is finished
Changed();
}
catch (IOException)
{
}
finally
{
if (stream != null) stream.Close();
}
}
}
Some of this is drawn from the answer here. In reality, you shouldn't use an infinite loop. You probably want to add timeouts, sleep in between checks, etc. but this is the general idea.
If the destination is a local folder, you can use the filesystem filter driver to track file create and file close events. Knowing when all files, previously created, are closed will inform you that copying is complete.
I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done.
Download FileSystemSafeWatcher and add it to your project.
Then use it as a normal FileSystemWatcher and monitor when the events are triggered.
var fsw = new FileExamSystemWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;
Unfortunatly there is no ready solution. but I designed a simple tricky solution to trigger (copy finished) event.
you should use timer.
FileSystemWatcher fsw = new FileSystemWatcher();
string fullPath = "";
DateTime tempTime;
fsw.Path = #"C:\temp";
private void startwatching()
{
timer1.Start();
}
fsw.EnableRaisingEvents = true;
fsw.Created += Fsw_Created;
private void Fsw_Created(object sender, FileSystemEventArgs e)
{
tempTime = DateTime.Now.AddSeconds(-4);
fullPath = e.FullPath;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (fullPath!=string.Empty)
{
timer1.Stop();
if (tempTime >= Directory.GetLastAccessTime(fullPath))
{
DirectoryInfo di = new DirectoryInfo(fullPath);
listBox1.Items.Add("Folder " + di.Name + " finished copying");
fullPath = string.Empty;
}
else
{
tempTime = DateTime.Now;
}
timer1.Start();
}
}

Is file being copied right now? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - from time to time check if a file exists and read from it
I'm using file watcher to check if a file was craeted in a directory. If it was, then I want to open it and remove to another directory.
My approach is also to use FileShare.None to an ensure exclusive access. What I did is:
class Program
{
private static void Main(string[] args)
{
FileSystemWatcher fileWatcher = new FileSystemWatcher();
fileWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
fileWatcher.Created += fileWatcher_Created;
fileWatcher.EnableRaisingEvents = true;
Console.ReadLine();
}
private static void fileWatcher_Created(object sender, FileSystemEventArgs e)
{
WorkOnFile(e.FullPath);
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
}
}
}
The bottom line is, that it necessary to do WorkOnFile() completely and only one time for one file. If a file being copied, then I need to call WorkOnFile() again until it has completely been copied and none process is using it.
How do I reach it?
If you need that file moved as fast as possible, you could try using FileWatcher to monitor 'LastWrite' and after a small delay, trying to move the file.
I had to do something very similar, but there was no hurry, just important that the file was moved reliably. So I instead used a System.Timers.Timer to run once a minute:
var scanDirectoryIn = new DirectoryInfo(folderIn);
foreach (var fileInfo in scanDirectoryIn.GetFiles())
{
if (fileInfo.Extension != ".csv") continue;
if (DateTime.UtcNow.Subtract(fileInfo.LastWriteTimeUtc).TotalMinutes < 5) continue;
try
{
fileInfo.MoveTo(folderOut + "\\" + fileInfo.Name);
}
catch (Exception) {}
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
while(true){
try{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
break; //exit while() infinite loop
}
}
catch(Exception e){
//file is locked because being written. wait a few seconds then retry
Thread.Sleep(10000);
}
}
}

C# IOException: The process cannot access the file because it is being used by another process

I have a slight problem. What my application is supose to do, is to watch a folder for any newly copied file with the extention '.XSD' open the file and assign the lines to an array. After that the data from the array should be inserted into a MySQL database, then move the used file to another folder if it's done.
The problem is that the application works fine with the first file, but as soon as the next file is copied to the folder I get this exception for example: 'The process cannot access the file 'C:\inetpub\admission\file2.XPD' because it is being used by another process'.
If two files on the onther hand is copied at the same time there's no problem at all.
The following code is on the main window:
public partial class Form1 : Form
{
static string folder = specified path;
static FileProcessor processor;
public Form1()
{
InitializeComponent();
processor = new FileProcessor();
InitializeWatcher();
}
static FileSystemWatcher watcher;
static void InitializeWatcher()
{
watcher = new FileSystemWatcher();
watcher.Path = folder;
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.EnableRaisingEvents = true;
watcher.Filter = "*.XPD";
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
processor.QueueInput(e.FullPath);
}
}
As you can see the file's path is entered into a queue for processing which is on another class called FileProcessor:
class FileProcessor
{
private Queue<string> workQueue;
private Thread workerThread;
private EventWaitHandle waitHandle;
public FileProcessor()
{
workQueue = new Queue<string>();
waitHandle = new AutoResetEvent(true);
}
public void QueueInput(string filepath)
{
workQueue.Enqueue(filepath);
if (workerThread == null)
{
workerThread = new Thread(new ThreadStart(Work));
workerThread.Start();
}
else if (workerThread.ThreadState == ThreadState.WaitSleepJoin)
{
waitHandle.Set();
}
}
private void Work()
{
while (true)
{
string filepath = RetrieveFile();
if (filepath != null)
ProcessFile(filepath);
else
waitHandle.WaitOne();
}
}
private string RetrieveFile()
{
if (workQueue.Count > 0)
return workQueue.Dequeue();
else
return null;
}
private void ProcessFile(string filepath)
{
string xName = Path.GetFileName(filepath);
string fName = Path.GetFileNameWithoutExtension(filepath);
string gfolder = specified path;
bool fileInUse = true;
string line;
string[] itemArray = null;
int i = 0;
#region Declare Db variables
//variables for each field of the database is created here
#endregion
#region Populate array
while (fileInUse == true)
{
FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read,
FileShare.ReadWrite);
StreamReader reader = new StreamReader(fs);
itemArray = new string[75];
while (!reader.EndOfStream == true)
{
line = reader.ReadLine();
itemArray[i] = line;
i++;
}
fs.Flush();
reader.Close();
reader.Dispose();
i = 0;
fileInUse = false;
}
#endregion
#region Assign Db variables
//here all the variables get there values from the array
#endregion
#region MySql Connection
//here the connection to mysql is made and the variables are inserted into the db
#endregion
#region Test and Move file
if (System.IO.File.Exists(gfolder + xName))
{
System.IO.File.Delete(gfolder + xName);
}
Directory.Move(filepath, gfolder + xName);
#endregion
}
}
The problem I get occurs in the Populate array region. I read alot of other threads and was lead to believe that by flushing the file stream would help...
I am also thinking of adding a try..catch for if the file process was successful, the file is moved to gfolder and if it failed, moved to bfolder
Any help would be awesome
Tx
You're not disposing of your FileStream instance, so a lock remains on the file. Change your code to use using blocks:
using (var fileStream = new FileStream(...))
{
using (var reader = new StreamReader(fileStream))
{
}
}
These using blocks will ensure the instances are correctly disposed of.
Also, why are you calling Flush on the file stream? You're not writing anything with it...
I would suggest :
1° use the using syntax on StreamReader
2° use the using syntax on FileStream

How to lock file

please tell me how to lock file in c#
Thanks
Simply open it exclusively:
using (FileStream fs =
File.Open("MyFile.txt", FileMode.Open, FileAccess.Read, FileShare.None))
{
// use fs
}
Ref.
Update: In response to comment from poster: According to the online MSDN doco, File.Open is supported in .Net Compact Framework 1.0 and 2.0.
FileShare.None would throw a "System.IO.IOException" error if another thread is trying to access the file.
You could use some function using try/catch to wait for the file to be released. Example here.
Or you could use a lock statement with some "dummy" variable before accessing the write function:
// The Dummy Lock
public static List<int> DummyLock = new List<int>();
static void Main(string[] args)
{
MultipleFileWriting();
Console.ReadLine();
}
// Create two threads
private static void MultipleFileWriting()
{
BackgroundWorker thread1 = new BackgroundWorker();
BackgroundWorker thread2 = new BackgroundWorker();
thread1.DoWork += Thread1_DoWork;
thread2.DoWork += Thread2_DoWork;
thread1.RunWorkerAsync();
thread2.RunWorkerAsync();
}
// Thread 1 writes to file (and also to console)
private static void Thread1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 20; i++)
{
lock (DummyLock)
{
Console.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " - 3");
AddLog(1);
}
}
}
// Thread 2 writes to file (and also to console)
private static void Thread2_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 20; i++)
{
lock (DummyLock)
{
Console.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " - 4");
AddLog(2);
}
}
}
private static void AddLog(int num)
{
string logFile = Path.Combine(Environment.CurrentDirectory, "Log.txt");
string timestamp = DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss");
using (FileStream fs = new FileStream(logFile, FileMode.Append,
FileAccess.Write, FileShare.None))
{
using (StreamWriter sr = new StreamWriter(fs))
{
sr.WriteLine(timestamp + ": " + num);
}
}
}
You can also use the "lock" statement in the actual writing function itself (i.e. inside AddLog) instead of in the background worker's functions.

Categories