17

Source

class Program
{
    static void Main(string[] args)
    {
        var fw = new FileSystemWatcher(@"M:\Videos\Unsorted");
        fw.Created+= fw_Created;
    }

    static void fw_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine("added file {0}", e.Name);
    }
}

Question

Should be pretty self explanatory. I'm trying to create a file watcher so I can sort my videos for me automatically...how do I get the program to not terminate, ever?

I want to keep it console-based for now so I can debug it, but eventually I want to remove the console and just have it run in the background (I guess as a service).

Charles
  • 50,943
  • 13
  • 104
  • 142
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • What event do you expect to cause your application to stop? – M.Babcock Dec 30 '11 at 04:13
  • @M.Babcock: I don't expect to stop it. I'll forcibly close it with Ctrl+Alt+Del on windows. Or while I'm developing, closing the console window via the X can terminate it. Ultimately it would be terminated by stopping the windows service. – mpen Dec 30 '11 at 04:14

5 Answers5

22

Perhaps something like this:

class Program
{
    static void Main(string[] args)
    {
        var fw = new FileSystemWatcher(@"M:\Videos\Unsorted");
        fw.Changed += fw_Changed;
        fw.EnableRaisingEvents = true;

        new System.Threading.AutoResetEvent(false).WaitOne();
    }

    static void fw_Changed(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine("added file {0}", e.Name);
    }
}

Update

In the spirit of helping anyone else that may be looking for a similar solution, as @Mark stated in the comments, there is also a way to use the WaitForChanged method of the FileSystemWatcher class to solve this question:

class Program
{
    static void Main(string[] args)
    {
        var fw = new FileSystemWatcher(@".");
        while (true)
        {
            Console.WriteLine("added file {0}",
                fw.WaitForChanged(WatcherChangeTypes.All).Name);
        }
    }
}

Doing so allows the application to wait indefinitely (or until the while is broken) for a file to be changed.

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • That's keeping the window open.....but I think it's preventing the file watcher from receiving events, unless I've programmed it wrong...."added file" is never being hit when I create files in said directory. – mpen Dec 30 '11 at 04:19
  • Err...I should have been using the "Created" event instead, but it's the same problem. – mpen Dec 30 '11 at 04:20
  • 3
    After you set your event handler you need to set EnableRaisingEvents to true. I've updated the answer to show how. Check out the example from the FileSystemWatcher documentation for more information: http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx – M.Babcock Dec 30 '11 at 04:21
  • 1
    Ooo...it also has a `WaitForChanged` function..then we don't have to use a separate threading object. – mpen Dec 30 '11 at 04:37
  • @Mark - The answer has been updated to include a very primitive example of using the `WaitForChange` method. – M.Babcock Dec 30 '11 at 05:10
  • @MBabcock: That doesn't return a bool.....you can't put it in a while. I amended my answer with one that works awhile ago :D – mpen Dec 30 '11 at 05:47
  • @Mark - My mistake. Fixed now. – M.Babcock Dec 30 '11 at 05:50
  • 1
    I like the AutoResetEvent approach since I have multiple FSW objects that are firing events, so the WaitForChanged approach won't work for me. – arcain Mar 10 '17 at 16:59
7
class Program
{
    static void Main(string[] args)
    {
        var fw = new FileSystemWatcher(@"M:\Videos\Unsorted");
        fw.EnableRaisingEvents = true;
        fw.Created += fw_Created;

        Console.ReadLine();

    }

    static void fw_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine("added file {0}", e.Name);
    }

}

Just had to EnableRaisingEvents apparently.


Found another solution that is perhaps even nicer:

class Program
{
    static void Main(string[] args)
    {
        var fw = new FileSystemWatcher(@"M:\Videos\Unsorted");
        fw.Created += fw_Created;
        while(true) fw.WaitForChanged(WatcherChangeTypes.All);
    }

    static void fw_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine("added file {0}", e.Name);
    }
}
mpen
  • 272,448
  • 266
  • 850
  • 1,236
2

You can wait forever using this:

