6

Is there an Event I can capture for when a known file has been closed by an external application?

For example, a user is editing a workbook in Excel and I want to read that file as soon as the user finishes working on it and closes the file.

My current solution is to use a combination of FileSystemWatcher and Timer. The FileSystemWatcher will detect when changes have been made to a file, and start a new thread running a Timer to check when the file has closed (via try-catch) However I don't feel as though this is a good solution. If the user forgot to close the file and heads home for the weekend, it feels wasteful for my Timer to be running the whole time. If I increase the interval on my Timer, then my program won't be as responsive. Is there a solution that doesn't involve polling?

EDIT: updated with code example of what I have

    private System.Windows.Forms.Timer processTimer;
    private string blockedFile;

    // Starts here. File changes were detected.
    private void OnFileSystemWatcher_Changed(object source, FileSystemEventArgs e)
    {
        FileSystemWatcher fsw = (FileSystemWatcher)source;
        string fullpath = Path.Combine(fsw.Path, fsw.Filter);
        StartFileProcessing(fullpath);
    }

    private void StartFileProcessing(string filePath)
    {
        if (isFileOpen(new FileInfo(filePath)))
        {
            blockedFile = filePath;
            processTimer = new System.Windows.Forms.Timer();
            processTimer.Interval = 1000; // 1 sec
            processTimer.Tick += new EventHandler(processTimer_Elapsed);
            processTimer.Enabled = true;
            processTimer.Start(); 
        }
        else
            ProcessFile(filePath);

    }

    private void ProcessFile(string filePath)
    {
        // Do stuff, read + writes to the file.
    }

    // GOAL: Without polling, how can I get rid of this step just know right away when the file has been closed?
    private void processTimer_Elapsed(object sender, EventArgs e)
    {
        if (isFileOpen(new FileInfo(blockedFile)) == false)
        {
            // The file has been freed up
            processTimer.Enabled = false;
            processTimer.Stop();
            processTimer.Dispose();

            ProcessFile(blockedFile);
        }
    }

    // Returns true if the file is opened
    public bool isFileOpen(FileInfo file)
    {
        FileStream str = null;
        try
        {
            str = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (IOException)
        {
            return true;
        }
        finally
        {
            if (str != null)
                str.Close();
        }
        return false;
    }
Community
  • 1
  • 1
Calseon
  • 325
  • 3
  • 18
  • 2
    You can listen to Changed event of FileSysteWatcher, then you will not have to check it with timer. It says "The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, or security permissions of a file or directory in the directory being monitored." Further details [Here](https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.changed(v=vs.110).aspx). Though I am not sure about the need to know if file is closed. Is that mandatory for your program to know if it is closed? – PM. Feb 13 '17 at 22:07
  • 1
    See if this helps https://chrisbitting.com/2013/12/10/checking-if-a-file-is-locked-in-c-using-win32/ – Tony Feb 13 '17 at 22:13
  • 1
    Thank you for your response PM & Tony. @ PM: Yes I need to know if the file is closed because I need to write back to it. @ Tony: I'll give it a look. Thanks – Calseon Feb 13 '17 at 22:44
  • In my experience, while your specific example is okay, the more general case is that it's often the result of something going wrong when releasing the lock, and you'll be waiting a _looong_ time. – Joel Coehoorn Feb 13 '17 at 22:49
  • 4
    Depending on what is triggering the first file changed notification, this article may be useful https://www.intertech.com/Blog/avoiding-file-concurrency-using-system-io-filesystemwatcher/ – TJ Rockefeller Feb 13 '17 at 22:56
  • 1
    The better implementation of isFileOpen would be to return the actual file stream if it can successfully open the file rather than a bool. If you return false saying that the file is not open, there is still the possibility that some other process will lock the file before you can do what you need to do with the file. – TJ Rockefeller Feb 13 '17 at 22:59

0 Answers0