8

上記のよくあるエラーに問題があります。数年間正常に動作している TCP/IP サーバー アプリケーションがあります。次に、ソケット接続を内部的に使用してサーバー アプリケーション内の localhost (127.0.0.1) にパッチを適用することにより、アプリケーションが直接接続された USB デバイスからの接続を受け入れるようにする必要があります。(ところで、私がこれを行っている理由を説明するためだけに USB について言及します。この問題のデバッグの一環として、すべての USB 機能を無効にしました)。

このソケットに沿った通信により、クライアント側とサーバー側の両方で GUI 要素が呼び出される可能性があります。クライアント側で GUI 要素にアクセスすると、タイトルにエラーが発生します (以下のコール スタック)。ここでの重要な問題の 1 つは、デバッガーが例外で停止できないことです。すべての例外がスローされたときに停止するように設定されているにもかかわらず、エラーが発生すると、アプリケーションは単純に終了します。

私のアプリケーションの唯一の特徴は、内部ソケットを使用して 127.0.0.1 に接続していることです。また、クライアントを別のアプリケーションに分離した場合、アプリケーションが正常に動作することも確認しています。ただし、他の理由により、これを永続的な解決策として使用することはできません。

この種の問題について議論している投稿がいくつかあります。それらを以下にリストします。残念ながら、私の場合、解決策を提供するものはないようです:

  • ほとんどの関連記事では、Invoke または BeginInvoke を使用して、すべての GUI 操作が GUI スレッドで実行されるようにする必要性について説明しています。私のアプリケーションはこれを正しく行い (Application.Forms を使用してフォームを取得し、メイン フォームを取得し、これに対して Invoke を呼び出します)、デバッガーでダブル チェックしたと確信しています。
  • 上記に関連して、ブロックする/ブロックしないための Invoke と BeginInvoke の使用に関するいくつかの議論があります。私の場合、どちらも同じ結果になります。
  • 一部の投稿では、GUI スレッドでソケット自体を作成する必要があることが示唆されています (私の場合)。
  • これは、アプリケーションで DoEvents を使用するとエラーが発生する可能性があることを説明しています (私は使用しません)。
  • これは、クライアント ソケット接続に非同期呼び出しを使用している場合 (私のクライアント接続は同期)、EndConnect 呼び出しが見つからないというエラーが発生する可能性があることも意味します。
  • これは、ウィンドウ ハンドルがまだ作成されていない場合に InvokeRequired から誤った結果が得られる可能性があることを説明しています (これは IsHandleCreated で確認済みです)。
  • これは microsoft connectで同様のバグを報告していますが、解決策はありません (microsoft は 2006 年から「調査」しています!)
  • これには、AsyncOperationManager.SynchronizationContext を使用して同期コンテキストをバックアップ/復元するという提案が含まれていますが、これは (当然のことですが?) さまざまなエラーを引き起こすだけです。
  • エラーがデバッグのみであることを示唆する投稿がいくつかありますが、次のようにすれば解消されますが、それを試してみることはありませんでした:
    System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false

同様の質問をしている他の投稿があります: herehere、および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

.NET4 で VB .NET 2010 を使用しています。

助けてくれてありがとう - 上記の投稿の統合リストが他の人にも役立つことを願っています.

ティム

コール スタック:

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).
4

2 に答える 2

2

この例外は、スレッドのExecutionContextプロパティが変更されたときに発生します。具体的には、そのスレッドがコールバックを実行するスレッドプールまたはI / O完了スレッドであり、非同期操作を開始するためにBeginXxx呼び出しを行った別のスレッドからExecutionContextを取得した場合。Socket.BeginReceive()のように。

コールバックのフォームをいじくり回すため、投稿されたコードでこれが発生する可能性は十分にあります。ExecutionContextには、SynchronizationContext.Currentを追跡するSynchronizationContextという名前の非表示のプロパティがあります。Winformsは、フォームが最初に作成されたときにカスタム同期プロバイダーをインストールします。ワーカースレッドからUIスレッドへの呼び出しを適切にマーシャリングするために必要です。これは、WindowsFormsSynchronizationContextという名前のSynchronizationContextから派生したクラスです。

したがって、失敗する可能性のあるモードは、Winformsフォームが作成される前にsock_Receive()メソッドが呼び出されることです。フォーム作成コードを使用して、同期プロバイダーをインストールし、ExecutionContextを変更して、例外を除いてコードをクラッシュさせます。このような問題は、アプリの初期化を変更して修正する必要があります。非同期コードでBeginInvoke()を使用できるようにする前に、メインフォームが存在することを確認してください。

于 2012-04-13T00:51:34.607 に答える
0

IO を発行して終了するコードを表示できますか?

考えられる回避策は次のとおりです。すべての IO 操作を開始している間に、syncrectioncontext.current を null に設定しましょう。.NET フレームワークの何かが混乱し、実行コンテキストを 2 回復元しようとしているようです。

便利なヘルパーを次に示します。

    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 をキャプチャし、後で復元します。これはスタック トレースで確認できます。

于 2012-04-12T21:43:32.803 に答える