System.Threading.Thread.Sleep(-1);
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • The description reads "acquires an exclusive lock on the specified object"... not sure how that helps, but I tried it anyway. Doesn't do anything; still closes immediately. Tried it with `obj` and `fw`. – mpen Dec 30 '11 at 04:17
  • @Mark: Quite right! I've updated my answer with a better solution anyway. – Ry- Dec 30 '11 at 04:22
  • Nope...that's going to wait for keyboard input and not allow any events to get through. – mpen Dec 30 '11 at 04:26
  • @Mark: No, `FileSystemWatcher` is asynchronous. You need to set the `EnableRaisingEvents` property to `true` - just noticed that now :) Also, a more elegant solution is now in my answer. – Ry- Dec 30 '11 at 04:29
  • You're right. I knew it was async but I thought that meant it wouldn't halt execution *but* I thought `ReadLine` would essentially hog all the threads and not let anything through...but I guess it would just hog the one thread, and the async event would fire on a separate thread... – mpen Dec 30 '11 at 05:50
2

I have had the exact same problem as you. What i did is if the program is launched with the command line --console it will prompt you to hit enter to close, if no parameters it will expect to be launched as a service.

class MyExampleApp : ServiceBase
{

    public static void Main(string[] args)
    {
        if (args.Length == 1 && args[0].Equals("--console"))
        {
            new MyExampleApp().ConsoleRun();
        }
        else
        {
            ServiceBase.Run(new MyExampleApp());
        }
    }
    private void ConsoleRun()
    {
        Console.WriteLine(string.Format("{0}::starting...", GetType().FullName));

        OnStart(null);

        Console.WriteLine(string.Format("{0}::ready (ENTER to exit)", GetType().FullName));
        Console.ReadLine();

        OnStop();

        Console.WriteLine(string.Format("{0}::stopped", GetType().FullName));
    }
    //snip
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Err...perhaps I'm missing something, but merely adding `Console.ReadLine();` at the end of Main() doesn't seem to work either. I think it's waiting for input and not accepting events, no? Is `ServiceBase` one of your classes, or something I should be inheriting from too? – mpen Dec 30 '11 at 04:24
  • That seems like an awful lot of code to fulfill the minimal task that Mark is asking for. – M.Babcock Dec 30 '11 at 04:25
  • 2
    ServiceBase is [part of .NET](http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.aspx). When I write a service I want my debugging to be as close to a running service as possible. Events will file while waiting for a ReadLine(), however I think you problem is like [M.Babcock mentioned](http://stackoverflow.com/a/8676112/80274) ,you need to set EnableRaisingEvents to true. [From the MSDN](http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.enableraisingevents.aspx) "**The component will not raise events unless you set EnableRaisingEvents to true**" – Scott Chamberlain Dec 30 '11 at 04:28
  • @ScottChamberlain: You are correct! Didn't think to look for such an option..why would you create a file system watcher if you didn't want to listen for events?! – mpen Dec 30 '11 at 04:32
  • @Mark You may want to delay the watching until you are ready to receive the events or something else happens first, it is easier to start disabled and enable when you are set up, than start enabled and ignore messages until you had a chance to stop it. – Scott Chamberlain Dec 30 '11 at 04:35
  • @ScottChamberlain: I suppose.. I'm trying to think of a scenario where you couldn't immediately set it to false...but... I dunno! Perhaps a bool in the constructor to start enabled or disabled would have been optimal then. – mpen Dec 30 '11 at 05:52
2

Console apps are never a good way to test events due to this scenario. Whatever method you use, it has to halt the current thread, either sleep or lock in some while(true) loop, which pevents your events from firing or makes it almost impossible to hit breakpoints inside events. Use a windows app if you can.

Tomislav Markovski
  • 12,331
  • 7
  • 50
  • 72
  • Seems like the only legit solution so far. I can do that, but console app seemed more convenient because I'm only using it as a debug window. – mpen Dec 30 '11 at 04:28
  • But often this is done to make an easier way to work on windows services. – NickG Jan 20 '17 at 13:07