2009-11-15 3 views
0

У меня есть метод, который асинхронно вызывается, когда завершается System.Net.Sockets.NetworkStream.BeginRead.Взаимодействие с потоком пользовательского интерфейса от метода обратного вызова Async?

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

В этом методе обратного вызова мне необходимо взаимодействовать с потоком пользовательского интерфейса.

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

Это вызывает исключение после завершения метода. (При End Sub выполняется)

Операция отмены обнаружена контекст, который отличается от того, что был применен в соответствующем множестве операции. Возможная причина заключается в том, что контекст был установлен в потоке, а не отменен (отменен).

Как я могу взаимодействовать с потоком пользовательского интерфейса из метода обратного вызова? Что я делаю не так?

ответ

2

Вы должны использовать Invoke или BeginInvoke для объекта frmMain для размещения сообщения (делегата) для выполнения в потоке пользовательского интерфейса.

Вот как я сделал бы это на C#.

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

Также проверьте это list of Invoke types and their uses.

+0

Если Я вызываю непосредственно на frmMain, я получаю сообщение об ошибке, указывающее, что вызовы могут быть вызваны только на элемент управления после создания дескриптора окна. Поэтому я попытался использовать форму в коллекции OpenForms, но потом получаю ту же самую старую ошибку: «Операция отмены ...» Что я делаю неправильно? –

+0

Другими словами, ваше «решение» делает * НЕ * решает мою основную проблему, InvalidContextException. В любом случае, я нашел ответ на этот вопрос, если кто-нибудь встретит это снова. См. Мой ответ ниже. –

+0

Итак, в основном, я думаю, что этот код выполняется до того, как Windows создала дескриптор Win32 для окна формы. Мне любопытно, отображается ли окно до запуска этого кода? –

1

Трэвис прав. Приложение форм Windows однопоточно, вы не можете получить доступ к пользовательскому интерфейсу из любого другого потока. Вам нужно настроить маркер вызова потока пользовательского интерфейса с помощью BeginInvoke.

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

0

Я нашел решение (обходной путь, на самом деле!) В этой повторяющейся ошибки InvalidContextException, что я получил, когда я взаимодействовал или даже прочитать свойство из формы в потоке пользовательского интерфейса.

Мне пришлось создать резервную копию и восстановить контекст выполнения до и после взаимодействия с потоком пользовательского интерфейса из моего метода обратного вызова Async. Затем исключение исчезает как таинственно, как оно появилось, и вы можете читать/записывать свойства, методы вызова и делать в основном что угодно, используя поток пользовательского интерфейса, синхронно с вашим обратным вызовом Async, без необходимости использования делегатов или вызовов!

Это исключение на самом деле является ошибкой LOW-уровня в самом .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

Очень интересно. Хорошая работа. –

1

Вы должны иметь интерфейс Thread вызова метода frmMain.refreshStats. Существует много способов сделать это, используя свойство Control.InvokeRequired и Control.Invoke (MSDN Documentation).

Вы можете либо использовать метод «EndAsync», чтобы метод вызывал поток пользовательского интерфейса, либо проверял метод refreshStats на безопасность потоков (используя Control.InvokeRequired).

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, чтобы увидеть, если это необходимо, чтобы вызвать себя под поток пользовательского интерфейса:

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

Пробовал всех этих помощников, они просто не решают настоящего исключения! См. Мой ответ для обходного пути. –

Смежные вопросы