English 中文(简体)
Interacting with the UI thread from an Async callback method?
原标题:

I have a method that is asynchronously called when System.Net.Sockets.NetworkStream.BeginRead completes.

 skDelegate = New AsyncCallback(AddressOf skDataReceived)
 skStream.BeginRead(skBuffer, 0, 100000, skDelegate, New Object)

In that callback method, I need to interact with the UI thread.

Sub skDataReceived(ByVal result As IAsyncResult)
    CType(My.Application.OpenForms.Item("frmMain"), frmMain).refreshStats(d1, d2)
End Sub

This causes an exception after the method completes. (when End Sub is executed)

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

So how do I interact with the UI thread from the callback method? What am I doing wrong?

最佳回答

You have to use Invoke or BeginInvoke on the frmMain object to enqueue a message (a delegate) to execute on the UI thread.

Here s how I d do it in C#.

frmMain.Invoke(() => frmMain.refreshStats(d1, d2));

Also check this list of Invoke types and their uses.

问题回答

Travis is correct. Windows forms application are single threaded, you can not access the UI from any other thread. You need to marshall the call to UI thread using BeginInvoke.

See : http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

You need to have the UI Thread invoke the frmMain.refreshStats method. There is of-course many ways of doing this using the Control.InvokeRequired property, and Control.Invoke (MSDN Documentation).

You can either have the "EndAsync" method make the method call UI thread safe, or have the refreshStats method check for thread safety (using Control.InvokeRequired).

EndAsync UI thread-safe would be something like this:

Public Delegate Sub Method(Of T1, T2)(ByVal arg1 As T1, ByVal arg2 As T2)

Sub skDataReceived(ByVal result As IAsyncResult)
    Dim frmMain As Form = CType(My.Application.OpenForms.Item("frmMain"), frmMain)
    Dim d As Method(Of Object, Object)
 create a generic delegate pointing to the refreshStats method
    d = New Method(Of Object, Object)(AddressOf frmMain.refreshStats)
 invoke the delegate under the UI thread
    frmMain.Invoke(d, New Object() {d1, d2})
End Sub

Or you can have the refreshStats method check to see if it needs to invoke itself under the UI thread:

Public Delegate Sub Method(Of T1, T2)(ByVal arg1 As T1, ByVal arg2 As T2)

Sub refreshStats(ByVal d1 As Object, ByVal d2 As Object)
 check to see if current thread is the UI thread
    If (Me.InvokeRequired = True) Then
        Dim d As Method(Of Object, Object)
 create a delegate pointing to itself
        d = New Method(Of Object, Object)(AddressOf Me.refreshStats)
 then invoke itself under the UI thread
        Me.Invoke(d, New Object() {d1, d2})
    Else
         actual code that requires UI thread safety goes here
    End If
End Sub

I found the solution (workaround, actually!) to that recurring InvalidContextException error that I got whenever I interacted or even read a property from a Form on the UI thread.

I had to backup and restore the execution context, before and after interacting with the UI thread from my Async callback method. Then the exception disappears as mysteriously as it appeared, and you can read/write properties, call methods and do basically anything you like with the UI thread, synchronously from your Async callback, without having to use delegates or invokes!

This exception is actually a LOW-level bug in the .NET framewok itself. See the Microsoft Connect bug report, but note that they list no functional workarounds.

Workaround: (production code)

Sub skDataReceived(ByVal result As IAsyncResult)

    // backup the context here
    Dim syncContext As SynchronizationContext = AsyncOperationManager.SynchronizationContext

    // interact with the UI thread
    CType(My.Application.OpenForms.Item("frmMain"), frmMain).refreshStats(d1, d2)

    // restore context.
    AsyncOperationManager.SynchronizationContext = syncContext
End Sub




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签