-1

once upon a time when calling CancellationTokenSource Cancel() method I am getting an exception: The handle is invalid.

   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.Threading.EventWaitHandle.Set()
   at System.Threading.CancellationTokenSource.NotifyCancellation(Boolean throwOnFirstException)
   at System.Threading.CancellationTokenSource.Cancel()
   at .CancelProcess(Object obj) in proj\ViewModels\WorkflowContainerViewModel.cs:line 162
   at DelegateCommand.Execute(Object parameter) in proj\Commands\DelegateCommand.cs:line 32
   at MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(ICommandSource commandSource, Boolean userInitiated)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
   at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

I couldn't find anything anywhere. Any ideas?

MistyK
  • 6,055
  • 2
  • 42
  • 76
  • People who want to close it - please let me know what is the reason – MistyK Apr 25 '16 at 13:30
  • Seeing that stack trace, I am able to find the solution to this question, and also found fifteen other bugs in your code, three in the .NET framework, and four in WPF. – Jcl Apr 25 '16 at 13:34
  • cool, there are no other things I can think of to show here which are important. I'm calling cts.Cancel() and this is the stacktrace. What do you want me to show? You can see from the stacktrace that it's being invoked from the wpf control bound to command but I can't see why it would be important. – MistyK Apr 25 '16 at 13:39
  • 3
    Zbignew, the `invalid handle` problem shows when you try to do something on a `CancellationTokenSource` whose kernel event was disposed (and its handle lost). This should not happen on normal usage, but it could happen if you are for example, serializing and deserializing it (the original object is lost and the kernel event is disposed of, after deserializing it tries to set the same native handle, but that one doesn't exist). There are several reasons why this could happen: without a minimal reproduceable example, it's impossible to help you at all – Jcl Apr 25 '16 at 13:48
  • Hey that's a very nice post! It's very difficult for me to show even a small part of code because it's a part of big project and I am not sure what may be causing that. Information about serialization is perfect because there is actual a place in the code which serializes and deserializes the token. I will investigate it further, thanks mate – MistyK Apr 25 '16 at 13:51
  • 2
    Mentioning that in the question would have been much more productive than the stack trace :-) Let us know if you find the cause – Jcl Apr 25 '16 at 13:53
  • Yes that's true but I didn't have an idea that this part of code matters, thanks – MistyK Apr 25 '16 at 13:54
  • @Jcl FYI - it fixed the issue and the second exception which I was getting only in debug mode - SehException. Could you explain in more detail what exactly happens when you serialize/deserialize or could you provide me any article about it? Why is this trying to use the same CTS object after deserialization? – MistyK Apr 26 '16 at 07:55
  • I wrote an answer which hopefully explains why it happened (sorry if it's not very detailed, my English is being dense this morning) – Jcl Apr 26 '16 at 08:31
  • On SO you don't just paste a Stacktrace and say "hey, i've got this error". A [minimal complete and verifiable example](http://stackoverflow.com/help/mcve) is needed to understand what went wrong otherwise, without information needed, people could only guess of what is the problem and how to deal with it. – Fabjan Apr 26 '16 at 08:47
  • Fabjan don't be a police officer - if you notice my problem was resolved in 5 minutes. Sometimes it's hard to provide more details because there are no possible guesses. – MistyK Apr 26 '16 at 10:02

1 Answers1

3

The problem, (as figured out in the comments), is that the CancellationToken was being serialized and then deserialized again.

The CancellationTokenSource (a CancellationToken creates one if you instantiate it directly) holds a reference to a non-managed kernel event (using EventWaitHandle), and internally holds a reference to that handle.

When the CancellationTokenSource gets disposed, it releases that non-managed reference... so if you serialize the token, it'll get serialized with that handle... afterwards, you are not using the CancellationTokenSource anymore so the garbage collector collects it, and it releases the kernel event (so the handle is no more valid).

Now upon deserializing, the EventWaitHandle gets the values back, but it doesn't create the kernel event again (because upon deserializing, you are only assigning the values to the fields, not going through the construction logic again), but it retains the old handle... so when you want to cancel it, it'll try to use a handle to that kernel event that is not valid anymore: thus, your exception.

Possible solutions:

  1. Do not serialize/deserialize the CancellationToken... instead, construct a different one upon deserializing.
  2. If that's not possible (because you need to use that one, and can't use other), then keep a reference of the CancellationTokenSource somewhere so that it doesn't get garbage-collected, and it never releases the kernel event, so the handle is still valid when deserializing
Jcl
  • 27,696
  • 5
  • 61
  • 92