2009-11-15 50 views
0

System.Net.Sockets.NetworkStream.BeginRead完成时,我有一个异步调用的方法。从异步回调方法与UI线程交互?

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

在该回调方法中,我需要与UI线程进行交互。

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

该方法完成后会导致异常。 (执行End Sub当)

撤消操作时遇到 上下文是从什么 在相应的集 操作施加不同。可能的原因是 上下文在线程上设置,并且 未还原(撤消)。

那么如何从回调方法与UI线程进行交互?我究竟做错了什么?

回答

2

你必须使用调用或BeginInvoke的frmMain对象入队的消息(委托)到UI线程上执行。

下面是我如何在C#中完成它。

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

还检查这个list of Invoke types and their uses

+0

如果我直接调用frmMain,我得到一个错误,指出调用只能在窗口句柄创建后在控件上调用。所以我试图在OpenForms集合中使用表单,但是我得到了同样的旧错误:“撤消操作...”我做错了什么? – 2009-11-15 06:48:03

+0

换句话说,你的“解决方案”不*解决我的基本问题,即InvalidContextException。无论如何,如果有人再次遇到这个问题,我已经找到了答案。请参阅下面的答案。 – 2009-11-15 07:46:02

+0

所以基本上我认为是在Windows为窗体窗口创建Win32句柄之前执行此代码。我很好奇,是代码运行之前显示的窗口? – 2009-11-15 20:33:11

0

我找到了解决办法(解决方法,实际上!)给我,每当我互动,甚至从UI线程上构成读出的属性是经常性InvalidContextException错误。

我不得不备份和恢复的执行上下文,前后从我的异步回调方法UI线程交互之后。然后,异常消失,就像它出现的神秘,你可以读/写属性,调用方法,并基本上做任何你喜欢的用户界面线程,同步你的异步回调,而不必使用委托或调用!

这个异常实际上是.NET framewok本身的一个低级错误。请参阅Microsoft Connect bug report,但请注意,它们没有列出功能变通方法。

解决方法:(产品代码)

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 
+1

非常有趣。好工作。 – 2009-11-15 20:35:55

1

你需要有UI线程调用frmMain.refreshStats方法。使用Control.InvokeRequired属性和Control.Invoke(MSDN Documentation)这样做当然有许多方法。

你可以有“EndAsync”的方法使该方法调用UI线程安全的,或有(使用Control.InvokeRequired)线程安全的refreshStats方法检查。

EndAsync UI线程安全的将是这样的:

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 

或者你可以有refreshStats方法检查,看是否需要调用自身的UI线程下:

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 
+0

试过所有这些伴侣,他们只是不解决实际的例外!请参阅我的答案以找到解决方法。 – 2009-11-17 03:42:37