Заранее извиняюсь, если мой вопрос слишком многословен. Я посмотрел на вопрос «Как обновить данные в графическом интерфейсе сообщениями, которые получает поток другого класса?» и это очень близко к тому, что я пытаюсь сделать, но ответ не был достаточно подробным, чтобы быть полезным.
Я преобразовал приложение VB6 в VB.NET (VS2013). Основная функция приложения — отправлять запросы на сервер Linux и отображать результаты в форме вызова. Поскольку элемента управления WinSock больше не существует, я создал класс для обработки функций, связанных с классом TcpClient. Я могу успешно подключиться к серверу и отправлять и получать данные.
Проблема в том, что у меня есть несколько форм, которые используют этот класс для отправки запросов на сервер. Сервер отвечает данными, которые будут отображаться в вызывающей форме. Когда я пытаюсь обновить элемент управления в форме, я получаю сообщение об ошибке «Недействительная операция с несколькими потоками: доступ к элементу управления x осуществляется из потока, отличного от потока, в котором он был создан». Я знаю, что должен использовать Control.InvokeRequired вместе с Control.Invoke для обновления элементов управления в потоке Main/UI, но я не могу найти хороший, полный пример в VB. Кроме того, у меня есть более 50 форм с различными элементами управления на каждой форме, я действительно не хочу писать обработчик делегатов для каждого элемента управления. Я также должен упомянуть, что концепция потоков и делегатов очень нова для меня. Я читал все, что мог найти по этой теме за последнюю неделю или две, но я все еще застрял!
Есть ли способ просто вернуться к основному потоку? Если нет, есть ли способ использовать Control.Invoke только один раз, чтобы охватить множество элементов управления?
Я попытался запустить поток сразу после подключения, прежде чем начать отправлять и получать данные, но netStream.BeginRead запускает свой собственный поток после срабатывания функции обратного вызова. Я также пытался использовать Read вместо BeginRead. Это не сработало, если в ответе было большое количество данных, BeginRead справлялся с этим лучше. Я чувствую, что Дороти застряла в стране Оз, я просто хочу вернуться к основной теме!
Заранее благодарим за любую помощь, которую вы можете предоставить.
Option Explicit On
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Friend Class ATISTcpClient
Public Event Receive(ByVal data As String)
Private Shared WithEvents oRlogin As TcpClient
Private netStream As NetworkStream
Private BUFFER_SIZE As Integer = 8192
Private DataBuffer(BUFFER_SIZE) As Byte
Public Sub Connect()
Try
oRlogin = New Net.Sockets.TcpClient
Dim localIP As IPAddress = IPAddress.Parse(myIPAddress)
Dim localPrt As Int16 = myLocalPort
Dim ipLocalEndPoint As New IPEndPoint(localIP, localPrt)
oRlogin = New TcpClient(ipLocalEndPoint)
oRlogin.NoDelay = True
oRlogin.Connect(RemoteHost, RemotePort)
Catch e As ArgumentNullException
Debug.Print("ArgumentNullException: {0}", e)
Catch e As Net.Sockets.SocketException
Debug.Print("SocketException: {0}", e)
End Try
If oRlogin.Connected() Then
netStream = oRlogin.GetStream
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0, BUFFER_SIZE, _
AddressOf DataArrival, DataBuffer)
End If
Send(vbNullChar)
Send(User & vbNullChar)
Send(User & vbNullChar)
Send(Term & vbNullChar)
End If
End Sub
Public Sub Send(newData As String)
On Error GoTo send_err
If netStream.CanWrite Then
Dim sendBytes As [Byte]() = Encoding.UTF8.GetBytes(newData)
netStream.Write(sendBytes, 0, sendBytes.Length)
End If
Exit Sub
send_err:
Debug.Print("Error in Send: " & Err.Number & " " & Err.Description)
End Sub
Private Sub DataArrival(ByVal dr As IAsyncResult)
'This is where it switches to a WorkerThread. It never switches back!
On Error GoTo dataArrival_err
Dim myReadBuffer(BUFFER_SIZE) As Byte
Dim myData As String = ""
Dim numberOfBytesRead As Integer = 0
numberOfBytesRead = netStream.EndRead(dr)
myReadBuffer = DataBuffer
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Do While netStream.DataAvailable
numberOfBytesRead = netStream.Read(myReadBuffer, 0, myReadBuffer.Length)
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Loop
'Send data back to calling form
RaiseEvent Receive(myData)
'Start reading again in case we don‘t have the entire response yet
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0,BUFFER_SIZE,AddressOf DataArrival,DataBuffer)
End If
Exit Sub
dataArrival_err:
Debug.Print("Error in DataArrival: " & err.Number & err.Description)
End Sub
On Error GoTo
следует преобразовать вTry...Catch
, аDim myReadBuffer(BUFFER_SIZE) As Byte
назначает на один элемент массива больше, чем вы хотели (это должно бытьBUFFER_SIZE - 1
). - person Andrew Morton   schedule 30.04.2014InvokeRequired
иInvoke
. Вот полное пошаговое объяснение того, как создать решение: vbforums.com/ Если код не находится в форме, у вас нет доступа к этим членам. В этом случае вы должны использовать классSynchronizationContext
. Эта ссылка выше дает пример ее использования в более позднем посте. - person jmcilhinney   schedule 30.04.2014