如果我的问题太冗长,我很抱歉。我看了一个问题:“如何使用另一个类的线程正在接收的消息来更新GUI中的数据?”,这与我正在尝试做的非常接近,但是答案不够详细,无法提供帮助。是否有从工作线程切换到主(UI)线程?
我已经将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
您可能想查看[Asynchronous Programming](http://msdn.microsoft.com/zh-cn/library/hh191443.aspx)。 Nitpicking:'On Error GoTo'应该转换为'Try ... Catch'和'Dim myReadBuffer(BUFFER_SIZE)As Byte'分配一个多于你想要的数组元素(它应该是'BUFFER_SIZE - 1')。 –
我决定将我的答案提交给评论,因为它主要是一个链接而不是具体的信息。 如果代码在表单中,那么使用'InvokeRequired'和'Invoke'。以下是关于如何逐步构建解决方案的完整说明: http://www.vbforums.com/showthread.php?498387-访问 - 控制 - 从 - 工作线程 如果代码不在表单然后你无法访问这些成员。在这种情况下,你应该使用'SynchronizationContext'类。上面的链接提供了一个在以后的帖子中使用它的例子。 – jmcilhinney