Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FileSystemWatcher fires before file is saved - how do you "pause" the process?

Here is the logic I'm trying code:

Service monitors a .pptx file in directory. If the file has changed perform convertion to jpg. Then do other tasks, which will be added later.

I'm using file wather object but that fires as soon as I open the file, so I thought to stop the process by checking if the file is "locked". I thought a "while locked" loop would do the trick - but no. Below is reduced code prototype and I woiuld like to if you could look at it suggest what I'm doing wrong and/or if there is a better way to write this for a production environment. The pptx file can be open for a long time.

namespace FileWatcherDemo
{
   public class Program
   {

    static void Main(string[] args)
    {
        FileSystemWatcher fsWatcher = new FileSystemWatcher();
        fsWatcher.Path = @"e:\\";

        fsWatcher.NotifyFilter = NotifyFilters.LastWrite; 

        fsWatcher.Filter = "*.pptx";
        fsWatcher.Changed += new FileSystemEventHandler(fsWatcher_Changed);
        //fsWatcher.Created += new FileSystemEventHandler(fsWatcher_Changed);
        //fsWatcher.Deleted += new FileSystemEventHandler(fsWatcher_Changed);
        //fsWatcher.Renamed += new RenamedEventHandler(fsWatcher_Changed);
        fsWatcher.EnableRaisingEvents = true;
        Console.ReadKey();
    }        

    static void fsWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        try
        {
            while( !IsFileLocked())
            {
                Console.WriteLine("Changed Event Fired");
                Microsoft.Office.Interop.PowerPoint.Application app = new Microsoft.Office.Interop.PowerPoint.Application();
                Presentation pptPresentation = app.Presentations.Open(@"e:\\HowTo.pptx", MsoTriState.msoFalse, MsoTriState.msoFalse, MsoTriState.msoFalse);

                pptPresentation.SaveAs(@"e:\\Output", PpSaveAsFileType.ppSaveAsJPG, MsoTriState.msoFalse);
                pptPresentation.Close();
            }
        }
        catch (Exception ex)
        {
            using (StreamWriter w = File.AppendText(@"e:\\ErrorLog.txt"))
            {
                Log(ex.Message.ToString(), w);
                Log(ex.StackTrace.ToString(), w);

                w.Close();
            }
        }

        Console.ReadKey();
    }

    static bool IsFileLocked()
    {
        FileStream fs = null;
        FileInfo file = new FileInfo(@"e:\\HowTo.pptx");            

        try
        {
            fs = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (IOException)
        {
            return true;
        }
        finally
        {
            if(fs != null)
                fs.Close();
        }
        return false;
    }

    public static void Log(string LogMessage, TextWriter w)
    {
        w.Write("\r\nLog Entry: ");
        w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString());
        w.WriteLine("  :");
        w.WriteLine("  {0}", LogMessage.ToString());
        w.WriteLine("------------------------------------------");

        w.Flush();
    }
}

}

like image 535
Risho Avatar asked Aug 26 '11 20:08

Risho


2 Answers

Here's one other idea: When the FileSystemWatcher detects the change (You say it fires immediately the file is opened) record the LastModifiedTime of the file and keep looping until the LastModifiedTime of the file changes (Assuming the LastModifiedTime gets written only when the file is saved - I don't know when this is actually done) and then perform your process of converting to JPG.

EDIT

Adding code that should demonstrate how to track when a file has been modified:

class Program
{
    static void Main(string[] args)
    {
        Thread t = new Thread(()=> DoTest());
        t.Start();

        Console.WriteLine("Waiting...");
        Console.ReadKey();
    }

    private static void DoTest()
    {
        FileSystemWatcher fsw = new FileSystemWatcher("C:\\");
        fsw.Filter = "*.txt";
        fsw.Changed += new FileSystemEventHandler(fsw_Changed);
        fsw.Deleted += new FileSystemEventHandler(fsw_Deleted);
        fsw.Renamed += new RenamedEventHandler(fsw_Renamed);
        fsw.Created += new FileSystemEventHandler(fsw_Created);
        fsw.EnableRaisingEvents = true;
    }

    static void fsw_Created(object sender, FileSystemEventArgs e)
    {
        FileInfo fi = new FileInfo(e.FullPath);
        Console.WriteLine("File Created: "+e.FullPath);
        Console.WriteLine("Creation Time: " + fi.CreationTime.ToLongTimeString());
        Console.WriteLine("Last Access Time: " + fi.LastAccessTime.ToLongTimeString());
        Console.WriteLine("Last Write Time: " + fi.LastWriteTime.ToLongTimeString());
        Console.WriteLine("Length: " + fi.Length);

    }

    static void fsw_Renamed(object sender, RenamedEventArgs e)
    {
        FileInfo fi = new FileInfo(e.FullPath);
        Console.WriteLine("File Renamed: "+e.FullPath);
        Console.WriteLine("Creation Time: " + fi.CreationTime.ToLongTimeString());
        Console.WriteLine("Access Time: " + fi.LastAccessTime.ToLongTimeString());
        Console.WriteLine("Last Write Time: " + fi.LastWriteTime.ToLongTimeString());
        Console.WriteLine("Length: " + fi.Length);
    }

    static void fsw_Deleted(object sender, FileSystemEventArgs e)
    {
        FileInfo fi = new FileInfo(e.FullPath);
        Console.WriteLine("File Deleted: "+e.FullPath);
        Console.WriteLine("Creation Time: " + fi.CreationTime.ToLongTimeString());
        Console.WriteLine("Last Access Time: " + fi.LastAccessTime.ToLongTimeString());
        Console.WriteLine("Last Write Time: " + fi.LastWriteTime.ToLongTimeString());

    }

    static void fsw_Changed(object sender, FileSystemEventArgs e)
    {
        FileInfo fi = new FileInfo(e.FullPath);
        Console.WriteLine("File Changed: "+e.FullPath);
        Console.WriteLine("Creation Time: " + fi.CreationTime.ToLongTimeString());
        Console.WriteLine("Last Access Time: " + fi.LastAccessTime.ToLongTimeString());
        Console.WriteLine("Last Write Time: " + fi.LastWriteTime.ToLongTimeString());
        Console.WriteLine("Length: " + fi.Length);
    }
}
like image 155
Icarus Avatar answered Nov 14 '22 18:11

Icarus


There is a large amount of logic your need to do to make the FileSystemWatcher suitable for production-level code.

  • You want to keep the event handlers very light, simply queue that something happened and then process it later.

  • Use a timer (System.Threading is best) to process the queue with a delay of 1000ms, when you get an event, stop/start the timer.

  • Check the queue for multiple events for the same file, e.g. a program may create a file and then delete it again.

Edit: I just did a quick Google search and found an article and sample code that will get you 90% there.

http://csharp-codesamples.com/2009/02/file-system-watcher-and-large-file-volumes/

http://web.archive.org/web/20120814142626/http://csharp-codesamples.com/2009/02/file-system-watcher-and-large-file-volumes/

Edit 2: Just re-read your question. The advice above still applies, however a few more things for you to do to solve your problem:

  • Powerpoint, like other Office applications, creates a hidden temporary file with a ~ prefix.

  • Check file modification time stamps. When you first notice a file has changed, save the modification time and compare against it next time the file is changed.

  • There is a some flag property of the file system watcher that you will need to set to get modification time changes.

Hope all this helps...

like image 32
Dennis Avatar answered Nov 14 '22 19:11

Dennis