I am making an interactive benchmarking program that works in console mode, and would like to be able to interrupt current computation by pressing Ctrl+C or Ctrl+Break once, while subsequent pressing should act as usual, terminating the program. However, when no computation is currently performing, the very first Ctrl+C press should cause termination.
At first, this looked like a simple task. I created a static class with obvious EnableHandler()
and DisableHandler()
routines, and a handler function with behavior dependent on previous state: if interruption was already requested, then just stand down and return, otherwise set arg.Cancel
and raise interruption request flag. Thus, when a benchmark starts, it enables the handler and then checks for interruption flag on each major cycle; when finished, it disables the handler and propagates interruption request up to the topmost caller, if needed.
However, this approach works only once: after the handler was removed for the first time (no matter was it ever fired or not), installing it again afterwards has no effect anymore — the operation doesn't produce errors, but the handler never receives control when event occurs. Is this an expected behavior in .NET event handling?
There are numerous topics regarding Console.CancelKeyPress
on SO and other forums, but virtually none of them considers removing the handler, thus no wonder they didn't run into trouble. Nevertheless, in “How to use ConsoleCancelEventHandler several times” some similar(?) issue was mentioned, but that application was a complex GUI frontend for several external console utilities launched on demand, and the issue was clearly associated with explicit exception raised when trying to add handler the second time, — which is not my case.
Having been informed of previously existing bugs related to console operation in various .NET versions, I won't be surprised if such a behavior is abnormal and caused by .NET malfunction or is specific to Visual Basic (I'm not sure whether AddHandler
and RemoveHandler
in VB.NET are totally equivalent to +=
and -=
event operators in C#). I'm currently using .NET 4.5 and Visual Basic 2012 on Windows 7 x86-64 with all available updates installed.
As a workaround, I actually don't remove the handler in DisableHandler()
routine, but simply toggle a flag: when this flag is cleared, the handler returns immediately, as if not installed. However, this approach looks ugly to me, and I hope to resolve the issue and achieve the goal in proper way.
PS. Current code as requested by Chris Dunaway:
' Usage example
Dim fAbort As Boolean = False
BreakHandler.Enable()
For Each oImplementation As Implementation In oCompetition.Implementations
Benchmark(oImplementation)
If BreakHandler.AbortRequested() Then fAbort = True : Exit For
Next
BreakHandler.Disable()
Return Not fAbort
Public Class BreakHandler
Protected Shared AbortFlag As Boolean = False
Protected Shared HandlerInstalled As Boolean = False
Protected Shared HandlerEnabled As Boolean = False
Public Shared ReadOnly Property AbortRequested() As Boolean
Get
If AbortFlag Then
AbortFlag = False
Return True
Else
Return False
End If
End Get
End Property
Public Shared Sub Enable()
If HandlerEnabled Then Return
If Not HandlerInstalled Then
AddHandler Console.CancelKeyPress, AddressOf Handler
HandlerInstalled = True
End If
HandlerEnabled = True
End Sub
Public Shared Sub Disable()
AbortFlag = False
If Not HandlerEnabled Then Return
' This is where the handler was removed originally.
'RemoveHandler Console.CancelKeyPress, AddressOf Handler
HandlerEnabled = False
End Sub
Protected Shared Sub Handler(ByVal sender As Object, ByVal args As ConsoleCancelEventArgs)
If (Not HandlerEnabled) OrElse AbortFlag Then
Return ' Stand down, allow complete abortion.
Else
Console.Out.WriteLine("Will abort on next cycle. Press Break again to quit.")
AbortFlag = True
args.Cancel = True
End If
End Sub
End Class