如何使基于事件的控制台应用程序不立即终止?

17

来源

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

问题

应该很容易理解。我正在尝试创建一个文件监视器,以便可以自动为我对视频进行排序…如何使程序永远不终止?

我现在想保持控制台界面,以便进行调试,但最终我想去掉控制台,并让它在后台运行(我猜作为服务)。


你期望哪个事件会导致你的应用程序停止? - M.Babcock
@M.Babcock:我不指望它自动停止。在Windows上,我会使用Ctrl+Alt+Del强制关闭它。或者在我开发时,通过X关闭控制台窗口可以终止它。最终,它将通过停止Windows服务来终止。 - mpen
5个回答

22

也许可以这样:

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

更新

为了帮助其他可能在寻找类似解决方案的人,正如@Mark在评论中所说,还有一种使用FileSystemWatcher类的WaitForChanged方法来解决此问题的方法:

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

这样做允许应用程序无限期地等待(或者直到while循环被打破)文件被更改。


这让窗口保持打开状态......但我认为它阻止了文件监视器接收事件,除非我的编程有误....当我在该目录中创建文件时,“添加文件”从未被触发。 - mpen
哎呀……我本来应该使用“Created”事件的,但问题是一样的。 - mpen
3
在设置事件处理程序后,您需要将EnableRaisingEvents设置为true。我已经更新了答案以显示如何操作。有关更多信息,请查看FileSystemWatcher文档中的示例:http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx - M.Babcock
1
哦...它还有一个WaitForChanged函数..那么我们就不需要使用单独的线程对象了。 - mpen
1
我喜欢AutoResetEvent方法,因为我有多个FSW对象正在触发事件,所以WaitForChanged方法对我不起作用。 - arcain
显示剩余3条评论

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

}

刚才只需要EnableRaisingEvents就可以了。


找到了另一个解决方案,可能更好:

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

1
与其他事件很好地配合。尝试了使用计时器,不会阻塞执行。 - Tomislav Markovski

2
您可以使用以下方式无限期等待:
System.Threading.Thread.Sleep(-1);

描述中写着“获取指定对象的独占锁”……不确定它有什么帮助,但我还是尝试了一下。并没有起到作用,仍然立即关闭。我已经尝试过使用'obj'和'fw'。 - mpen
@Mark:非常正确!无论如何,我已经更新了我的答案,提供了更好的解决方案。 - Ry-
不,那会等待键盘输入并阻止任何事件通过。 - mpen
@Mark:不,FileSystemWatcher是异步的。你需要将EnableRaisingEvents属性设置为true - 我现在才注意到 :) 此外,我的答案中现在有一个更优雅的解决方案。 - Ry-
你说得对。我知道它是异步的,但我认为这意味着它不会停止执行,但我认为 ReadLine 本质上会占用所有线程并阻止任何东西通过...但我想它只会占用一个线程,并且异步事件将在单独的线程上触发... - mpen

2

由于这种情况,控制台应用程序从来不是测试事件的好方法。无论使用何种方法,它都必须停止当前线程,要么睡眠,要么在某些while(true)循环中锁定,这将防止您的事件触发或几乎无法在事件内部命中断点。 如果可以,请使用Windows应用程序。


目前看来似乎只有这个合法的解决方案。我可以这样做,但控制台应用程序似乎更方便,因为我只将其用作调试窗口。 - mpen
但通常这是为了更轻松地在Windows服务上工作而完成的。 - NickG

2

我曾经遇到和你一样的问题。我的解决方法是,如果使用命令行启动程序时加上参数--console,它会提示你按回车键关闭;如果不带参数,则期望以服务方式启动。

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
}

可能我漏掉了什么,但是仅仅在Main()的结尾添加Console.ReadLine();似乎也不起作用。我认为它正在等待输入而不接受事件,对吗?ServiceBase是你的类之一,还是我也应该从中继承? - mpen
这似乎是一段非常冗长的代码,只是为了满足Mark提出的最小要求。 - M.Babcock
2
ServiceBase是.NET的一部分。当我编写服务时,我希望我的调试尽可能接近运行服务。在等待ReadLine()时,事件将被记录,但是我认为您的问题就像M.Babcock所提到的那样,您需要将EnableRaisingEvents设置为true。根据MSDN的说明:“仅当您将EnableRaisingEvents设置为true时,组件才会引发事件”。 - Scott Chamberlain
@ScottChamberlain:你说得对!我没想到会有这样的选项...如果你不想监听事件,为什么要创建一个文件系统监视器呢?! - mpen
@Mark 你可能希望延迟观察,直到你准备好接收事件或者其他事情先发生,这样更容易是先禁用,等你设置好后再启用,而不是一开始就启用,忽略消息直到你有机会停止它。 - Scott Chamberlain
@ScottChamberlain:我想……我在努力想象一个你不能立即将其设置为false的场景……但是……我不知道!也许在构造函数中使用一个bool值来启用或禁用会更理想。 - mpen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接