I am making an interactive benchmarking program that works in console mode and would like to interrupt the current calculations by pressing Ctrl + C or Ctrl + Break once, and the next press should act as usual, ending the program. However, when no calculation is currently in progress, the very first press of Ctrl + C should complete.
At first it looked like a simple task. I created a static class with obvious routines EnableHandler()and DisableHandler(), as well as a handler function with behavior depending on the previous state: if an interrupt has already been requested, then just stop and return, otherwise set arg.Canceland set the interrupt request flag, Thus, when the benchmark starts , it allows the handler, and then checks the interrupt flag for each main loop; upon completion, it disables the handler and, if necessary, passes the interrupt request to the highest caller.
However, this approach works only once: after the handler was removed for the first time (regardless of when it was started or not), setting it again after that no longer has an effect - the operation does not cause errors, but the handler never Gets control when an event occurs . Is this the expected behavior in .NET event processing?
There are many topics related Console.CancelKeyPressto SO and other forums, but almost none of them are considering removing a handler, so it is not surprising that they did not encounter difficulties. However, in “ How to use ConsoleCancelEventHandler several times ” some similar (?) Problem was mentioned, but this application was a complicated GUI for several external console utilities that were launched on demand, and the problem was clearly related to an explicit exception that occurred when trying add a handler a second time - which is not my case.
, .NET, , .NET Visual Basic ( AddHandler RemoveHandler VB.NET += -= #). .NET 4.5 Visual Basic 2012 Windows 7 x86-64 .
DisableHandler(), : , , . , .
PS. 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