如果我的問題太冗長,我很抱歉。我看了一個問題:「如何使用另一個類的線程正在接收的消息來更新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