8

I have an application that launches other applications, and then waits for them to create a specific data file (it watches one application at a time). Each time an application is launch it watches a specific directory for a specific file to be created. I am using the FileSystemWatcher to do this (set it to the directory, then filter for the correct file name). This works great the first time (always), but the second application launched never fires the event. The only way it seems to fire the event is if I place a break-point in the event handler, or if I have a Thread.Sleep command in the event handler. This seems very strange to me...is there some race condition that I'm not aware of? Here is the code. Notice I have a Thread.Sleep(500). With this line the code works every time. Without it will fail. I'm really not comfortable relying on a Sleep command. I'm not sure what condition will cause that not to work as well.

    public static void watchFiles(string path)
    {
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.Created += new FileSystemEventHandler(watcher_Handler);
        watcher.EnableRaisingEvents = true;
   }

    public static void watcher_Handler(object sender, FileSystemEventArgs e)
    {
        //Hack - the sleep allows the second and third application to be caught by this event
        Thread.Sleep(500);

        switch (e.ChangeType.ToString())
        {
            case "Changed":
                break;
            case "Deleted":
                break;
            case "Created":
                if (e.Name == "log.dat")
                {
                    parseDataFile();
                    moveHTMLtoLMS();

                }
                break;
            default:
                break;
        }
    }

Anyone know why I need to have that Sleep (or break-point) to get the code to work a second time?

cwo
  • 81
  • 1
  • 2
  • 5
    note: you don't need to use `e.ChangeType.ToString()` in the switch, just switch on `e.ChangeType` and make the cases `ChangeType.XXX`. This makes it strongly typed and less error prone. – Femaref Dec 27 '10 at 20:28
  • The filesystem watcher seems to have a lot of caveats. http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.oncreated.aspx That said, are you sure the various watchers aren't watching the same directory? – NotMe Dec 27 '10 at 20:32
  • BTW, you might want to post the OS and other details that are involved, such as whether these are local paths etc. – NotMe Dec 27 '10 at 20:33
  • Thanks for the note on the ChangeType. I'm sure that the directories are correct. The only line difference between functioning as expected and only functioning the first time is the Thread.Sleep. p.s. I'm using local paths on Windows 7. – cwo Dec 27 '10 at 20:35
  • 1
    Are you certain the watcher.Created registration is taking place multiple times and on different directories when no breakpoints exist? Adding log lines can often times help debug these types of issues as well...as setting breakpoints provides varying results... – Aaron McIver Dec 27 '10 at 20:37
  • Yes. I've added some logging to ensure that the path to be watched is being set correctly each time. – cwo Dec 27 '10 at 21:16
  • I have the same problem encountered. Without the Sleep, it only fired for once. _changeWatcher = new FileSystemWatcher() { Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), NotifyFilter = NotifyFilters.LastWrite, InternalBufferSize = 65536, IncludeSubdirectories = false, EnableRaisingEvents = true, Filter = "guard.config" }; _changeWatcher.Changed +=OnFileChanged; – Mr.Wang from Next Door Dec 09 '13 at 02:16

4 Answers4

1
public static void watchFiles(string path)
{
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = path;
    watcher.Created += new FileSystemEventHandler(watcher_Handler);
    watcher.EnableRaisingEvents = true;
}

The watcher variable is eligible for garbage collection at the end of this method. Instead of being a local variable, make it a class-level member as such:

private static FileSystemWatcher watcher;

public static void watchFiles(string path)
{
    if (watcher != null)
    {
        watcher.EnableRaisingEvents = false;
        watcher.Created -= new FileSystemEventHandler(watcher_Handler);
    }

    watcher = new FileSystemWatcher();
    watcher.Path = path;
    watcher.Created += new FileSystemEventHandler(watcher_Handler);
    watcher.EnableRaisingEvents = true;
}
Jesse C. Slicer
  • 19,901
  • 3
  • 68
  • 87
1

According to the documentation of the System.IO.FileSystemWatcher class:

The Windows operating system notifies your component of file changes in a buffer created by the FileSystemWatcher. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer with the InternalBufferSize property is expensive, as it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small yet large enough to not miss any file change events. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties so you can filter out unwanted change notifications.

It might be that the event isn't being consumed fast enough and the internal buffer isn't large enough to handle all the notifications. By default, the watcher handles FileName, DirectoryName, LastWrite notifications yet you only consume creation events (both file and directory). Are your applications running in quick succession? I'd try putting a delay between the invocations of your applications (instead of the event handler), use more specific filters (just the FileName notification or watch only for log files using the Filter property), increase the internal buffer size or any combination of the above. I think that should fix your problem.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • 1
    The other applications are not running in quick succession. I can wait a few seconds or a few minutes between launching different applications with the same results. – cwo Dec 27 '10 at 20:47
  • @cwo: Then you might be able to get away with leaving out the delays if you tried the other things I've mentioned. – Jeff Mercado Dec 27 '10 at 20:50
0

You are listenting to only one "Created" event. You need to listen to all other ones too - OnChanged, OnDeleted - http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx

EDIT: Most programs will not "Create" file when one already exists. You can use FileMon (now Process Monitor - http://technet.microsoft.com/en-us/sysinternals/bb896645 ) to see what operations each program perform with your file.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • The created event is the only one I'm interested in. The other cases in the switch statement were only there for testing the filesystemwatcher in the beginning. When the file is created I need to parse it. The way it works the files will never be changed, deleted, or renamed (or if they are I don't care about that). – cwo Dec 27 '10 at 20:45
  • Try using Process Monitor to verify your assumptions - I suspect there is no "Create" operations performed by other programs... Or at least you'll know for sure that the problem is with your code, not with notifications. – Alexei Levenkov Dec 27 '10 at 20:50
  • I have verified that the file is being created in the correct location. I've been using process explorer. But I can also just open the directory in question and see that the file is being created. – cwo Dec 27 '10 at 21:00
  • Any chance that your code in parseDataFile/moveHTMLtoLMS fails with Access Denied (or other exception) because the file is JUST created but not yet written to? It would explain why waiting for some time (enought for program to write to the file and close it) makes code working... – Alexei Levenkov Dec 28 '10 at 01:32
0

I'm facing the exact same problem here (running Windows XP). Your hack solves the problem. I would like to add some notes that might be relevant.

In my case the filename is always the same: C:\blah.txt is created, deleted, created and so forth. Also, I'm using a trick to hide my application:

Integrator.StartMonitor(); // Start the file monitor!

Form f = new Form();
f.ShowInTaskbar = false;
f.ShowIcon = false;
f.StartPosition = FormStartPosition.Manual; 
f.Location = new Point(-32000, -32000);

f.Show();
f.Hide();

Application.Run();

My file watcher works in debug mode or when I add the sleep-hack of yours. It certainly looks like a bug in the FileSystemWatcher.

l33t
  • 18,692
  • 16
  • 103
  • 180