Pregunta

Tengo un método que se forma asíncrona llama cuando System.Net.Sockets.NetworkStream.BeginRead completa.

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

En ese método de devolución de llamada, necesito para interactuar con el hilo de interfaz de usuario.

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

Esto provoca una excepción después de que el método se completa. (Cuando se ejecuta End Sub)

  

La operación de deshacer se encontró con una   contexto que es diferente de lo   se aplicó en el conjunto correspondiente   operación. La posible causa es que   un contexto se estableció en la rosca y   no revertido (deshacer).

Entonces, ¿cómo interactúo con el hilo de interfaz de usuario a partir del método de devolución de llamada? ¿Qué estoy haciendo mal?

¿Fue útil?

Solución

Usted tiene que usar invocar o BeginInvoke en el objeto frmMain a poner en cola un mensaje (un delegado) para ejecutar en el subproceso de interfaz de usuario.

Así es como lo haría en C #.

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

También puedes ver esta lista de tipos de invocar y sus usos .

Otros consejos

Travis es correcta. Aplicación de Windows Forms son de un solo subproceso, no se puede acceder a la interfaz de usuario de cualquier otro hilo. Que necesita para ordenar de la llamada al hilo de interfaz de usuario mediante BeginInvoke.

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

Es necesario tener el hilo de interfaz de usuario invocar el método frmMain.refreshStats. Hay, por supuesto, muchas maneras de hacer esto utilizando la propiedad Control.InvokeRequired y Control.Invoke ( Documentación de MSDN ).

Usted puede tener el método "EndAsync" hacer que el hilo de interfaz de usuario llamada a un método seguro, o tener el cheque método refreshStats para la seguridad de los subprocesos (usando Control.InvokeRequired).

EndAsync UI thread-safe sería algo como esto:

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

O puede hacer que el cheque método refreshStats para ver si necesita invocar sí mismo bajo el hilo de interfaz de usuario:

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

He encontrado la solución (solución, en realidad!) Para que el error InvalidContextException recurrente que tengo cada vez que interactuaba o incluso leer una propiedad de un formulario en el subproceso de interfaz de usuario.

Yo tenía una copia de seguridad y restaurar el contexto de ejecución, antes y después de interactuar con el hilo de interfaz de usuario de mi método de devolución de llamada asincrónica. Entonces la excepción desaparece tan misteriosamente como apareció, y se puede leer las propiedades de lectura / escritura, los métodos de llamar y hacer básicamente cualquier cosa que quiera con el hilo de interfaz de usuario, de forma sincronizada desde su devolución de llamada asincrónica, sin tener que utilizar delegados o invoca!

Esta excepción es en realidad un error de bajo nivel en el .NET Framewok sí. Vea la de Microsoft Connect informe de error , pero tenga en cuenta que la lista no hay soluciones funcionales.

Solución: (código de producción)

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top