我遇到上述问题很多的问题。我们有一个TCP / IP服务器应用程序,已经运行了好几年。我现在需要允许应用程序接受来自直接连接的USB设备的连接,通过内部使用套接字连接来修补服务器应用程序中的localhost(127.0.0.1)。 (顺便说一句,我只提到USB来解释我为什么这样做 - 我禁用所有USB功能作为调试此问题的一部分)。
沿着此套接字的通信可能导致在客户端和服务器端调用GUI元素。访问客户端上的GUI元素会导致标题中的错误(下面的调用堆栈)。这里的一个关键问题是调试器无法暂停异常:尽管在抛出时将所有异常设置为暂停,但应用程序只是在发生错误时终止。
我的应用程序唯一看起来独一无二的是它使用内部套接字连接到127.0.0.1。我还确认,如果将客户端分成单独的应用程序,该应用程序可以正常工作。但是,由于其他原因,我无法将其用作永久解决方案。
有几篇帖子讨论了我在下面列出的这类问题。不幸的是,在我的案例中似乎没有提供解决方案:
还有其他帖子提出类似问题:here,here和here。一个好的here。
以下是代码段 - 当客户端收到套接字数据时,这会导致ProcessCommandCT内发生崩溃:
' 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
我正在使用.NET .NET 2010和.NET4。
感谢您的帮助 - 我希望上面的综合列表也能帮助其他人。
添
调用堆栈:
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).
答案 0 :(得分:2)
当线程的ExecutionContext属性更改时,会发生此异常。特别是当该线程是执行回调的线程池或I / O完成线程时,它从另一个进行BeginXxx调用的线程获取其ExecutionContext以启动异步操作。像Socket.BeginReceive()。
在发布的代码中有很多机会发生这种情况,因为它在回调中有表单。 ExecutionContext有一个名为SynchronizationContext的隐藏属性,用于跟踪SynchronizationContext.Current。 Winforms会在第一次创建任何表单时安装自定义同步提供程序。正确编组从工作线程到UI线程的调用所必需的。它是一个派生自SynchronizationContext的类,名为WindowsFormsSynchronizationContext。
因此,可能的失败模式是在创建任何Winforms表单之前调用sock_Receive()方法。使用表单创建代码安装同步提供程序并更改ExecutionContext,从而使代码崩溃并发生异常。这个问题需要通过改变应用程序的初始化来修复,确保在允许任何异步代码使用BeginInvoke()之前存在主表单。
答案 1 :(得分:0)
您能显示发布和结束IO的代码吗?
这是一个可能的解决方法:在开始所有IO操作时,让我们将synchronizationcontext.current设置为null。看起来.NET框架中的某些内容变得混乱,并且正在尝试两次恢复executioncontext。
这是一些有用的帮手:
public static void ChangeSynchronizationContext(SynchronizationContext synchronizationContext, Action actionUnderSynchronizationContext)
{
var oldSyncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
try
{
actionUnderSynchronizationContext();
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSyncContext);
}
}
这样称呼:
ChangeSynchronizationContext(null, () => { /* start io */ });
另一个问题:您是否正在嵌套IO呼叫?您是否在此套接字上发布了许多小型IO?我问这个是因为框架在这里有一个特例:
if (currentThreadContext.m_NestedIOCount >= 50)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.WorkerThreadComplete));
flag = true;
}
else
{
this.m_AsyncCallback(this);
}
这段代码引起了人们的怀疑,在这个很少遇到的情况下,.NET框架中存在一个错误。 ThreadPool.QueueUserWorkItem将捕获当前的ExecutionContext并在以后恢复它,这是我们在堆栈跟踪中看到的。