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);
}
}
}
Related
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
This question already has answers here:
Wait until file is unlocked in .NET
(16 answers)
Closed 7 years ago.
private void DisplayLastTakenPhoto()
{
string mypath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),"RemotePhoto");
var directory = new DirectoryInfo(mypath);
var myFile = directory.EnumerateFiles()
.Where(f => f.Extension.Equals(".jpg", StringComparison.CurrentCultureIgnoreCase) || f.Extension.Equals("raw", StringComparison.CurrentCultureIgnoreCase))
.OrderByDescending(f => f.LastWriteTime)
.First();
LiveViewPicBox.Load(myFile.FullName);
}
protected virtual bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
The problem is with the line:
LiveViewPicBox.Load(myFile.FullName);
Sometimes it's working fine sometimes i'm getting exception on this line say the file is in use by another process.
So i want to use the IsFileLocked method or some other method to check untill the file is not locked.
But if i will call this method before the line
LiveViewPicBox.Load(myFile.FullName);
It will check if the file locked only once. I need somehow to use while or somet other way to check if the file is locked over and over again until it's unlocked.
And only when it's unlocked to make the line LiveViewPicBox.Load(myFile.FullName);
public static bool IsFileReady(String sFilename)
{
// If the file can be opened for exclusive access it means that the file
// is no longer locked by another process.
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
if (inputStream.Length > 0)
{
return true;
}
else
{
return false;
}
}
}
catch (Exception)
{
return false;
}
}
Place this in a loop and wait for it to return true.
First the watch method; I need to watch for any newly created jpg files, since I don't know yet the file names. My program creates each time a new jpg in the directory specified by a TextBox. So my first problem is how to know/get the file name when it's being created?
Second problem, how can I use all these methods, the two methods and the event changed (code below)? I have a button click event when I click it, it will create the new jpg file. Then in the button click event I want to start watching it and give a message on a label something like: "Creating file wait", then when the file is created and ready for use "File created".
private void watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = SavePathTextBox.Text;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.jpg";
watcher.Changed += watcher_Changed;
watcher.EnableRaisingEvents = true;
}
Then the event watcher_Changed:
void watcher_Changed(object sender, FileSystemEventArgs e)
{
}
And the method that checks if the file is locked or not
public static bool IsFileReady(String sFilename)
{
// If the file can be opened for exclusive access it means that the file
// is no longer locked by another process.
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
if (inputStream.Length > 0)
{
return true;
}
else
{
return false;
}
}
}
catch (Exception)
{
return false;
}
}
This is what i tried:
In the button click event:
private void TakePhotoButton_Click(object sender, EventArgs e)
{
try
{
if ((string)TvCoBox.SelectedItem == "Bulb") CameraHandler.TakePhoto((uint)BulbUpDo.Value);
else CameraHandler.TakePhoto();
watch();
}
catch (Exception ex) { ReportError(ex.Message, false); }
}
In the watch method:
private void watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = SavePathTextBox.Text;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.JPG";
watcher.Changed += watcher_Changed;
watcher.EnableRaisingEvents = true;
}
The event watcher_Changed
void watcher_Changed(object sender, FileSystemEventArgs e)
{
if (IsFileReady(e.FullPath) == false)
{
this.Invoke((Action)delegate { label6.Text = "Busy"; });
}
else
{
this.Invoke((Action)delegate { label6.Text = "File Ready"; });
}
}
And the method to find if file is locked or not:
public static bool IsFileReady(String sFilename)
{
// If the file can be opened for exclusive access it means that the file
// is no longer locked by another process.
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
if (inputStream.Length > 0)
{
return true;
}
else
{
return false;
}
}
}
catch (Exception)
{
return false;
}
}
The problem is that sometimes in most of the cases it's getting to the line inside the watcher_Changed event:
this.Invoke((Action)delegate { label6.Text = "File Ready"; });
And making this line twice or somtimes even 3 times in a row.
I can say that each click my camera take one photo and it's creating two files one for example with the name: IMG_0001.CR2 and the Jpg one: IMG_0001.JPG
But i'm not sure if that's why it's getting to the event and doing the line/s there more then once.
I also checked the file in the e.FullPath is always .jpg and never cr2.
The question is why it's getting there more then once and how can i make sure that the file is really ready ? ("File Ready")
Maybe i need somehow to track the file size from 0kb until the size not change any more and then to decide in the event that it's ready or not ?
I see some problems with the way You use watcher.
Watcher should run before You call CameraHandler.TakePhoto or there is a (small) chance You would miss it.
Either do not create new instance of watcher on every TakePhotoButton_Click or stop the old one. Otherwise You would end up with new running watcher with every click and get as many calls to watcher_Changed as many watchers You have.
I have read there is a chance watcher can be garbage collected. I am not sure if it is true, but rather be safe and save it to some local field.
What You are now waiting for is end of writing to some .jpg file. This may do what You need and in that case it is good. But if You want to wait for file create, You need different setting. This worked for me:
string watchDir = Application.StartupPath;
watcher = new FileSystemWatcher(watchDir, "*.jpg");
watcher.NotifyFilter |= NotifyFilters.Size;
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.EnableRaisingEvents = true;
protected void watcher_Created(object sender, FileSystemEventArgs e)
{
string changeType = e.ChangeType.ToString();
if (changeType != "Created")
{
return;
}
// file is created, wait for IsFileReady or whatever You need
}
I have built a small tray app that will watch a folder and when a new file is added it runs a job. The job is to watch for video files and convert them to .mp4 using handBrakeCli. I have all this logic worked out. The problem I run into is that if there is more than one file I want it to queue the job til the prior one is complete. I am fairly new to c# and I am not sure of the best way to handle this.
one idea is to create a queue somehow, a file to store the commands in order maybe, then execute the next one after the process is complete. We are dealing with large movie files here so it can take a while. I am doing this on a quad core with 8gb of RAM and it seems to generally take about 30mins to complete a full length movie. I just dont know how to do this.
here is the code I have so far. there are some bits in here that are for future functionality so it refers to some classes that you wont see but it doesnt matter as they aren't used here. any suggestions are welcome.
public void Watcher()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = textBox1.Text + "\\"; //path to watch
watcher.Filter = strfilter; //what types to look for set to * and i will filter later as it cant accept an array
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; //properties to look at
watcher.IncludeSubdirectories = true; //scan subdirs
watcher.Created += new FileSystemEventHandler(OnChanged);
//TODO: make this only run if the files are of a certain type
watcher.EnableRaisingEvents = true; // start the watcher
}
static bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
// Define the event handlers.
private void OnChanged(object source, FileSystemEventArgs e)
{
string sFile = e.FullPath;
//check that file is available
FileInfo fileInfo = new FileInfo(sFile);
while (IsFileLocked(fileInfo))
{
Thread.Sleep(500);
}
if (System.Diagnostics.Process.GetProcessesByName("HandBrakeCLI").Length != 0)
{
Thread.Sleep(500);
}
else
{
//hbOptions hbCl = new hbOptions();
//hbCli = hbCl.HbCliOptions();
if (textBox3.Text != "")
{
hbCli = textBox3.Text.ToString();
}
else
{
hbCli = "-e x264 -q 20 -B 160";
}
string t = e.Name;
string s = t.Substring(0, t.Length - 4); //TODO: fix this its not reliable
file = e.FullPath;
string opath = textBox1.Text.ToString();
cmd = "-i \"" + file + "\" -o \"" + opath + "\\" + s + ".mp4\" " + hbCli;
try
{
for (int i = 0; i < Ext.Count(); i++)
{
if (e.Name.Contains(Ext[i]))
{
Process hb = new Process();
hb.StartInfo.FileName = "D:\\Apps\\Handbrake\\Install\\Handbrake\\HandBrakeCLI.exe";
hb.StartInfo.Arguments = cmd;
notifyIcon.BalloonTipTitle = "Now Converting";
notifyIcon.BalloonTipText = file;
notifyIcon.ShowBalloonTip(2000);
hb.Start();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void button1_Click(object sender, EventArgs e) //ok button
{
//add each array item to the list
for (int i = 0; i < filter.Count(); i++)
{
Ext.Add(filter[i]);
}
if (textBox1.Text != "" && textBox1.Text.Length > 2)
{
Watcher(); //call watcher to run
}
this.WindowState = FormWindowState.Minimized;
}
}
You way want to utilize WCF and MsmqQueueBinding:
Service uses NetMsmqBinding
Queue implemented for you using built-in into Windows queue called MSMQ (you can use MMC snap-it to control main, dead and poisoned letters queue. Both client and server Windows OS are bundled with it, turn it on in Windows Features)
Client puts process request into the queue and forgets about it
Service receives it automatically and process
Queue is durable, persisted and transactional (if you want)
You can run a queue on the same machine or on another intranet server
See the follow wonderful tutorial:
MSMQ, WCF and IIS: Getting them to play nice (Part 1)
MSMQ, WCF and IIS: Getting them to play nice (Part 2)
MSMQ, WCF and IIS: Getting them to play nice (Part 3)
I'm writing a program that uses FileSystemWatcher to monitor changes to a given directory, and when it receives OnCreated or OnChanged event, it copies those created/changed files to a specified directories. At first I had problems with the fact that OnChanged/OnCreated events can be sent twice (not acceptable in case it needed to process 500MB file) but I made a way around this and with what I'm REALLY BLOCKED with is getting the following IOException:
The process cannot access the file 'C:\Where are Photos\bookmarks (11).html' because it is being used by another process.
Thus, preventing the program from copying all the files it should.
So as I mentioned, when user uses this program he/she specifes monitored directory, when user copies/creates/changes file in that directory, program should get OnCreated/OnChanged event and then copy that file to few other directories.
Above error happens in all cases, if user copies few files that needs to overwrite other ones in folder being monitored or when copying bulk of several files or even sometimes when copying one file in a monitored directory.
Whole program is quite big so I'm sending the most important parts.
OnCreated:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
OnChanged:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
fileType:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
Copy:
private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
I checked everywhere in my program and whenever I use some file I use it in using block so even writing event to log (class for what I omitted since there is probably too much code already in post) wont lock the file, that is it shouldn't since all operations are using using statement block.
I simply have no clue who's locking the file if not my program "copy" process from user through Windows or something else.
Right now I have two possible "solutions" (I can't say they are clean solutions since they are hacks and as such not desirable). Since probably the problem is with fileType method (what else could lock the file?) I tried changing it to this, to simulate "blocking-until-ready-to-open" operation:
fileType:
private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
This is working as much as I could tell (test), but... it's hack, not to mention other deficients.
The other "solution" I could try (I didn't test it yet) is using GC.Collect() somewhere at the end of fileType() method. Maybe even worse "solution" than previous one.
Can someone pleas tell me, what on earth is locking the file, preventing it from opening and how can I fix that? What am I missing to see?
Thanks in advance.
The problem is most likely that the file is still being copied while you already try to access it. This can happen especially on large files.
You can try to check whether the file can be opened with write permissions before you actually start your processing. For details how to do that check here.
If you can influence the process creating the file there might be a better solution. First copy the file with a temporary extension, and then, after the copying is completed, rename it so that the FileSystemWatcher event will be triggered.
You can try with Volume Shadow Copies.
See www.codeproject.com/KB/dotnet/makeshadowcopy.aspx for more details.
FileSystemWatcher events trigger when the file begins the copy, not at the end, so it's common to run into this kind of errors.
Your first approach will work, however, I would recommend spinning the I/O intensive code on another thread, and using an incremental Sleep() instead of the busy waiting you do.
However, if you have access to the software that actually creates the files, the extension changing is a slightly less complicated solution. Just beware, that a xls filter on the FileSystemwatcher will match a file called myfile1.xls.temp, as I found that out the hard way :)