2.11.0
This commit is contained in:
@@ -467,7 +467,7 @@ Public Module ModAnimation
|
||||
''' 按照 WPF 方式旋转控件的动画。
|
||||
''' </summary>
|
||||
''' <param name="Obj">动画的对象。它必须已经拥有了单一的 ScaleTransform 值。</param>
|
||||
''' <param name="Value">大小改变的百分比(如-0.6)。</param>
|
||||
''' <param name="Value">旋转角度(度)。</param>
|
||||
''' <param name="Time">动画长度(毫秒)。</param>
|
||||
''' <param name="Delay">动画延迟执行的时间(毫秒)。</param>
|
||||
''' <param name="Ease">插值器类型。</param>
|
||||
@@ -792,44 +792,46 @@ Public Module ModAnimation
|
||||
AniFPSTimer = AniLastTick
|
||||
AniRunning = True '标记动画执行开始
|
||||
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
Log("[Animation] 动画线程开始")
|
||||
Do While True
|
||||
'两帧之间的间隔时间
|
||||
Dim DeltaTime As Long = MathClamp(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
If DeltaTime < 3 Then GoTo Sleeper
|
||||
AniLastTick = GetTimeTick()
|
||||
'记录 FPS
|
||||
If ModeDebug Then
|
||||
If MathClamp(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
AniFPS = AniFPSCounter
|
||||
AniFPSCounter = 0
|
||||
AniFPSTimer = AniLastTick
|
||||
End If
|
||||
AniFPSCounter += 2
|
||||
End If
|
||||
'执行动画
|
||||
RunInUiWait(Sub()
|
||||
AniCount = 0
|
||||
AniTimer(DeltaTime * AniSpeed)
|
||||
'#If DEBUG Then
|
||||
' FrmMain.Title = "F " & AniFPS & ", A " & AniCount & ", R " & NetManage.FileRemain
|
||||
'#Else
|
||||
' If ModeDebug Then FrmMain.Title = "FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManage.FileRemain
|
||||
'#End If
|
||||
If RandomInteger(0, 64 * If(ModeDebug, 5, 30)) = 0 AndAlso ((AniFPS < 62 AndAlso AniFPS > 0) OrElse AniCount > 4 OrElse NetManager.FileRemain <> 0) Then
|
||||
Log("[Report] FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManager.FileRemain & "(" & GetString(NetManager.Speed) & "/s)")
|
||||
End If
|
||||
End Sub)
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Log("[Animation] 动画线程开始")
|
||||
Do While True
|
||||
'两帧之间的间隔时间
|
||||
Dim DeltaTime As Long = MathClamp(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
If DeltaTime < 3 Then GoTo Sleeper
|
||||
AniLastTick = GetTimeTick()
|
||||
'记录 FPS
|
||||
If ModeDebug Then
|
||||
If MathClamp(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
AniFPS = AniFPSCounter
|
||||
AniFPSCounter = 0
|
||||
AniFPSTimer = AniLastTick
|
||||
End If
|
||||
AniFPSCounter += 2
|
||||
End If
|
||||
'执行动画
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
AniCount = 0
|
||||
AniTimer(DeltaTime * AniSpeed)
|
||||
'#If DEBUG Then
|
||||
' FrmMain.Title = "F " & AniFPS & ", A " & AniCount & ", R " & NetManage.FileRemain
|
||||
'#Else
|
||||
' If ModeDebug Then FrmMain.Title = "FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManage.FileRemain
|
||||
'#End If
|
||||
If RandomInteger(0, 64 * If(ModeDebug, 5, 30)) = 0 AndAlso ((AniFPS < 62 AndAlso AniFPS > 0) OrElse AniCount > 4 OrElse NetManager.FileRemain <> 0) Then
|
||||
Log("[Report] FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManager.FileRemain & "(" & GetString(NetManager.Speed) & "/s)")
|
||||
End If
|
||||
End Sub)
|
||||
Sleeper:
|
||||
'控制 FPS
|
||||
Thread.Sleep(1)
|
||||
Loop
|
||||
Catch ex As Exception
|
||||
Log(ex, "动画帧执行失败", LogLevel.Critical)
|
||||
End Try
|
||||
End Sub, "Animation", ThreadPriority.AboveNormal)
|
||||
'控制 FPS
|
||||
Thread.Sleep(1)
|
||||
Loop
|
||||
Catch ex As Exception
|
||||
Log(ex, "动画帧执行失败", LogLevel.Critical)
|
||||
End Try
|
||||
End Sub, "Animation", ThreadPriority.AboveNormal)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
@@ -996,7 +998,7 @@ NextAni:
|
||||
Case AniType.ScaleTransform
|
||||
Dim Obj As FrameworkElement = Ani.Obj
|
||||
If TypeOf Obj.RenderTransform IsNot ScaleTransform Then
|
||||
Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
If Obj.RenderTransformOrigin = New Point(0, 0) Then Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
Obj.RenderTransform = New ScaleTransform(1, 1)
|
||||
End If
|
||||
Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.TimeFinished / Ani.TimeTotal, Ani.TimePercent))
|
||||
@@ -1006,7 +1008,7 @@ NextAni:
|
||||
Case AniType.RotateTransform
|
||||
Dim Obj As FrameworkElement = Ani.Obj
|
||||
If TypeOf Obj.RenderTransform IsNot RotateTransform Then
|
||||
Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
If Obj.RenderTransformOrigin = New Point(0, 0) Then Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
Obj.RenderTransform = New RotateTransform(0)
|
||||
End If
|
||||
Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.TimeFinished / Ani.TimeTotal, Ani.TimePercent))
|
||||
|
||||
@@ -13,13 +13,13 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.10.9" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.10.9." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const VersionBaseName As String = "2.11.0" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.11.0." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
|
||||
#If BETA Then
|
||||
Public Const VersionCode As Integer = 369 'Release
|
||||
Public Const VersionCode As Integer = 372 'Release
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 371 'Snapshot
|
||||
Public Const VersionCode As Integer = 373 'Snapshot
|
||||
#End If
|
||||
'自动生成的版本信息
|
||||
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
|
||||
@@ -498,6 +498,7 @@ Public Module ModBase
|
||||
RealNum += Digit * Scale
|
||||
Scale *= FromRadix
|
||||
Next
|
||||
If RealNum = 0 Then Return "0"
|
||||
'转换为指定进制
|
||||
Dim Result = ""
|
||||
While RealNum > 0
|
||||
@@ -1300,10 +1301,12 @@ Re:
|
||||
Dim TotalCount As Long = Archive.Entries.Count
|
||||
For Each Entry As ZipArchiveEntry In Archive.Entries
|
||||
If ProgressIncrementHandler IsNot Nothing AndAlso TotalCount > 0 Then ProgressIncrementHandler(1 / TotalCount)
|
||||
Dim DestinationPath As String = IO.Path.Combine(DestDirectory, Entry.FullName)
|
||||
If DestinationPath.EndsWithF("\") OrElse DestinationPath.EndsWithF("/") Then Continue For '不创建空文件夹
|
||||
Directory.CreateDirectory(GetPathFromFullPath(DestinationPath))
|
||||
Entry.ExtractToFile(DestinationPath, True)
|
||||
Dim DestinationFullPath As String = IO.Path.Combine(DestDirectory, Entry.FullName)
|
||||
If Not IO.Path.GetFullPath(DestinationFullPath).StartsWithF(IO.Path.GetFullPath(DestDirectory), True) Then Continue For 'ZipSlip 漏洞修复
|
||||
If DestinationFullPath.EndsWithF("\") OrElse DestinationFullPath.EndsWithF("/") Then Continue For '不创建空文件夹
|
||||
Dim DestinationDirectory As String = GetPathFromFullPath(DestinationFullPath)
|
||||
Directory.CreateDirectory(DestinationDirectory)
|
||||
Entry.ExtractToFile(DestinationFullPath.Replace(DestinationDirectory, ShortenPath(DestinationDirectory)), True) '#7329
|
||||
Next
|
||||
End Using
|
||||
End If
|
||||
@@ -2368,7 +2371,7 @@ NextElement:
|
||||
''' <param name="Arguments">运行参数。</param>
|
||||
''' <param name="Timeout">等待该程序结束的最长时间(毫秒)。超时会抛出错误。</param>
|
||||
Public Function StartProcessAndGetOutput(FileName As String, Optional Arguments As String = "", Optional Timeout As Integer = 1000000,
|
||||
Optional WorkingDirectory As String = Nothing) As String
|
||||
Optional WorkingDirectory As String = Nothing, Optional Encoding As Encoding = Nothing, Optional PrintLog As Boolean = True) As String
|
||||
Dim Info = New ProcessStartInfo With {
|
||||
.Arguments = Arguments,
|
||||
.FileName = ShortenPath(FileName),
|
||||
@@ -2376,7 +2379,9 @@ NextElement:
|
||||
.CreateNoWindow = True,
|
||||
.RedirectStandardError = True,
|
||||
.RedirectStandardOutput = True,
|
||||
.WorkingDirectory = ShortenPath(If(WorkingDirectory, Path.TrimEnd("\"c)))
|
||||
.WorkingDirectory = ShortenPath(If(WorkingDirectory, Path.TrimEnd("\"c))),
|
||||
.StandardErrorEncoding = Encoding,
|
||||
.StandardOutputEncoding = Encoding
|
||||
}
|
||||
If WorkingDirectory IsNot Nothing Then
|
||||
If Info.EnvironmentVariables.ContainsKey("appdata") Then
|
||||
@@ -2385,7 +2390,7 @@ NextElement:
|
||||
Info.EnvironmentVariables.Add("appdata", WorkingDirectory)
|
||||
End If
|
||||
End If
|
||||
Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
|
||||
If PrintLog Then Log("[System] 执行命令并等待返回结果:" & FileName & " " & Arguments)
|
||||
Using Program As New Process With {.StartInfo = Info}
|
||||
Program.Start()
|
||||
Dim Result As String = Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
|
||||
@@ -3111,7 +3116,7 @@ Retry:
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 取随机整数。
|
||||
''' 取随机整数(包含)。
|
||||
''' </summary>
|
||||
Public Function RandomInteger(min As Integer, max As Integer) As Integer
|
||||
Return Math.Floor((max - min + 1) * Random.NextDouble) + min
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
Implements ILoadingTrigger
|
||||
|
||||
Public ReadOnly Property IsLoader As Boolean = True Implements ILoadingTrigger.IsLoader
|
||||
Protected Shared LogBlackList As String() = {"EasyTier CLI"}
|
||||
|
||||
'基础属性
|
||||
''' <summary>
|
||||
@@ -89,7 +90,7 @@
|
||||
Dim OldState = _State
|
||||
If value = LoadState.Finished AndAlso Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(100, 2000))
|
||||
_State = value
|
||||
Log("[Loader] 加载器 " & Name & " 状态改变:" & GetStringFromEnum(value))
|
||||
If Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载器 {Name} 状态改变:{GetStringFromEnum(value)}")
|
||||
'实现 ILoadingTrigger 接口与 OnStateChanged 回调
|
||||
RunInUi(
|
||||
Sub()
|
||||
@@ -170,6 +171,7 @@
|
||||
'状态变化
|
||||
Public MustOverride Sub Start(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = False)
|
||||
Public MustOverride Sub Abort()
|
||||
Public MustOverride Sub Failed(Ex As Exception)
|
||||
|
||||
'等待结束
|
||||
Public Const WaitForExitTimeoutMessage As String = "等待加载器执行超时。"
|
||||
@@ -290,6 +292,7 @@
|
||||
'检验输入以确定情况
|
||||
If IsForceRestart Then Return True '强制要求重启
|
||||
If ((Input Is Nothing) <> (Me.Input Is Nothing)) OrElse (Input IsNot Nothing AndAlso Not Input.Equals(Me.Input)) Then Return True '输入不同
|
||||
|
||||
If (State = LoadState.Loading OrElse State = LoadState.Finished) AndAlso '正在加载或已结束…
|
||||
(IgnoreReloadTimeout OrElse ReloadTimeout = -1 OrElse
|
||||
LastFinishedTime = 0 OrElse GetTimeTick() - LastFinishedTime < ReloadTimeout) Then '…且没有超时…
|
||||
@@ -316,13 +319,13 @@
|
||||
Sub()
|
||||
Try
|
||||
IsForceRestarting = IsForceRestart
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已{If(IsForceRestarting, "强制", "")}启动")
|
||||
If ModeDebug AndAlso Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已{If(IsForceRestarting, "强制", "")}启动")
|
||||
LoadDelegate(Me)
|
||||
If IsAborted Then
|
||||
Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已中断但线程正常运行至结束,输出被弃用(最新线程:{If(LastRunningThread Is Nothing, -1, LastRunningThread.ManagedThreadId)})", LogLevel.Developer)
|
||||
Return
|
||||
End If
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已完成")
|
||||
If ModeDebug AndAlso Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已完成")
|
||||
RaisePreviewFinish()
|
||||
State = LoadState.Finished
|
||||
LastFinishedTime = GetTimeTick() '未中断,本次输出有效
|
||||
@@ -335,25 +338,33 @@
|
||||
'如果线程是在没有 IsAborted 时手动引发了 ThreadInterruptedException,则代表没有重启线程,这通常代表用户手动取消,应当改为 Aborted
|
||||
If Not IsAborted Then State = LoadState.Aborted
|
||||
Catch ex As Exception
|
||||
If IsAborted Then Return
|
||||
Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 出错,已完成 {Math.Round(Progress * 100)}%", LogLevel.Developer)
|
||||
[Error] = ex
|
||||
State = LoadState.Failed
|
||||
Failed(ex)
|
||||
End Try
|
||||
End Sub) With {.Name = "L/" & Name, .Priority = ThreadPriority}
|
||||
LastRunningThread.Start() '不能使用 RunInNewThread,否则在函数返回前线程就会运行完,导致误判 IsAborted
|
||||
End Sub
|
||||
Public Overrides Sub Abort()
|
||||
If State <> LoadState.Loading Then Return
|
||||
Public Overrides Sub Failed(ex As Exception)
|
||||
SyncLock LockState
|
||||
If IsAborted OrElse State >= LoadState.Finished Then Return
|
||||
State = LoadState.Failed
|
||||
End SyncLock
|
||||
Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 出错,已完成 {Math.Round(Progress * 100)}%", LogLevel.Developer)
|
||||
[Error] = ex
|
||||
TriggerThreadAbort()
|
||||
End Sub
|
||||
Public Overrides Sub Abort()
|
||||
SyncLock LockState
|
||||
If State <> LoadState.Loading Then Return
|
||||
State = LoadState.Aborted
|
||||
End SyncLock
|
||||
TriggerThreadAbort()
|
||||
End Sub
|
||||
Private Sub TriggerThreadAbort()
|
||||
If LastRunningThread Is Nothing Then Return
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({LastRunningThread.ManagedThreadId}) 已中断")
|
||||
If LastRunningThread.IsAlive Then LastRunningThread.Interrupt()
|
||||
If LastRunningThread.IsAlive Then
|
||||
LastRunningThread.Interrupt()
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({LastRunningThread.ManagedThreadId}) 已中断")
|
||||
End If
|
||||
LastRunningThread = Nothing
|
||||
End Sub
|
||||
|
||||
@@ -452,6 +463,17 @@
|
||||
Next
|
||||
End Sub)
|
||||
End Sub
|
||||
Public Overrides Sub Failed(Ex As Exception)
|
||||
SyncLock LockState
|
||||
If State >= LoadState.Finished Then Return
|
||||
State = LoadState.Failed
|
||||
End SyncLock
|
||||
[Error] = Ex
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 子任务状态变更。
|
||||
@@ -469,18 +491,9 @@
|
||||
Case LoadState.Aborted
|
||||
'被中断,这个任务也中断
|
||||
Abort()
|
||||
Case Else
|
||||
Case LoadState.Failed
|
||||
'完蛋,出错了
|
||||
SyncLock LockState
|
||||
If State >= LoadState.Finished Then Return
|
||||
[Error] = New Exception(Loader.Name & "失败", Loader.Error)
|
||||
State = Loader.State
|
||||
End SyncLock
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
Return
|
||||
Failed(New Exception(Loader.Name & "失败", Loader.Error))
|
||||
End Select
|
||||
End Sub
|
||||
''' <summary>
|
||||
|
||||
@@ -1063,7 +1063,7 @@ NotSupportRange:
|
||||
ResponseStream = Response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
|
||||
If Setup.Get("SystemDebugDelay") Then Threading.Thread.Sleep(RandomInteger(50, 3000))
|
||||
Dim ResponseBytes As Byte() = New Byte(16384) {}
|
||||
HttpDataCount = ResponseStream.ReadAsync(ResponseBytes, 0, 16384, CancelToken.Token).GetAwaiter().GetResult()
|
||||
HttpDataCount = ReadWithTimeout(ResponseStream, ResponseBytes, 0, 16384, Timeout)
|
||||
While (IsUnknownSize OrElse Th.DownloadUndone > 0) AndAlso '判断是否下载完成
|
||||
HttpDataCount > 0 AndAlso Not IsProgramEnded AndAlso State < NetState.Merge AndAlso (Not Th.Source.IsFailed OrElse Th.Source.SingleThread = Th)
|
||||
'限速
|
||||
@@ -1113,7 +1113,7 @@ NotSupportRange:
|
||||
'无数据,且已超时
|
||||
Throw New TimeoutException("操作超时,无数据。")
|
||||
End If
|
||||
HttpDataCount = ResponseStream.Read(ResponseBytes, 0, 16384)
|
||||
HttpDataCount = ReadWithTimeout(ResponseStream, ResponseBytes, 0, 16384, Timeout)
|
||||
End While
|
||||
SourceBreak:
|
||||
If State = NetState.Error OrElse (Th.Source.IsFailed AndAlso Th.Source.SingleThread <> Th) OrElse (Th.DownloadUndone > 0 AndAlso Not IsUnknownSize) Then
|
||||
@@ -1190,7 +1190,7 @@ SourceBreak:
|
||||
ElseIf Not Retried Then
|
||||
'合并失败或首次下载失败,未重试:将所有下载源重新标记为不允许断点续传的下载源,逐个重新尝试下载
|
||||
'若所有源均不支持 Range,也会走到这里重试
|
||||
If Not IsRangeNotSupported Then Hint($"{LocalName}:文件下载失败,正在自动重试……", If(ModeDebug, HintType.Red, HintType.Blue)) '在调试模式下给出红色提示
|
||||
If Not IsRangeNotSupported Then Log($"[Download] {LocalName}:文件下载失败,正在自动重试……", LogLevel.Debug)
|
||||
Retried = True
|
||||
SyncLock LockSource
|
||||
SourcesOnce.Clear()
|
||||
@@ -1642,9 +1642,12 @@ Retry:
|
||||
Next
|
||||
OnFail(File.Ex)
|
||||
End Sub
|
||||
Public Overrides Sub Failed(Ex As Exception)
|
||||
OnFail(New List(Of Exception) From {Ex})
|
||||
End Sub
|
||||
Public Sub OnFail(ExList As List(Of Exception))
|
||||
SyncLock LockState
|
||||
If State > LoadState.Loading Then Return
|
||||
If State >= LoadState.Finished Then Return
|
||||
If ExList Is Nothing OrElse Not ExList.Any() Then ExList = New List(Of Exception) From {New Exception("未知错误!")}
|
||||
'寻找有效的错误信息
|
||||
Dim UsefulExs = ExList.Where(Function(e) TypeOf e IsNot OperationCanceledException AndAlso TypeOf e IsNot ThreadInterruptedException).ToList
|
||||
@@ -1982,4 +1985,27 @@ Retry:
|
||||
}.Any(Function(k) Detail.ContainsF(k, True))
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 随机获取一个可用的端口。
|
||||
''' </summary>
|
||||
Public Function GetAvailablePort() As Integer
|
||||
Dim Listener As New TcpListener(IPAddress.Loopback, 0)
|
||||
Listener.Start()
|
||||
Dim port As Integer = CType(Listener.LocalEndpoint, IPEndPoint).Port
|
||||
Listener.Stop()
|
||||
Return port
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 带有 Timeout 的读取 Stream 方法。
|
||||
''' </summary>
|
||||
Private Function ReadWithTimeout(TargetStream As Stream, Buffer() As Byte, Offset As Integer, Count As Integer, ReadTimeoutMs As Integer) As Integer
|
||||
Dim ReadTask = TargetStream.ReadAsync(Buffer, Offset, Count)
|
||||
Dim DelayTask = Task.Delay(ReadTimeoutMs)
|
||||
If Task.WhenAny(ReadTask, DelayTask).GetAwaiter().GetResult() Is DelayTask Then
|
||||
Throw New TimeoutException($"读取数据超时({ReadTimeoutMs} ms)")
|
||||
End If
|
||||
Return ReadTask.GetAwaiter().GetResult()
|
||||
End Function
|
||||
|
||||
End Module
|
||||
|
||||
Reference in New Issue
Block a user