0

I am using fileSystemWatcher in C# winforms to monitor new files and folders created in the c:\Backup Folder, my clients transfer files to this folder and my application is supposed to zip newly created files and folders and sends them with email.

I have no problem with Watcher and its events and it's working fine.

my question is how can I know if copying large files to c:\Backup is finished so my app can start zipping them and send them?

Update : i just found out that every file copied raises theCreated event first and then at the start of copying raises Changed event and after copy is finished it raises Changed event again.

Mohsen K
  • 257
  • 4
  • 10
  • 1
    You could always just try to open the file with write access, if this fails it is likely due to it still being copied, and you need to wait and try again. Not very elegant, but I do not know of any better way. – JonasH Apr 27 '23 at 13:17
  • 1
    have you seen [this post](https://stackoverflow.com/questions/10982104/wait-until-file-is-completely-written) ? Please tell us whether it is helpful – Mong Zhu Apr 27 '23 at 13:59
  • There's no file close notification. To avoid paying the cost of exceptions when you try to open a locked file, you can wait until FSW notifications have stopped for eg a few seconds before trying to process the files – Panagiotis Kanavos Apr 27 '23 at 14:19
  • Backup/disk utilities use the file system's journaling or snapshot features to either detect the files that closed or get access to the original version of even locked files. That's not exposed to .NET though as there's no common API between OSs or even on Windows. FAT has no journaling and NTFS requires administrator privileges to enable journaling on an entire volume. You can't use journaling or snapshots on a single folder. In Linux, different file systems have different journaling features and some do support journaling/snapshots per folder – Panagiotis Kanavos Apr 27 '23 at 14:21
  • 1
    `and after copy is finished it raises Changed event again.` that's wrong. This happens only when applications preallocate the space they need (eg Explorer, Word) and the second change is *not* associated with closing. If you copy a really large file with Explorer you'd get 2 change events but the file will remain locked as long as explorer copies to it. FSW events correspond to file metadata changes, not byte writes. – Panagiotis Kanavos Apr 27 '23 at 15:39
  • BTW I found out that the second change doesn't mean the file closed the hard way, while trying to archive a large virtual disk file with Windows Explorer – Panagiotis Kanavos Apr 27 '23 at 15:41
  • You're only partially right, the last change event is not the file closing, but it is the last write. Whether or not the application closes the handle after it finishes writing is a separate story, and where the loop to try to open it comes in. – Blindy Apr 27 '23 at 16:52

2 Answers2

2

and after copy is finished it raises Changed event again.

Just to be clear here, you get changed notifications every time the file is changed, ie when data is finished writing to disk. That can happen for every byte is uncached, or only once in a while if cached. High level frameworks like .Net actually have multiple layers of caching, including the OS cache(s), so you have no real idea how many of these events will be triggered.

All that to say, don't assume it will always be just 2 events. Chances are it depends on the size of the file your particular application is writing. The only guarantee you have is that every time you do see that event data is finished writing. So your best bet is to try to open it with no sharing (FileSharing.None) and if it fails it's not done copying yet.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 1
    2 events happen when the application (Explorer, Word) preallocate the target file size and correspond to metadata changes (size, modification time). Copying a large file may continue long after the second change event, something I found the hard way – Panagiotis Kanavos Apr 27 '23 at 15:42
  • 1
    That's exactly what I said as well, yes. The only guarantee you have is that there will be a change event fired after the last change, but you can have many intermediary ones as well. – Blindy Apr 27 '23 at 16:20
0

so after reading Useful comments and some workaround, I found a solution that may not be the best way but I think it's doing the job.

 async private void fileSystemWatcher1_Created(object sender, FileSystemEventArgs e)
    {
        listBox6.Items.Add(e.FullPath + " " + e.ChangeType);

        FileInfo file = new FileInfo(e.FullPath);
        if (file.Attributes != FileAttributes.Directory)
        {

            Action lbl = () => { listBox7.Items.Add(e.Name + " copy in progress"); };

            await Task.Run(() =>
            {

                while (IsFileLocked(file))
                {
                    //some code like progressbar can be here
                    //this.Invoke(lbl);
                }

            }).ConfigureAwait(true);

        }

        listBox7.Items.Add(e.Name + " copied!");

    }

and the method to check for file Lock (I just got it from mong zhu's comment):

  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;
    }
Mohsen K
  • 257
  • 4
  • 10