6

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();
    }
}

}

LeftyX
  • 35,328
  • 21
  • 132
  • 193
Risho
  • 2,561
  • 5
  • 31
  • 56

2 Answers2

2

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);
    }
}
Icarus
  • 63,293
  • 14
  • 100
  • 115
  • Yes, FileInfo's LastAccesTime dislplays indeed them time which is not the current time and that's what I want. There is not a property that tracks change however. How do you test for that? Thanks. – Risho Aug 29 '11 at 14:18
  • @Risho I don't know why do you say that there's no property that tracks change. I am editing my answer with sample code. I was able to monitor a change event on a file and the time DID indeed increase every time the file was modified. – Icarus Aug 29 '11 at 15:57
  • becase all of the fsw_ methods fire as soon as I click on the file not when the file is saved. I have to account for the fact that the file may open for hours. – Risho Aug 29 '11 at 19:46
  • @Risho: That is not true. The Changed event fires when the file is actually saved. I tested this my self 100 times. Maybe you have another issue with your code? – Icarus Aug 29 '11 at 20:29
  • I appreciate your time and effort as it is crucial that I get this done. However it is not the case. Right now this is just a console app, and as soon as I run it and open the file, FileSystemWather Change event fires immediately. If I didn't have breakpoint set just inside method, the program would run through the method before my file is modified. – Risho Aug 30 '11 at 14:39
  • Here is what's in the Main: 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.EnableRaisingEvents = true; Console.ReadKey();} the change method declaration looks like this: static void fsWatcher_Changed(object sender, FileSystemEventArgs e) – Risho Aug 30 '11 at 14:42
  • @Risho, what can I tell you? The FileSystemWatcher (At least for me, DOES fire the OnChange event when the file is actually saved). Granted, the user may save the file without closing PowerPoint but then you could open the file in ReadWirte mode (http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx) and create the jpg image. If the user makes a subsequent change, you do the same when the OnChange event fires again. Finally, remove the fsWatcher.NotifyFilter = NotifyFilters.LastWrite; from your code and see if that makes any difference. – Icarus Aug 30 '11 at 14:48
1

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...

Dennis
  • 20,275
  • 4
  • 64
  • 80