8

I'm having problems with the above much asked-about error. We have a TCP/IP server application which has been working fine for some years. I now need to allow the application to accept connections from directly connected USB devices, by internally using a socket connection to patch through to localhost (127.0.0.1) within the server application. (BTW I mention the USB only to explain why I am doing this - I disabled all USB functions as part of debugging this problem).

The communications along this socket can result in calls to GUI elements on both the client and the server side. Accesses to GUI elements on the client side cause the error in the title (call stack below). One of the key problems here is that the debugger is unable to halt on the exception: despite all exceptions being set to halt when thrown the application simply terminates when the error occurs.

The only thing that seems unique about my application is that it uses an internal socket to connect to 127.0.0.1. I have also confirmed that the application works fine if the client is separated into a separate application. However, I can't use this as a permanent solution for other reasons.

There are several posts discussing this sort of problem which I have listed below. Unfortunately none seem to provide a solution in my case:

  • Most related posts discuss the need to ensure that all GUI operations are performed on the GUI thread, by using Invoke or BeginInvoke. I am confident my application does this correctly (it obtains a form using Application.Forms to get the main form and calls Invoke on this) and have double checked in the debugger.
  • Relating to the above, there is some discussion as to the use of Invoke vs BeginInvoke in order to block/not block. In my case both have the same result.
  • Some posts suggest it is necessary to create the sockets themselves on the GUI thread (mine are).
  • This one explains that you can get the error if you use DoEvents in your application (I don't).
  • This one also implies that you could get the error with a missing EndConnect call when using asynchronous calls for client socket connection (my client connection is synchronous).
  • This one explains that you can get incorrect results from InvokeRequired if the window handle is not yet created (have checked this with IsHandleCreated).
  • This one on microsoft connect reports a similar sounding bug but doesnt have a solution (microsoft have been 'investigating' it since 2006!)
  • This one contains a suggestion to use AsyncOperationManager.SynchronizationContext to backup/restore the synchronsiation context, which (unsurprisingly?) just causes different errors.
  • There are a couple of posts that suggest that the error is debug only, and the following will make it go away - but I haven't bothered trying that:
    System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false

There are other posts asking similar questions: here, here and here. A good one here too.

Here is a code snippet - this causes a crash within ProcessCommandCT when socket data is received by the client:

' Find application main form from any thread
' There is only one instance of 'RibbonForm1' and this is the main form
Public Function GetRibbonForm() As RibbonForm1
    Dim rf As RibbonForm1 = Nothing
    For Each f As Form In My.Application.OpenForms
        rf = TryCast(f, RibbonForm1)
        If rf IsNot Nothing Then Return rf
    Next
    Return Nothing
End Function

Public Sub ProcessCommandCT(ByVal cmd As String)
    ' code is peppered with these to debug this problem
    Debug.Assert(GetRibbonForm.IsHandleCreated)
    Debug.Assert(Not GetRibbonForm.InvokeRequired)
    Try
        Select Case cmd
            Case "MYCMD"
                Dim f As New Form 
                f.ShowDialog()
        End Select
    Catch ex As Exception
        MsgBox(ex.ToString)
    End Try

End Sub

Private Sub sock_Receive(ByVal msg As String) Handles sck.Receive
    Dim rf As RibbonForm1 = GetRibbonForm
    If rf.InvokeRequired Then
        rf.BeginInvoke(New SubWithStringArgDelegate(AddressOf ProcessCommandCT), New Object() {msg})
    Else
        ProcessCommandCT(msg)
    End If
End Sub

I'm using VB .NET 2010 with .NET4.

Thanks for any help - I hope the consolidated list of posts above also helps others.

Tim

Call stack:

The thread '<No Name>' (0x148c) has exited with code 0 (0x0).
System.Transactions Critical: 0 : <TraceRecord    xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>myapp.vshost.exe</AppDomain><Exception><ExceptionType>System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The Undo operation encountered a context that is different from what was applied in the corresponding Set operation. The possible cause is that a context was Set on the thread and not reverted(undone).</Message><StackTrace>   at System.Threading.SynchronizationContextSwitcher.Undo()
at System.Threading.ExecutionContextSwitcher.Undo()
at System.Threading.ExecutionContext.runFinallyCode(Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteBackoutCodeHelper(Object backoutCode, Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)</StackTrace><ExceptionString>System.InvalidOperationException: The Undo operation encountered a context that is different from what was applied in the corresponding Set operation. The possible cause is that a context was Set on the thread and not reverted(undone).
at System.Threading.SynchronizationContextSwitcher.Undo()
at System.Threading.ExecutionContextSwitcher.Undo()
at System.Threading.ExecutionContext.runFinallyCode(Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteBackoutCodeHelper(Object backoutCode, Object userData, Boolean exceptionThrown)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)</ExceptionString></Exception></TraceRecord>
The program '[6324] myapp.vshost.exe: Managed (v4.0.30319)' has exited with code 0 (0x0).
Community
  • 1
  • 1
ttt
  • 164
  • 1
  • 10
  • Try to set Control.CheckForIllegalCrossThreadCalls Property to true in the beginning of the program. – Alex F Sep 28 '10 at 12:08
  • Thanks Alex - CheckForIllegalCrossThreadCalls is true by default (and is set to true in my application). I mention in my post that disabling it probably prevents the error being reported - but I'm obviously not interested in doing that. – ttt Sep 28 '10 at 12:53
  • 1
    Post real code, nobody has a chance to diagnose it when you edit it. – Hans Passant Sep 28 '10 at 13:40
  • I should have mentioned that one of the key problems here is that the debugger is unable to halt on the exception, despite all exceptions being set to halt when thrown. Unfortunately it all points to something conceptually much deeper than an unexpectedly null variable. As Hans says, next step is to invest some time in a minimal application that reproduces the problem. In the meantime, I hope the list of possible causes of this exception are of use to others, and also that someone can add to the list from their own experience. – ttt Sep 28 '10 at 14:30
  • Good related page here: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html – ttt Sep 28 '10 at 16:01
  • Blocking call to ShowDialog in response to each receive? Is this real code? Even with BeginInvoke launching it you are tying up at least the threadpool thread being used indefinitely, plus I assume it creates a lot of stacked modal msg loops? – tcarvin Apr 12 '12 at 22:01

2 Answers2

2

This exception occurs when a thread's ExecutionContext property changes. Specifically when that thread is a threadpool or I/O completion thread that executes a callback and it acquired its ExecutionContext from another thread that made a BeginXxx call to start an asynchronous operation. Like Socket.BeginReceive().

There's ample of opportunity for this to happen in the posted code since it tinkers with forms in the callback. ExecutionContext has a hidden property named SynchronizationContext which keeps track of SynchronizationContext.Current. Winforms installs a custom synchronization provider the first time any form is created. Required to properly marshal calls from a worker thread to the UI thread. It is a class derived from SynchronizationContext named WindowsFormsSynchronizationContext.

The likely failure mode therefore is that the sock_Receive() method is called before any Winforms forms are created. With the form creation code installing the synchronization provider and altering the ExecutionContext and thus crashing the code with the exception. Such a problem needs to be fixed by altering the initialization of the app, ensuring that a main form exists before you allow any asynchronous code to use BeginInvoke().

Hans Passant
  • 873,011
  • 131
  • 1,552
  • 2,371
  • In my case, I thought all of my code that executed on an async callback (from the ThreadPool) was being properly Invoke()d, but I guess it wasn't. I used `SyncronizationContext.Send()` (after capturing the original ctx in the ctor) to raise all of my events in question, and this problem went away. – Jonathon Reinhart Apr 14 '12 at 21:18
  • What if I can't created the main before? I need to download stuff from the socket before I open the main form, would another temporary form do the job? Where if the reference for that? – Zyo Sep 20 '13 at 17:16
  • Zyo: Why don't you show a progress Form that displays the download progress? This must be showing before the download starts. – Elmue Mar 27 '17 at 16:48
0

Can you show the code where you are issuing and ending IO?

Here is a possible workaround: Let's set the synchronizationcontext.current to null while you are beginning all IO operations. It looks like something in the .NET framework gets confused and is trying to restore the executioncontext twice.

Here is some useful helper:

    public static void ChangeSynchronizationContext(SynchronizationContext synchronizationContext, Action actionUnderSynchronizationContext)
    {
        var oldSyncContext = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(synchronizationContext);

        try
        {
            actionUnderSynchronizationContext();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(oldSyncContext);
        }
    }

Call it like this:

ChangeSynchronizationContext(null, () => { /* start io */ });

Another question: Are you nesting IO calls? Are you issuing many small IOs on this socket? I am asking this because the framework has a special case here:

        if (currentThreadContext.m_NestedIOCount >= 50)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(this.WorkerThreadComplete));
            flag = true;
        }
        else
        {
            this.m_AsyncCallback(this);
        }

This piece of code gives rise to the suspicion that there is a bug in the .NET framework in this rarely hit case. ThreadPool.QueueUserWorkItem will capture the current ExecutionContext and restore it later which is what we are seeing in the stack trace.

usr
  • 162,013
  • 33
  • 219
  • 345