我现在正在维护一个用 VB.net (.net 3.5) 编写的软件,它通过网络与在其他地方运行的另一个我无法控制的软件进行通信。我对 WinForms 软件或最佳实践不是很熟悉,但以下是我对该程序的了解:
当用户通过 UI 执行某些操作时,程序会向主机进程发送消息并等待响应返回,处理响应的结果,然后允许用户继续。在客户端和主机之间进行此对话时,必须锁定 UI。此对话可能需要两到三秒钟,具体取决于主持人所承受的压力。
该软件的编写者在通信进行时使用 while 循环阻塞 UI 线程。显然这不是一个好方法,因为它可能导致 Windows 报告程序已锁定并提供退出选项。
我的问题是,这样做的最佳做法是什么。winforms 程序有几个窗口,其中包含数百个按钮和控件,在通信期间必须阻止所有这些按钮和控件。通信(和等待)是数百种不同可能的用户与程序交互的结果。那么,如何在通信进行时阻止整个程序接受输入、按钮按下、键盘等?
这是一些关于它当前工作方式的精简代码。我不害怕重写这个程序的通信部分。
Private clientSocket As New System.Net.Sockets.TcpClient()
Private serverStream As NetworkStream
Private readThread As Thread = New Thread(AddressOf readLoop)
Public Sub Connect()
'This function called to start the connection when program starts
Try
clientSocket.Connect(_Address, _Port)
serverStream = clientSocket.GetStream()
readThread.IsBackground = True
readThreadActive = True
readThread.Start()
Catch ex As Exception
RaiseEvent communicationsErrorEvent("CONNECT: " & ex.Message.ToString)
End Try
End Sub
Public Sub WriterLogOn(ByVal UserName As String, ByVal Password As String)
'This Sub is called from a button press on a WinForm
Dim xmlString As String = "blah" 'generate xmlstring to send to host including username and encrypted password
communicationsState = state_WriterLogOn 'store the state from an enum
'each communication type has its own enum
write(xmlString)
While communicationsState = state_WriterLogOn
Thread.Sleep(1)
End While
End Sub
Private Sub write(ByVal writeString As String)
Try
Dim outStream As Byte() = System.Text.Encoding.ASCII.GetBytes(writeString & Chr(0))
serverStream.Write(outStream, 0, outStream.Length)
serverStream.Flush()
Catch ex As Exception
RaiseEvent communicationsErrorEvent("WRITE: " & ex.Message)
End Try
End Sub
Private Sub readLoop()
Try
While (readThreadActive) 'always true unless changed in certain conditions
Dim buffSize As Integer
Dim inStream(10024) As Byte
buffSize = clientSocket.ReceiveBufferSize
Dim numberOfBytesRead As Integer = 0
Dim returnDataBuilder As StringBuilder = New StringBuilder
Do
numberOfBytesRead = serverStream.Read(inStream, 0, inStream.Length)
returnDataBuilder.AppendFormat("{0}", Encoding.ASCII.GetString(inStream, 0, numberOfBytesRead))
Loop While serverStream.DataAvailable
Dim returnData As String = returnDataBuilder.ToString()
If (returnData.Length > 0) Then
ParseMessage(returnData)
End If
Threading.Thread.Sleep(1)
End While
Catch ex As Exception
RaiseEvent communicationsErrorEvent("READLOOP: " & ex.Message)
End Try
End Sub
Private Sub ParseMessage(ByVal readdata As String)
'This function parses the string received and does the appropriate things with it
'And when appropriate it sets the state back to idle, allowing the UI thread to unblock
communicationsState = state_Idle
End Sub
读取循环必须始终运行,因为它还可以随时接收未经请求的消息,这些消息必须在后台处理而不会阻塞 UI 线程。(这是从 ParseMessage() 子完成的)。
所以,我知道这是处理阻塞 UI 线程的不好方法。但是在四处搜索之后,我还没有找到一个好的解决方案来停止应用程序范围内的所有用户输入,直到在不同线程中收到的一些其他通信释放它。
我已经尝试将代码插入到各种 UI 方法中,这些方法检查状态并停止按钮/键盘/等执行任何操作,但是我需要在很多不同的地方插入此代码,感觉这样做是错误的方法。我四处搜索,但只找到有关如何不阻止 UI 线程的问题。
如果没有 UI 线程中的 Threading.Sleep,如何处理?模态形式是处理这个问题的方法吗?
提前致谢!