Cleanly Handling Ctrl-C In Console Applications

I find myself writing Console applications now and again. Command line ninjas know that if you want to stop a running console application you can hit Ctrl-C to  exit the application. In our Dovetail Seeker product, which we will be releasing with the next version of Dovetail Agent, when you cancel out of the indexer while it is runnLook out its a command line it would corrupt the search index. Not a big problem as the search indexes can be easily recreated but that can sometimes take a long time. Besides allowing scenario to exist where the user can corrupt application data is just plain wrong. Luckily it was easy to fill in this hole.


My Google-Fu took me to Curt Nichol’s post on Ctrl-C and the .NET console application where he introduces the System.Console.CancelKeyPress event and demonstrates using an anonymous method for handling it. For what I need this is the perfect solution with a small wrinkle the CancelKeyPress event is invoked from a separate thread. To explain why that is a little scary I need to go into gory details. The main take away of this post is be careful you are entering the land of concurrency and you need to tread lightly.


The reason the index would get corrupted is that a Ctrl-C exit would not properly close the index being written to. Closing the index is easy. The trouble is that because the CancelKeyPress event is fired on a separate thread if the event handler simply closes the index, on the other thread the running indexer will throw an exception because it can’t write to a closed index. The better solution is to tell the indexer to stop indexing. The last thing needed is to add the ability to tell when the indexing has successfully stopped. Here is my CancelKeyPress event handler:


System.Console.CancelKeyPress += delegate
_logger.WarnFormat("Indexing was canceled by user hitting Ctrl+C or Ctrl+Break. Attempting to cleanly cancel indexing.");


Within the documentIndexer‘s main loop each time around it checks to see if indexing is canceled. When indexing is all done it sets the IndexingCompleted ManualResetEvent which is there so that the application stays alive long enough for the indexer to finish up and properly close the index.


Have fun making your console applications more battle ready to fight those powerful command line ninjas.