Imports System.Drawing.Imaging Imports System.Reflection Imports System.Security.Cryptography Imports System.Windows.Threading Imports Ionic.Zip Public Module Modules #Region "Review完成 | 声明" ''' ''' 程序的启动路径,以“\”结尾。 ''' ''' Public PATH As String = GetPathFromFullPath(System.Windows.Forms.Application.ExecutablePath) ''' ''' 随机数。 ''' ''' Public Rnd As New Random ''' ''' 正在拖动的滚动条。 ''' ''' Public DragingScroll As VScroll = Nothing ''' ''' 位图缓存。 ''' ''' Public BitmapCache As New Dictionary(Of String, MyBitmap) #End Region #Region "Review完成 | API" ''' ''' 获取指定窗口的句柄。返回查找到的第一个窗口的句柄。 ''' ''' 窗口的类名,使用Spy++可以查看。 ''' 窗口的标题。 ''' 查找到的第一个窗口的句柄。 ''' Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer ''' ''' 彻底释放某个句柄对应项目的资源。 ''' ''' 需要释放资源的句柄。 ''' ''' Public Declare Function DeleteObject Lib "gdi32" Alias "DeleteObject" (ByVal hObject As IntPtr) As Boolean ''' ''' 根据句柄设置对应窗口的标题。 ''' ''' 窗口句柄。 ''' 要设置的标题。 ''' ''' Public Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Integer, ByVal lpString As String) As Integer ''' ''' 获取某个句柄对应的窗口的所属进程ID。 ''' ''' 窗口句柄。 ''' 获取到的ID。需要变量承载。 ''' ''' Public Declare Function GetWindowThreadProcessId Lib "user32" Alias "GetWindowThreadProcessId" (ByVal hwnd As Integer, ByVal lpdwProcessId As Integer) As Integer ''' ''' 设置某个句柄对应的窗口的属性。 ''' ''' 窗口句柄。 ''' 置顶标记。-1 为置顶,-2 为不置顶。 ''' 窗口的 X 坐标。 ''' 窗口的 Y 坐标。 ''' 窗口的宽度。 ''' 窗口的高度。 ''' 利用 Or 连接的参数。 ''' SWP_HIDEWINDOW:隐藏窗口。 ''' SWP_SHOWWINDOW:显示窗口。 ''' SWP_NOMOVE:维持当前位置(忽略 X 和 Y 参数)。 ''' SWP_NOSIZE:维持当前尺寸(忽略 cx 和 cy 参数)。 ''' ''' Public Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" (ByVal hwnd As Integer, ByVal hWndInsertAfter As Integer, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) As Integer ''' ''' 播放 BGM。 ''' ''' 音乐文件的完整路径。 ''' 填写“0”。 ''' 填写“131072 Or 1 Or 8”。 ''' ''' Public Declare Function PlaySound Lib "winmm.dll" (ByVal lpszSoundName As String, ByVal hModule As Integer, ByVal dwFlags As Integer) As Integer ''' ''' 播放 MP3 相关项。 ''' ''' ''' ''' ''' ''' ''' Public Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrRetumString As String, ByVal uReturnLength As Integer, ByVal hwndCallback As Integer) As Integer Public Const SWP_HIDEWINDOW As Integer = 8 * 16 '0x0080 Public Const SWP_SHOWWINDOW As Integer = 4 * 16 '0x0040 Public Const SWP_NOMOVE As Integer = 2 '0x0002 Public Const SWP_NOSIZE As Integer = 1 '0x0001 #End Region #Region "类" ''' ''' 支持浮点数与常见类型隐式转换的颜色。 ''' ''' Public Class MyColor '属性 Public A As Double = 0 Public R As Double = 0 Public G As Double = 0 Public B As Double = 0 '类型转换 Public Shared Widening Operator CType(ByVal col As Color) As MyColor Return New MyColor(col) End Operator Public Shared Widening Operator CType(ByVal conv As MyColor) As Color Return Color.FromArgb(MathByte(conv.A), MathByte(conv.R), MathByte(conv.G), MathByte(conv.B)) End Operator Public Shared Widening Operator CType(ByVal conv As MyColor) As System.Drawing.Color Return System.Drawing.Color.FromArgb(MathByte(conv.A), MathByte(conv.R), MathByte(conv.G), MathByte(conv.B)) End Operator Public Shared Widening Operator CType(ByVal bru As SolidColorBrush) As MyColor Return New MyColor(bru.Color) End Operator Public Shared Widening Operator CType(ByVal conv As MyColor) As SolidColorBrush Return New SolidColorBrush(Color.FromArgb(MathByte(conv.A), MathByte(conv.R), MathByte(conv.G), MathByte(conv.B))) End Operator Public Shared Widening Operator CType(ByVal bru As Brush) As MyColor Return New MyColor(ColorConverter.ConvertFromString(bru.ToString)) End Operator Public Shared Widening Operator CType(ByVal conv As MyColor) As Brush Return New SolidColorBrush(Color.FromArgb(MathByte(conv.A), MathByte(conv.R), MathByte(conv.G), MathByte(conv.B))) End Operator '颜色运算 Shared Operator +(ByVal a As MyColor, ByVal b As MyColor) As MyColor Return New MyColor With {.A = a.A + b.A, .B = a.B + b.B, .G = a.G + b.G, .R = a.R + b.R} End Operator Shared Operator -(ByVal a As MyColor, ByVal b As MyColor) As MyColor Return New MyColor With {.A = a.A - b.A, .B = a.B - b.B, .G = a.G - b.G, .R = a.R - b.R} End Operator Shared Operator *(ByVal a As MyColor, ByVal b As Double) As MyColor Return New MyColor With {.A = a.A * b, .B = a.B * b, .G = a.G * b, .R = a.R * b} End Operator Shared Operator /(ByVal a As MyColor, ByVal b As Double) As MyColor Return New MyColor With {.A = a.A / b, .B = a.B / b, .G = a.G / b, .R = a.R / b} End Operator '构造函数 Public Sub New() End Sub Public Sub New(ByVal col As Color) Me.A = col.A Me.R = col.R Me.G = col.G Me.B = col.B End Sub Public Sub New(ByVal newA As Double, ByVal col As Color) Me.A = newA Me.R = col.R Me.G = col.G Me.B = col.B End Sub Public Sub New(ByVal newR As Double, ByVal newG As Double, ByVal newB As Double) Me.A = 255 Me.R = newR Me.G = newG Me.B = newB End Sub Public Sub New(ByVal newA As Double, ByVal newR As Double, ByVal newG As Double, ByVal newB As Double) Me.A = newA Me.R = newR Me.G = newG Me.B = newB End Sub '复写 Public Shadows Function toString() As String Return "(" & A & "," & R & "," & G & "," & B & ")" End Function End Class Public Class AdvancedRect '属性 Public Property Width As Double = 0 Public Property Height As Double = 0 Public Property Left As Double = 0 Public Property Top As Double = 0 '构造函数 Public Sub New() End Sub Public Sub New(ByVal left As Double, ByVal top As Double, ByVal width As Double, ByVal height As Double) Me.Left = left Me.Top = top Me.Width = width Me.Height = height End Sub End Class '浮点矩形 Public Class FileSize Public Value As Integer = 0 '类型转换 Public Shared Widening Operator CType(ByVal value As Long) As FileSize Return New FileSize(value) End Operator Public Shared Widening Operator CType(ByVal value As FileSize) As Long Return value.Value End Operator Public Shared Widening Operator CType(ByVal value As Integer) As FileSize Return New FileSize(value) End Operator Public Shared Widening Operator CType(ByVal value As FileSize) As Integer Return value.Value End Operator '运算 Shared Operator +(ByVal a As FileSize, ByVal b As FileSize) As FileSize Return a.Value + b.Value End Operator Shared Operator -(ByVal a As FileSize, ByVal b As FileSize) As FileSize Return a.Value - b.Value End Operator Shared Operator *(ByVal a As FileSize, ByVal b As FileSize) As Integer Return a.Value * b.Value End Operator Shared Operator /(ByVal a As FileSize, ByVal b As FileSize) As Double Return MathRange(a.Value / b.Value, 0) End Operator Shared Operator =(ByVal a As FileSize, ByVal b As FileSize) As Boolean Return a.Value = b.Value End Operator Shared Operator <>(ByVal a As FileSize, ByVal b As FileSize) As Boolean Return Not a.Value = b.Value End Operator Shared Operator >(ByVal a As FileSize, ByVal b As FileSize) As Boolean Return a.Value > b.Value End Operator Shared Operator <(ByVal a As FileSize, ByVal b As FileSize) As Boolean Return a.Value < b.Value End Operator Shared Operator &(ByVal a As FileSize, ByVal b As String) As String Return a.ToString & b End Operator Shared Operator &(ByVal a As String, ByVal b As FileSize) As String Return a & b.ToString End Operator '构造函数 Public Sub New() End Sub Public Sub New(ByVal value As Integer) Me.Value = value End Sub '复写 Public Shadows Function ToString() As String Select Case Me.Value Case Is < 0 Return "未知" Case Is < 1024 Return Me.Value & "B" Case Is < 1024 ^ 2 Return Math.Round(Me.Value / 1024, 1) & "K" Case Is < 1024 ^ 3 Return Math.Round(Me.Value / 1024 ^ 2, 2) & "M" Case Else Return Math.Round(Me.Value / 1024 ^ 3, 2) & "G" End Select End Function End Class '文件大小 Public Class Time Private Property Value As Integer = 0 '类型转换 Public Shared Widening Operator CType(ByVal value As Integer) As Time Return New Time(value) End Operator Public Shared Widening Operator CType(ByVal value As Time) As Integer Return value.Value End Operator Public Shared Widening Operator CType(ByVal value As Int64) As Time Return New Time(value) End Operator Public Shared Widening Operator CType(ByVal value As Time) As Int64 Return value.Value End Operator '运算 Shared Operator +(ByVal a As Time, ByVal b As Time) As Time Return a.Value + b.Value End Operator Shared Operator -(ByVal a As Time, ByVal b As Time) As Time Return a.Value - b.Value End Operator Shared Operator *(ByVal a As Time, ByVal b As Time) As Integer Return a.Value * b.Value End Operator Shared Operator /(ByVal a As Time, ByVal b As Time) As Double Return MathRange(a.Value / b.Value, 0) End Operator Shared Operator =(ByVal a As Time, ByVal b As Time) As Boolean Return a.Value = b.Value End Operator Shared Operator <>(ByVal a As Time, ByVal b As Time) As Boolean Return Not a.Value = b.Value End Operator Shared Operator >(ByVal a As Time, ByVal b As Time) As Boolean Return a.Value > b.Value End Operator Shared Operator <(ByVal a As Time, ByVal b As Time) As Boolean Return a.Value < b.Value End Operator Shared Operator &(ByVal a As Time, ByVal b As String) As String Return a.toString & b End Operator Shared Operator &(ByVal a As String, ByVal b As Time) As String Return a & b.toString End Operator '构造函数 Public Sub New() End Sub Public Sub New(ByVal value As Integer) Me.Value = value End Sub '复写 Public Shadows Function toString() As String Dim left As Integer = Me.Value Dim a As Integer a = Math.Floor(left / 3600) toString = FillLength(a.ToString, "0", 2) left = left - a * 3600 a = Math.Floor(left / 60) toString = toString & ":" & FillLength(a.ToString, "0", 2) & ":" & FillLength((left - a * 60), "0", 2) End Function End Class '时间 #End Region #Region "控件" ''' ''' 获取控件或窗体的左边距。无视Alignment。 ''' ''' ''' ''' Public Function GetLeft(ByVal control) As Double GetLeft = 0 If control.GetType.BaseType.Name.Equals("Window") Then Return control.Left Else Select Case control.HorizontalAlignment Case HorizontalAlignment.Left, HorizontalAlignment.Stretch, HorizontalAlignment.Center Return control.Margin.Left Case HorizontalAlignment.Right Return CType(control.Parent, Object).ActualWidth - control.ActualWidth - control.Margin.Right 'Case HorizontalAlignment.Stretch ' Dim MaxWidth As Double = CType(control.Parent, Object).ActualWidth - control.Margin.Left - control.Margin.Right ' Return If(control.ActualWidth >= MaxWidth, control.Margin.Left, control.Margin.Left + (MaxWidth - control.ActualWidth) / 2) 'Case HorizontalAlignment.Center ' Dim CenterPos As Double = control.Margin.Left + (CType(control.Parent, Object).ActualWidth - control.Margin.Left - control.Margin.Right) / 2 ' Return CenterPos - control.ActualWidth / 2 End Select End If End Function ''' ''' 获取控件或窗体的顶边距。无视Alignment。 ''' ''' ''' ''' Public Function GetTop(ByVal control) As Double GetTop = 0 If control.GetType.BaseType.Name.Equals("Window") Then Return control.Top Else Select Case control.VerticalAlignment Case VerticalAlignment.Top, VerticalAlignment.Stretch, VerticalAlignment.Center Return control.Margin.Top Case VerticalAlignment.Bottom Return -control.Margin.Bottom 'Return CType(control.Parent, Object).ActualHeight - control.ActualHeight - control.Margin.Bottom 'Case VerticalAlignment.Stretch ' Dim MaxHeight As Double = CType(control.Parent, Object).ActualHeight - control.Margin.Top - control.Margin.Bottom ' Return If(control.ActualHeight >= MaxHeight, control.Margin.Top, control.Margin.Top + (MaxHeight - control.ActualHeight) / 2) 'Case VerticalAlignment.Center ' Dim CenterPos As Double = control.Margin.Top + (CType(control.Parent, Object).ActualHeight - control.Margin.Top - control.Margin.Bottom) / 2 ' Return CenterPos - control.ActualHeight / 2 End Select End If End Function ''' ''' 将一个IList中的内容添加到另外一个IList。 ''' ''' Public Sub IListCopy(ByVal list As IList, ByVal arr As IList) For Each item In arr list.Add(item) Next End Sub Public Sub SetLeft(ByVal control As Object, ByVal newValue As Double) If Double.IsNaN(newValue) Or Double.IsInfinity(newValue) Then Exit Sub '安全性检查 If control.GetType.BaseType.Name.Equals("Window") Then control.Left = newValue Else Select Case control.HorizontalAlignment Case HorizontalAlignment.Left, HorizontalAlignment.Stretch, HorizontalAlignment.Center control.Margin = New Thickness(newValue, control.Margin.Top, control.Margin.Right, control.Margin.Bottom) Case HorizontalAlignment.Right control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, CType(control.Parent, Object).ActualWidth - control.ActualWidth - newValue, control.Margin.Bottom) End Select End If End Sub '设置Left Public Sub SetTop(ByVal control As Object, ByVal newValue As Double) If Double.IsNaN(newValue) Or Double.IsInfinity(newValue) Then Exit Sub '安全性检查 If control.GetType.BaseType.Name.Equals("Window") Then control.Top = newValue Else Select Case control.VerticalAlignment Case VerticalAlignment.Top, VerticalAlignment.Stretch, VerticalAlignment.Center control.Margin = New Thickness(control.Margin.Left, newValue, control.Margin.Right, control.Margin.Bottom) Case VerticalAlignment.Bottom control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, control.Margin.Right, -newValue) 'control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, control.Margin.Right, CType(control.Parent, Object).ActualHeight - control.ActualHeight - newValue) End Select End If End Sub '设置Top ''' ''' 拖动滚动条。 ''' ''' Public Sub ControlStartRun() Dim timer As New DispatcherTimer() timer.Interval = TimeSpan.FromMilliseconds(20) AddHandler timer.Tick, Sub() If IsNothing(DragingScroll) Then Exit Sub If Mouse.LeftButton = MouseButtonState.Pressed Then DragingScroll.Draging(Mouse.GetPosition(DragingScroll.btnDrag).Y) Else '鼠标放下 If Not DragingScroll.IsMouseOver Then DragingScroll.DragMouseLeave() DragingScroll = Nothing End If End Sub timer.Start() End Sub #End Region #Region "数学" ''' ''' 获取两数间的百分比。小数点精确到6位。 ''' ''' ''' Public Function MathPercent(ByVal ValueA As Double, ByVal ValueB As Double, ByVal Percent As Object) As Double Return Math.Round(ValueA * (1 - Percent) + ValueB * Percent, 6) '解决Double计算错误 End Function ''' ''' 获取两颜色间的百分比,根据RGB计算。小数点精确到6位。 ''' ''' ''' Public Function MathPercent(ByVal ValueA As MyColor, ByVal ValueB As MyColor, ByVal Percent As Object) As MyColor Return MathRound(ValueA * (1 - Percent) + ValueB * Percent, 6) '解决Double计算错误 End Function ''' ''' 检查一个数字是否正确(如检查无限、NaN等)。 ''' ''' 需要检查的数字。 ''' 是否正确。 ''' Public Function MathCheck(ByVal Num As Double) As Boolean Return Not (Double.IsInfinity(Num) Or Double.IsNaN(Num)) End Function '贝塞尔曲线计算 Public Function MathBezier(ByVal t As Double, ByVal x1 As Double, ByVal y1 As Double, ByVal x2 As Double, ByVal y2 As Double) As Double Dim a, b a = t Do b = 3 * a * ((1 / 3 + x1 - x2) * a * a + (x2 - 2 * x1) * a + x1) a = a + (t - b) * 0.5 Loop Until Math.Abs(b - t) < 0.01 '精度1% Return 3 * a * ((1 / 3 + y1 - y2) * a * a + (y2 - 2 * y1) * a + y1) End Function 'Byte合理化 Public Function MathByte(ByVal d As Double) As Byte If d < 0 Then d = 0 If d > 255 Then d = 255 Return Math.Round(d) End Function '提供扩展类支持的Round Public Function MathRound(ByVal col As MyColor, Optional ByVal w As Integer = 0) As MyColor Return New MyColor With {.A = Math.Round(col.A, w), .R = Math.Round(col.R, w), .G = Math.Round(col.G, w), .B = Math.Round(col.B, w)} End Function '把数值限定范围 Public Function MathRange(ByVal value As Double, ByVal min As Double, Optional ByVal max As Double = Double.MaxValue) As Double Return Math.Max(min, Math.Min(max, value)) End Function 'Byte类溢出处理 Public Function ByteOverflow(ByVal int As Integer) As Byte Dim Original = int Dim ReturnValue As Byte Try If int < 0 Then Do Until int >= 0 int = int + 256 Loop ReturnValue = int Else ReturnValue = int Mod 256 End If Return ReturnValue Catch ex As Exception ExShow(ex, "Byte 溢出处理出错:" & int) Return 0 End Try End Function #End Region #Region "文本" '填充长度 Public Function FillLength(ByVal str As String, ByVal code As String, ByVal length As Byte) If (Len(str) > length) Then Return Mid(str, 1, length) Return Mid(str.Replace(" ", "-").PadRight(length), str.Length + 1).Replace(" ", code) & str End Function '选择其一 Public Function OneOf(ByVal objects As Array) Return objects.GetValue(RandomInteger(0, objects.Length - 1)) End Function Public Function OneOf(ByVal objects As ArrayList) Return objects(RandomInteger(0, objects.Count - 1)) End Function '正则搜索 Public Function RegexSearch(ByVal str As String, ByVal regex As String) As ArrayList Try RegexSearch = New ArrayList Dim RegexSearchRes = (New RegularExpressions.Regex(regex)).Matches(str) If IsNothing(RegexSearchRes) Then Return RegexSearch For Each item As RegularExpressions.Match In RegexSearchRes RegexSearch.Add(item.Value) Next Catch Return New ArrayList End Try End Function '加密解密 Public Function SerAdd(ByVal str As String) As String Dim text As Byte() = Encoding.Unicode.GetBytes(str) '-----------------------------------------------1 Array.Reverse(text) '-----------------------------------------------2 Dim out(text.Length - 1) As Byte For i As Integer = 0 To text.Length - 4 Step 4 out(i) = text(i + 2) out(i + 1) = text(i + 1) out(i + 2) = text(i + 3) out(i + 3) = text(i) Next i If text.Length Mod 4 = 2 Then out(text.Length - 2) = text(text.Length - 1) out(text.Length - 1) = text(text.Length - 2) End If text = out '-----------------------------------------------3 ReDim out(text.Length - 1) For i As Integer = 0 To text.Length - 3 Step 3 out(i) = ByteOverflow(text(i + 2) + 12) out(i + 1) = ByteOverflow(text(i) - 85) out(i + 2) = ByteOverflow(text(i + 1) + 36) Next i Select Case text.Length Mod 3 Case 1 out(text.Length - 1) = ByteOverflow(text(text.Length - 1) - 52) Case 2 out(text.Length - 1) = ByteOverflow(text(text.Length - 1) + 16) out(text.Length - 2) = ByteOverflow(text(text.Length - 2) + 48) End Select text = out '-----------------------------------------------4 For i As Integer = 0 To text.Length - 1 text(i) = ByteOverflow(text(i) + i) Next i '-----------------------------------------------E SerAdd = "" For Each bt As Byte In text SerAdd = SerAdd & BitConverter.ToString({bt}) Next End Function Public Function SerRemove(ByVal str As String) As String Dim line1 As String, line2 As String line1 = "这就是解密函数……自己写的……很口胡很辣鸡……做一个加密只是为了防那些脑残熊孩子,既然都能搞到这函数了,那加密也没用,所以就别吐槽这辣鸡加密了吧233" line2 = "by 龙腾猫跃" Dim text(Len(str) / 2 - 1) As Byte For i As Integer = 0 To Len(str) - 2 Step 2 text(i / 2) = Byte.Parse(Mid(str, i + 1, 2), Globalization.NumberStyles.AllowHexSpecifier) Next '-----------------------------------------------4 For i As Integer = 0 To text.Length - 1 text(i) = ByteOverflow(text(i) - i) Next i '-----------------------------------------------3 Dim out(text.Length - 1) As Byte For i As Integer = 0 To text.Length - 3 Step 3 out(i) = ByteOverflow(text(i + 1) + 85) out(i + 1) = ByteOverflow(text(i + 2) - 36) out(i + 2) = ByteOverflow(text(i) - 12) Next i Select Case text.Length Mod 3 Case 1 out(text.Length - 1) = ByteOverflow(text(text.Length - 1) + 52) Case 2 out(text.Length - 1) = ByteOverflow(text(text.Length - 1) - 16) out(text.Length - 2) = ByteOverflow(text(text.Length - 2) - 48) End Select text = out '-----------------------------------------------2 ReDim out(text.Length - 1) For i As Integer = 0 To text.Length - 4 Step 4 out(i) = text(i + 3) out(i + 1) = text(i + 1) out(i + 2) = text(i) out(i + 3) = text(i + 2) Next i If text.Length Mod 4 = 2 Then out(text.Length - 2) = text(text.Length - 1) out(text.Length - 1) = text(text.Length - 2) End If text = out '-----------------------------------------------1 Array.Reverse(text) '-----------------------------------------------E SerRemove = Encoding.Unicode.GetString(text) End Function ''' ''' 从用户输入的字符串中提取数字 ''' ''' 用户输入的字符串 ''' 字符串中的数字,没有任何数字返回0 ''' Public Function GetDoubleFromString(ByVal str As String) As Double Dim regResult As ArrayList = RegexSearch(str, "(-?[0-9]\d*)?\.?[0-9]\d*") If regResult.Count = 0 Then Return 0 Else Return Val(regResult(0)) End If End Function '把Exception转为字符串 Public Function GetStringFromException(ByVal ex As Exception, Optional ByVal isLong As Boolean = False) As String '构造基本信息与InnerException信息 GetStringFromException = ex.Message.Replace(vbCrLf, "") If Not IsNothing(ex.InnerException) Then GetStringFromException = GetStringFromException & vbCrLf & "InnerException: " & ex.InnerException.Message.Replace(vbCrLf, "") If Not IsNothing(ex.InnerException.InnerException) Then GetStringFromException = GetStringFromException & vbCrLf & "InnerException: " & ex.InnerException.InnerException.Message.Replace(vbCrLf, "") End If End If '添加堆栈信息 If isLong Then GetStringFromException = GetStringFromException & vbCrLf For Each Stack As String In ex.StackTrace.Split(vbCrLf) If Stack.Contains("pcl.") Then GetStringFromException = GetStringFromException & vbCrLf & Stack Next End If '清除双回车 GetStringFromException = GetStringFromException.Replace(vbCrLf & vbCrLf, vbCrLf).Replace(vbCrLf & vbCrLf, vbCrLf) End Function #End Region #Region "Review完成 | 图片" ''' ''' 裁剪一个 BitmapSource。 ''' ''' 源图片。 Public Function CutBitmap(ByVal img As BitmapSource, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) As BitmapSource Try Return New CroppedBitmap(img, New Int32Rect(x, y, width, height)) Catch ex As Exception ExShow(ex, "裁剪图片失败", ErrorLevel.AllUsers) Return Nothing End Try End Function #End Region #Region "文件" ''' ''' 从应用资源释放文件。 ''' ''' 需要释放的文件在应用资源中的名称。 ''' 释放的路径,包含文件名。 ''' Public Sub OutputFileInResource(ByVal FileName As String, ByVal OutputPath As String, ByVal PassOnExists As Boolean) Try Dim Res = My.Resources.ResourceManager.GetObject(FileName) If IsNothing(Res) Then Throw New FileNotFoundException("没有找到名为" & FileName & "的资源") If PassOnExists And File.Exists(OutputPath) Then If GetFileSize(OutputPath) = Res.Length Then Exit Sub End If Directory.CreateDirectory(Mid(OutputPath, 1, OutputPath.LastIndexOf("\"))) Using OutputStream As New FileStream(OutputPath, FileMode.OpenOrCreate, FileAccess.Write) OutputStream.Write(Res, 0, Res.Length) OutputStream.Flush() End Using log("[System] 释放文件:" & FileName) Catch ex As Exception ExShow(ex, "释放文件失败", ErrorLevel.DebugOnly) Throw ex End Try End Sub ''' ''' 解压文件。 ''' ''' 需要解压的文件的完整路径。 ''' 解压的目标文件夹。 ''' 解压结束后(无论成功还是失败)执行的方法。 ''' Public Sub UnzipFile(ByVal SourceFile As String, ByVal UnzipDir As String, Optional ByVal RunThread As ParameterizedThreadStart = Nothing) Dim th As New Thread(Sub() Try Directory.CreateDirectory(UnzipDir) Using zip As New ZipFile(SourceFile) zip.ExtractAll(UnzipDir) End Using Catch ex As Exception ExShow(ex, "解压文件失败:" & SourceFile) End Try If Not IsNothing(RunThread) Then RunThread.Method.Invoke(Nothing, {Nothing}) End Sub) th.Start() End Sub '解压ZIP文件 ''' ''' 弹出选取文件对话框并且要求选取文件。 ''' ''' 要求的格式。如:“常用图片文件(*.png;*.jpeg)|*.png;*.jpg;*.jpeg”。 ''' 弹窗的标题。 ''' 选择的唯一文件的完整路径,如果没有选择可能为空字符串。 ''' Public Function SelectFile(ByVal FileFilter As String, ByVal Title As String) As String Using fileDialog As New Forms.OpenFileDialog fileDialog.AddExtension = True fileDialog.AutoUpgradeEnabled = True fileDialog.CheckFileExists = True fileDialog.Filter = FileFilter fileDialog.Multiselect = False fileDialog.Title = Title fileDialog.ShowDialog() Return fileDialog.FileName End Using End Function ''' ''' 弹出选取文件夹对话框并且要求选取文件夹。 ''' ''' 弹窗的标题。 ''' 选择的唯一文件夹的完整路径,如果没有选择可能为空字符串。 ''' Public Function SelectFolder(ByVal Title As String) As String Using folderDialog As New Forms.FolderBrowserDialog folderDialog.Description = Title folderDialog.ShowNewFolderButton = True folderDialog.ShowDialog() Return folderDialog.SelectedPath End Using End Function ''' ''' 从文件路径或者URL获取不包含文件名的路径。不包含路径将会抛出异常。 ''' ''' 文件路径,以标识符结尾。如果不包含文件名则必须以标识符结尾。 ''' ''' Public Function GetPathFromFullPath(ByVal FilePath As String) As String If Not (FilePath.Contains("\") Or FilePath.Contains("/")) Then Throw New FileFormatException("不包含路径:" & FilePath) If FilePath.EndsWith("\") Or FilePath.EndsWith("/") Then Return FilePath GetPathFromFullPath = Left(FilePath, FilePath.LastIndexOfAny({"\", "/"}) + 1) If GetPathFromFullPath = "" Then Throw New FileFormatException("不包含路径:" & FilePath) End Function ''' ''' 从文件路径或者URL获取不包含路径的文件名。不包含文件名将会抛出异常。 ''' ''' 文件路径。可以不完整。 ''' ''' Public Function GetFileNameFromPath(ByVal FilePath As String) As String If FilePath.EndsWith("\") Or FilePath.EndsWith("/") Then Throw New FileFormatException("不包含文件名:" & FilePath) If Not (FilePath.Contains("\") Or FilePath.Contains("/")) Then Return FilePath GetFileNameFromPath = Mid(FilePath, FilePath.LastIndexOfAny({"\", "/"}) + 2) If GetFileNameFromPath.Contains("\\") And GetFileNameFromPath.Contains("?") Then '包含网络参数 GetFileNameFromPath = Left(GetFileNameFromPath, FilePath.LastIndexOf("?")) End If If GetFileNameFromPath = "" Then Throw New FileFormatException("不包含文件名:" & FilePath) End Function ''' ''' 获取文件的版本信息。 ''' ''' 文件目录。 ''' ''' Public Function GetFileVersion(ByVal Path As String) As String Try Return Diagnostics.FileVersionInfo.GetVersionInfo(Path).FileVersion Catch ex As Exception ExShow(ex, "获取文件版本失败:" & Path, ErrorLevel.Slient) Return "" End Try End Function ''' ''' 获取一个文件夹下的所有文件的完整路径。 ''' ''' 源文件夹,不以“\”结尾。 ''' ''' Public Function GetFilesFromPath(ByVal SourcePath As String) As ArrayList GetFilesFromPath = New ArrayList GetFilesFromPath(SourcePath, GetFilesFromPath) End Function Private Function GetFilesFromPath(ByVal SourcePath As String, ByRef CurrentList As ArrayList) As ArrayList Try If Not Directory.Exists(SourcePath) Then Return New ArrayList '列举文件 For Each File As FileInfo In (New DirectoryInfo(SourcePath)).GetFiles CurrentList.Add(File.FullName) Next '列举目录 For Each Folder As DirectoryInfo In (New DirectoryInfo(SourcePath)).GetDirectories GetFilesFromPath(Folder.FullName, CurrentList) Next Catch ex As Exception ExShow(ex, "列举文件失败:" & SourcePath, ErrorLevel.Slient) End Try '返回 Return CurrentList End Function '获取文件大小(HTTP URL或文件路径) Public Function GetFileSize(ByVal Path As String, Optional ByRef FileMission As WebFile = Nothing) As Integer Try If Path.StartsWith("http") Then If MODE_OFFLINE Then Return 0 '获取网络文件大小 Dim Res As Net.WebRequest = Net.WebRequest.Create(Path) Dim Response As WebResponse = Res.GetResponse Dim Size As Integer = Response.ContentLength() If Not IsNothing(FileMission) Then '用Ref返回实际地址 If Not FileMission.AllWebAddress(0) = Response.ResponseUri.ToString Then If FileMission.RefreshName Then FileMission.LocalName = GetFileNameFromPath(Response.ResponseUri.ToString) FileMission.AllWebAddress(0) = Response.ResponseUri.ToString End If End If '释放资源 Response.Close() Res.Abort() Return Size Else If File.Exists(Path) Then Return New FileInfo(Path).Length Else Return 0 End If End If Catch ex As Exception ExShow(ex, "获取文件大小失败:" & Path, ErrorLevel.Slient) Return 0 End Try End Function '获取MD5 Public Function GetFileMD5(ByVal filepath As String) As String Try '网上抄的啊,自己改了改而已XD Dim result As String = "" Using fstream As New FileStream(filepath, FileMode.Open, FileAccess.Read) Dim dataToHash(fstream.Length - 1) As Byte fstream.Read(dataToHash, 0, fstream.Length) Dim hashvalue As Byte() = CType(CryptoConfig.CreateFromName("MD5"), HashAlgorithm).ComputeHash(dataToHash) Dim i As Double For i = 0 To hashvalue.Length - 1 result += FillLength(Hex(hashvalue(i)).ToLower, "0", 2) Next End Using Return result Catch ex As Exception ExShow(ex, "获取文件MD5失败:" & filepath, ErrorLevel.Slient) Return "" End Try End Function '读取文件 Public Function ReadFileToEnd(ByVal filePath As String, Optional ByVal encode As Encoding = Nothing, Optional ByVal isFullPath As Boolean = True) As String Try Dim actualPath As String = If(isFullPath, "", PATH) & filePath If File.Exists(actualPath) Then encode = If(IsNothing(encode), New UTF8Encoding(False), encode) Using reader As New StreamReader(actualPath, encode) ReadFileToEnd = reader.ReadToEnd End Using Else Return "" End If Catch ex As Exception ExShow(ex, "读取文件出错:" & filePath) Return "" End Try End Function '写入文件 Public Sub WriteFile(ByVal filePath As String, ByVal text As String, Optional ByVal add As Boolean = False, Optional ByVal encode As Encoding = Nothing, Optional ByVal isFullPath As Boolean = True) Try Dim actualpath As String = If(isFullPath, "", PATH) & filePath If Not File.Exists(actualpath) Then Directory.CreateDirectory(GetPathFromFullPath(actualpath)) File.Create(actualpath).Dispose() End If encode = If(IsNothing(encode), New UTF8Encoding(False), encode) Using writer As New StreamWriter(actualpath, add, encode) writer.Write(text) writer.Flush() End Using Catch ex As Exception ExShow(ex, "写入文件时出错:" & filePath) End Try End Sub Private Class IniCache Public content As String = "" Public cache As New Dictionary(Of String, String) End Class Private CacheIni As New Dictionary(Of String, IniCache) ''' ''' 获取Ini文件内容,这可能会使用到缓存。 ''' ''' 文件路径或简写。简写将会使用“PCL\文件名.ini”作为路径。 ''' ''' Private Function GetIni(ByVal FileName As String) As IniCache If Not FileName.Contains(":\") Then FileName = PATH & "PCL\" & FileName & ".ini" If CacheIni.ContainsKey(FileName) Then '返回缓存中的信息 Dim Cache As New IniCache CacheIni.TryGetValue(FileName, Cache) Return Cache '防止ByRef导致缓存变更 Else If File.Exists(FileName) Then '返回文件信息并且记入缓存 Dim Cache As String = (vbCrLf & ReadFileToEnd(FileName) & vbCrLf).Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf).Replace(vbCrLf & vbCrLf, vbCrLf) Dim Ini As New IniCache With {.content = Cache} CacheIni.Add(FileName, Ini) Return Ini Else '返回空信息 Dim Ini As New IniCache With {.content = ""} CacheIni.Add(FileName, Ini) Return Ini End If End If End Function ''' ''' 读取Ini文件,这可能会使用到缓存。 ''' ''' 文件路径或简写。简写将会使用“PCL\文件名.ini”作为路径。 ''' 键。 ''' 没有找到键时返回的默认值。 Public Function ReadIni(ByVal FileName As String, ByVal Key As String, Optional ByVal DefaultValue As String = "") As String Try '获取目前文件 Dim NowIni As IniCache = GetIni(FileName) If IsNothing(NowIni) Then Return DefaultValue '使用缓存 If NowIni.cache.ContainsKey(Key) Then Return If(NowIni.cache(Key), DefaultValue) '新读取文件 If NowIni.content.Contains(vbCrLf & Key & ":") Then Dim Ret As String = Mid(NowIni.content, NowIni.content.IndexOf(vbCrLf & Key & ":") + 3) Ret = If(Ret.Contains(vbCrLf), Mid(Ret, 1, Ret.IndexOf(vbCrLf)), Ret).Replace(Key & ":", "") NowIni.cache.Add(Key, If(Ret = vbLf, "", Ret)) Return If(Ret = vbLf, "", Ret) Else Return DefaultValue End If Catch ex As Exception '读取失败 Return DefaultValue End Try End Function ''' ''' 写入ini文件,这会更新缓存。 ''' ''' 文件路径或简写。简写将会使用“PCL\文件名.ini”作为路径。 ''' 键。 ''' 值。 ''' Public Sub WriteIni(ByVal FileName As String, ByVal Key As String, ByVal Value As String) On Error Resume Next FileName = If(FileName.Contains(":\"), FileName, PATH & "PCL\" & FileName & ".ini") If IsNothing(Value) Then Value = "" Value = Value.Replace(vbCrLf, "") '创建文件夹 If Not Directory.Exists(GetPathFromFullPath(FileName)) Then Directory.CreateDirectory(GetPathFromFullPath(FileName)) '获取目前文件 Dim NowFile As String = GetIni(FileName).content '如果值一样就不处理 If NowFile.Contains(vbCrLf & Key & ":" & Value & vbCrLf) Then Exit Sub '处理文件 Dim FindResult As String = ReadIni(FileName, Key) If FindResult = "" And Not NowFile.Contains(vbCrLf & Key & ":") Then '不存在这个键 NowFile = NowFile & vbCrLf & Key & ":" & Value Else '存在这个键 NowFile = NowFile.Replace(vbCrLf & Key & ":" & FindResult & vbCrLf, vbCrLf & Key & ":" & Value & vbCrLf) End If WriteFile(FileName, NowFile) '刷新目前缓存 CacheIni(FileName).content = NowFile CacheIni(FileName).cache.Remove(Key) CacheIni(FileName).cache.Add(Key, Value) End Sub #End Region #Region "Review完成 | 网络" ''' ''' 获取网页源代码。 ''' ''' 网页的URL。 ''' 网页的编码,通常为UTF-8。 ''' ''' Public Function GetWebsiteCode(ByVal URL As String, ByVal Encode As Encoding) As String If MODE_OFFLINE Then Return "" '初始化 Dim req As HttpWebRequest = Nothing Dim res As HttpWebResponse = Nothing Dim strm As StreamReader = Nothing GetWebsiteCode = "" Try req = WebRequest.Create(URL) req.Timeout = 6000 res = req.GetResponse() strm = New StreamReader(res.GetResponseStream(), Encode) GetWebsiteCode = strm.ReadToEnd Catch ex As Exception If Not URL.Contains("https://sessionserver.mojang.com/session/minecraft/profile/") Then ExShow(ex, "获取网页源代码失败:" & URL) Finally '释放资源 If Not IsNothing(strm) Then strm.Dispose() If Not IsNothing(res) Then res.Close() If Not IsNothing(req) Then req.Abort() End Try End Function ''' ''' 从网络中直接下载文件。 ''' ''' 网络URL。 ''' 下载的本地地址。 ''' 是否下载成功。 ''' Public Function DownloadFile(ByVal URL As String, ByVal LocalFile As String) As Boolean If MODE_OFFLINE Then Return False '初始化 Try '建立目录 Directory.CreateDirectory(GetPathFromFullPath(LocalFile)) '尝试删除原文件 File.Delete(LocalFile) Catch ex As Exception ExShow(ex, "预处理文件失败:" & LocalFile) Return False End Try '下载 Using Client As New WebClient Try Client.DownloadFile(URL, LocalFile) log("[System] 直接下载文件成功:" & URL) Return True Catch ex As Exception ExShow(ex, "直接下载文件失败:" & URL) Return False End Try End Using End Function ''' ''' 进行Ping测试。这不会异步进行,失败将直接抛出异常。 ''' ''' 要进行Ping的IP。 ''' 最大响应时间,单位为毫秒。 ''' Ping用时,单位为毫秒。 ''' Public Function Ping(ByVal IP As String, ByVal MaxPingTime As Integer) As Integer If MODE_OFFLINE Then Throw New WebException("没有网络连接。") Using pin = New System.Net.NetworkInformation.Ping Return pin.Send(IP, MaxPingTime).RoundtripTime End Using End Function #End Region #Region "系统" ''' ''' 测试某段代码用时,单位为毫秒/次。 ''' ''' 需要测试的代码。 ''' ''' Public Function TimeTest(ByVal run As ThreadStart) As Double Dim StartTime As Long = My.Computer.Clock.TickCount Dim RunCount As Integer = 0 Do While True run.Invoke() RunCount = RunCount + 1 If My.Computer.Clock.TickCount - StartTime > 1000 Or RunCount > 100000 Then Exit Do Loop Return (My.Computer.Clock.TickCount - StartTime) / RunCount End Function ''' ''' 打开文件(等同于使用“运行”窗口)。 ''' ''' 文件名。可以为“notepad”等缩写。 ''' 运行参数。 ''' 是否等待该程序结束。默认为False。 ''' Public Sub Shell(ByVal FileName As String, Optional ByVal Arguments As String = "", Optional ByVal WaitForExit As Boolean = False) Try Dim Program As New Process Program.StartInfo.Arguments = Arguments Program.StartInfo.FileName = FileName Program.Start() If WaitForExit Then Program.WaitForExit() Catch ex As Exception ExShow(ex, "执行命令失败:" & FileName, ErrorLevel.AllUsers) End Try End Sub ''' ''' 返回一个枚举对应的字符串。 ''' ''' 一个已经实例化的枚举类型。 ''' ''' Public Function GetStringFromEnum(ByVal EnumData As Object) As String Return [Enum].GetName(EnumData.GetType, EnumData) End Function ''' ''' 从指定的枚举中查找某字符串对应的枚举项。 ''' ''' 源枚举。 ''' 对应枚举一项的字符串。 ''' ''' Public Function GetEnumFromString(ByVal EnumData As Object, ByVal Value As String) Return [Enum].Parse(EnumData.GetType, Value) End Function ''' ''' 获取格式类似于“11:08:52.037”的当前时间的字符串。 ''' ''' ''' Public Function GetTime() As String Return Date.Now.ToLongTimeString & "." & FillLength(Date.Now.Millisecond, "0", 3) End Function ''' ''' 数组去重。 ''' ''' ''' ''' Public Function ArrayNoDouble(ByVal array As Array) As ArrayList Dim ResultArray As New ArrayList For i = 0 To UBound(array) For ii = i + 1 To UBound(array) If array(i).Equals(array(ii)) Then GoTo NextElement Next ResultArray.Add(array(i)) NextElement: Next i Return ResultArray End Function ''' ''' 将数组从大到小排序。 ''' ''' 纯数字的ArrayList。 ''' Public Sub ArrayBigToSmall(ByRef Array As ArrayList) If Array.Count < 2 Then Exit Sub For i = 1 To Array.Count - 1 If Array(i) > Array(i - 1) Then Dim c = Array(i - 1) Array(i - 1) = Array(i) Array(i) = c If i >= 2 Then i = i - 2 End If Next i End Sub ''' ''' 强制将一个支持IList接口的对象转换为任意格式的一维数组。 ''' ''' 转换后的数组类型。 ''' ''' ''' Public Function ArrayConventer(Of T, B As IList)(ByVal Source As B) As T() Dim Re(Source.Count - 1) As T For i = 0 To Source.Count - 1 Re(i) = Source(i) Next Return Re End Function ''' ''' 检查是否拥有某一文件夹的读取权限。如果文件夹不存在,会返回 False。 ''' ''' 检查目录。 Public Function CheckDirectoryPermission(ByVal Path As String) As Boolean Try If Path = "" Then Return False If Not Directory.Exists(Path) Then Return False Dim folderCheck As New DirectoryInfo(Path) folderCheck.EnumerateFiles() Return True Catch Return False End Try End Function ''' ''' 获取程序启动参数。 ''' ''' 参数名。 ''' 默认值。 ''' ''' Public Function GetProgramArgument(ByVal Name As String, Optional ByVal DefaultValue As Object = "") Dim AllArguments() As String = Microsoft.VisualBasic.Command.Split(" ") For i = 0 To AllArguments.Length - 1 If AllArguments(i) = "-" & Name Then If AllArguments.Length = i + 1 Then Return True If AllArguments(i + 1).StartsWith("-") Then Return True Return AllArguments(i + 1) End If Next Return DefaultValue End Function ''' ''' 获取Json对象。 ''' ''' ''' ''' Public Function ReadJson(ByVal Text As String) As JObject Try Return CType(Newtonsoft.Json.JsonConvert.DeserializeObject(Text), JObject) Catch ex As Exception ExShow(ex, "Json读取失败:" & Left(Text, 30)) Throw New FileFormatException("错误的 Json:" & GetStringFromException(ex, True)) End Try End Function '执行命令行函数 'Public Function RunCMD(ByVal Commands As String, ByVal Wait As Boolean) As String ' Dim myProcess As New Process() ' Dim myProcessStartInfo As New ProcessStartInfo("cmd.exe") ' myProcessStartInfo.UseShellExecute = False ' myProcessStartInfo.RedirectStandardOutput = True ' myProcessStartInfo.CreateNoWindow = True ' myProcessStartInfo.Arguments = "/c """ & Commands & """" ' myProcess.StartInfo = myProcessStartInfo ' myProcess.Start() ' If Wait Then ' myProcess.WaitForExit() ' Dim myStreamReader As IO.StreamReader = myProcess.StandardOutput ' Dim myString As String = myStreamReader.ReadToEnd() ' myProcess.Close() ' myProcess.Dispose() ' Return myString ' Else ' Return "" ' End If 'End Function '注册表IO Public Function ReadReg(ByVal Key As String, Optional ByVal DefaultValue As String = "") As String Try Dim parentKey As Microsoft.Win32.RegistryKey, softKey As Microsoft.Win32.RegistryKey parentKey = My.Computer.Registry.CurrentUser softKey = parentKey.OpenSubKey("Software\" & APPLICATION_SHORT_NAME, True) If softKey Is Nothing Then ReadReg = DefaultValue '不存在则返回默认值 Else Dim readValue As New System.Text.StringBuilder readValue.AppendLine(softKey.GetValue(Key)) Dim value = readValue.ToString.Replace(vbCrLf, "") '去除莫名的回车 Return If(value = "", DefaultValue, value) '错误则返回默认值 End If Catch ex As Exception ExShow(ex, "读取注册表出错:" & Key, ErrorLevel.Slient) Return DefaultValue End Try End Function Public Sub WriteReg(ByVal Key As String, ByVal Value As String, Optional ByVal ShowException As Boolean = False) Try Dim parentKey As Microsoft.Win32.RegistryKey, softKey As Microsoft.Win32.RegistryKey parentKey = My.Computer.Registry.CurrentUser softKey = parentKey.OpenSubKey("Software\" & APPLICATION_SHORT_NAME, True) If softKey Is Nothing Then softKey = parentKey.CreateSubKey("Software\" & APPLICATION_SHORT_NAME) '如果不存在就创建 softKey.SetValue(Key, Value) Catch ex As Exception ExShow(ex, "写入注册表出错:" & Key, If(ShowException, modMain.ErrorLevel.DebugOnly, modMain.ErrorLevel.Slient)) If ShowException Then Throw '如果显示错误就丢一个 End Try End Sub Private UUID As Integer = 0 ''' ''' 获取一个全程序内不会重复的数字(伪UUID)。 ''' ''' UUID,一个大于0的有序整数。 ''' Public Function GetUUID() As Integer UUID = UUID + 1 Return UUID End Function #End Region #Region "Review完成 | 窗体" ''' ''' 等待显示的弹窗。 ''' ''' Public WaitingMyMsgbox As ArrayList = If(IsNothing(WaitingMyMsgbox), New ArrayList, WaitingMyMsgbox) ''' ''' 显示弹窗。 ''' Public Function MyMsgbox(ByVal Converter As MyMsgboxConverter) As Integer Try '存入等待列表 If Thread.CurrentThread.IsBackground Or Not Thread.CurrentThread.GetApartmentState = ApartmentState.STA Or Not AniRunning Then If IsNothing(WaitingMyMsgbox) Then WaitingMyMsgbox = New ArrayList '初始化 WaitingMyMsgbox.Add(Converter) '返回值 If Converter.IsWaitExit Then Do While Converter.ReturnCode = 0 Thread.Sleep(50) Loop Return Converter.ReturnCode Else Return 0 End If End If '在主线程,显示窗体 Dim MsgBox As New MetroMsgbox(Converter) With {.Owner = frmMain, .Width = MAINFORM_WIDTH - 20, .Height = MAINFORM_HEIGHT - 20, .Left = (My.Computer.Screen.WorkingArea.Width - MAINFORM_WIDTH) / 2 + 10, .Top = (My.Computer.Screen.WorkingArea.Height - MAINFORM_HEIGHT) / 2 + 10} If Converter.IsWaitExit Then MsgBox.ShowDialog() Return Converter.ReturnCode Else MsgBox.Show() Return 0 End If Catch ex As Exception ExShow(ex, "显示弹窗失败:" & Converter.Title, ErrorLevel.MsgboxAndFeedback) Return 0 End Try End Function ''' ''' 显示弹窗。 ''' ''' 弹窗的标题。 ''' 弹窗的内容。 ''' 显示的第一个按钮,默认为“确定”。 ''' 显示的第二个按钮,默认为空。 ''' 显示的第三个按钮,默认为空。 ''' 是否为警告弹窗,若为 True,第一个按钮会显示为红色。 ''' 是否等待弹窗关闭。若为 True,将会返回从 1 开始的点击的按钮编号,否则返回 0。 ''' ''' Public Function MyMsgbox(ByVal Caption As String, Optional ByVal Title As String = APPLICATION_FULL_NAME, Optional ByVal Button1 As String = "确定", Optional ByVal Button2 As String = "", Optional ByVal Button3 As String = "", Optional ByVal IsWarn As Boolean = False, Optional ByVal IsWaitExit As Boolean = True) As Integer Dim NewMsgBoxConverter As New MyMsgboxConverter With {.Button1 = Button1, .Button2 = Button2, .Button3 = Button3, .Caption = Caption, .IsWarn = IsWarn, .Title = Title, .IsWaitExit = IsWaitExit} Return MyMsgbox(NewMsgBoxConverter) End Function ''' ''' 存储弹窗信息的转换器。 ''' ''' Public Class MyMsgboxConverter Public Title As String Public Caption As String = "" Public Button1 As String = "确定" Public Button2 As String = "" Public Button3 As String = "" Public IsWarn As Boolean = False Public IsWaitExit As Boolean = True ''' ''' 弹窗是否已经关闭。 ''' ''' Public IsExited As Boolean = False ''' ''' 点击的按钮编号,从 1 开始。 ''' ''' Public ReturnCode As Integer = 0 End Class 'Public Function MyMsgbox(ByVal title As String, ByVal page As Grid, Optional ByVal caption As String = "", Optional ByVal btn1 As String = "确定", Optional ByVal btn2 As String = "", Optional ByVal btn3 As String = "", Optional ByVal wait As Boolean = False) As Object ' Dim wind As New MetroMsgbox With {.Owner = frmMain, .Width = MAINFORM_WIDTH - 20, .Height = MAINFORM_HEIGHT - 20, .Left = (My.Computer.Screen.WorkingArea.Width - MAINFORM_WIDTH) / 2 + 10, .Top = (My.Computer.Screen.WorkingArea.Height - MAINFORM_HEIGHT) / 2 + 10} ' wind.labTitle.Text = title ' wind.panCaption.Child = page ' wind.btn1.Text = btn1 ' wind.btn2.Text = btn2 ' wind.btn3.Text = btn3 ' wind.btn2.Visibility = If(btn2 = "", Visibility.Collapsed, Visibility.Visible) ' wind.btn3.Visibility = If(btn3 = "", Visibility.Collapsed, Visibility.Visible) ' msgboxCallback = 0 ' If wait Then ' wind.Show() ' Return wind ' Else ' wind.ShowDialog() ' Return msgboxCallback ' End If 'End Function ''' 设置 WebBrowser 静默,即是否不产生弹窗。 ''' 需要改变的 WebBrowser。 ''' 是否开启静默模式。 Public Sub SetWebBrowserSilent(ByVal WebBrowser As WebBrowser, ByVal IsSilent As Boolean) Try Dim fi As FieldInfo = WebBrowser.GetType.GetField("_axIWebBrowser2", BindingFlags.Instance Or BindingFlags.NonPublic) If Not IsNothing(fi) Then Dim browser = fi.GetValue(WebBrowser) If Not IsNothing(browser) Then browser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, Nothing, browser, {IsSilent}) End If End If Catch ex As Exception ExShow(ex, "设置浏览器失败", ErrorLevel.AllUsers) End Try End Sub #End Region #Region "随机" '打乱数组 Public Function RandomArray(ByVal array As ArrayList) As ArrayList RandomArray = New ArrayList Do While array.Count > 0 Dim i As Integer = RandomInteger(0, array.Count - 1) RandomArray.Add(array(i)) array.RemoveAt(i) Loop End Function '获取随机整数 Public Function RandomInteger(ByVal min As Integer, ByVal max As Integer) As Integer Return Math.Round((max - min) * Rnd.NextDouble) + min End Function #End Region #Region "Review 完成 | 音乐" ''' ''' 播放音乐。 ''' ''' 音乐文件完整路径。 ''' Public Sub PlayBgm(ByVal FilePath As String, ByVal IsStop As Boolean, Optional ByVal IsShowHint As Boolean = False) Try If IsStop Then '停止 log("[System] 停止播放音乐:" & FilePath) mciSendString("close " & FilePath, 0, 0, 0) PlaySound(Nothing, Nothing, Nothing) Else '播放 log("[System] 播放音乐:" & FilePath) mciSendString("open " & FilePath, "", 0, 0) Dim code = mciSendString("play " & FilePath & " repeat", "", 0, 0) If code = 259 Then PlaySound(FilePath, 0, 131072 Or 1 Or 8) '换用 PlaySound 播放不支持循环的 .wav 'If IsShowHint Then ShowHint("该音乐不支持循环播放,音乐将在播放完一次后停止。") 'code = mciSendString("play " & FilePath, "", 0, 0) ElseIf code = 277 Then If IsShowHint Then MyMsgbox("无效的音乐文件。" & vbCrLf & "PCL 不支持部分 MP3 的播放,请尝试更换文件。目前已知网易云下载的 MP3 会出现此问题,原因不明。", "播放音乐失败") ElseIf Not code = 0 Then Throw New Exception(code) End If End If 'PlaySound(FilePath, 0, 131072 Or 1 Or 8) Catch ex As Exception ExShow(ex, "播放音乐失败", ErrorLevel.AllUsers) End Try End Sub #End Region End Module '基础函数 'Module ComputerInfo ' '声明 ' 'Private cpuCounter As New PerformanceCounter("Processor", "% Processor Time", "_Total") ' 'Private downloadCounter As New PerformanceCounter("Network Interface", "Bytes Received/sec", New PerformanceCounterCategory("Network Interface").GetInstanceNames()(0)) ' 'Private uploadCounter As New PerformanceCounter("Network Interface", "Bytes Sent/sec", New PerformanceCounterCategory("Network Interface").GetInstanceNames()(0)) ' Private pcInfo As New Microsoft.VisualBasic.Devices.ComputerInfo ' '启动代码 ' Public Sub InfoStartRun(ByVal time As Integer) ' Dim timer As New DispatcherTimer() ' timer.Interval = TimeSpan.FromMilliseconds(time) ' AddHandler timer.Tick, AddressOf InfoTimer ' timer.Start() ' End Sub ' '每次执行的代码 ' Private Sub InfoTimer() ' '防止断网时获取错误 ' On Error Resume Next ' infoDownloadSpeed = 0 ' infoUploadSpeed = 0 ' '获取值 ' 'infoCpuPercent = cpuCounter.NextValue ' 'infoDownloadSpeed = downloadCounter.NextValue / 1024 ' 'infoUploadSpeed = uploadCounter.NextValue / 1024 ' End Sub ' '获取信息 ' Public infoCpuPercent As Integer = 0 'CPU使用率 ' Public infoDownloadSpeed As Integer = 0 '下载速度(KB) ' Public infoUploadSpeed As Integer = 0 '上传速度(KB) ' Public Function infoNetworkSpeed() As Integer ' Return infoDownloadSpeed + infoUploadSpeed ' End Function '网络速度(KB) ' Public Function infoRamPercent() As Integer ' Return (pcInfo.TotalPhysicalMemory - pcInfo.AvailablePhysicalMemory) / pcInfo.TotalPhysicalMemory * 100 ' End Function '内存使用率 ' Public Function infoRamUsed() As Integer ' Return (pcInfo.TotalPhysicalMemory - pcInfo.AvailablePhysicalMemory) / 1024 / 1024 ' End Function '内存使用量(MB) ' Public Function infoRamTotal() As Integer ' Return pcInfo.TotalPhysicalMemory / 1024 / 1024 ' End Function '内存总量(MB) ' Public Function infoRamAvailable() As Integer ' Return pcInfo.AvailablePhysicalMemory / 1024 / 1024 ' End Function '内存可用量(MB) 'End Module '电脑信息获取 Public Module MyAnimationer #Region "声明" ''' ''' 动画组列表。(组名→ArrayList) ''' ''' Private AniGroups As New Dictionary(Of String, ArrayList) ''' ''' 上一次记刻的时间。 ''' ''' Public AniLastTick As Integer ''' ''' 动画执行是否开启。 ''' ''' Public AniRunning As Boolean = False #End Region #Region "类与枚举" ''' ''' 单个动画对象。 ''' ''' Public Structure Animation ''' ''' 动画种类。 ''' ''' Public AniType As AniType ''' ''' 动画副种类。 ''' ''' Public AniSubType As AniSubType ''' ''' 动画总长度。 ''' ''' Public TotalLength As Integer ''' ''' 已经执行的动画长度。如果为负数则为延迟。 ''' ''' Public FinishLength As Integer ''' ''' 已经完成的百分比。 ''' ''' Public Percent As Double ''' ''' 是否为“以后”。 ''' ''' Public After As Boolean ''' ''' 插值器类型。 ''' ''' Public Ease As AniEase ''' ''' 动画对象。 ''' ''' Public Obj As Object ''' ''' 动画值。 ''' ''' Public Value As Object ''' ''' 上次执行时的动画值。 ''' ''' Public Last As Object Public Overrides Function ToString() As String Return GetStringFromEnum(AniType) & " | " & FinishLength & "/" & TotalLength & "(" & Math.Round(Percent * 100) & "%) | " & Obj.name & "(" & Obj.GetType.Name & ")" End Function End Structure ''' ''' 动画基础种类。 ''' ''' Public Enum AniType As Byte ''' ''' 单个Double的动画,包括位置、长宽、透明度等。这需要附属类型。 ''' ''' DoubleAnimation = 0 ''' ''' 颜色属性的动画。这需要附属类型。 ''' ''' ColorAnimation = 1 ''' ''' 缩放控件大小。比起4个DoubleAnimation来说效率更高。 ''' ''' Scale = 2 ''' ''' 文字一个个出现。 ''' ''' TextAppear = 3 ''' ''' 执行代码。 ''' ''' Code = 4 ''' ''' 执行按照规定格式书写的伪代码。比起Code来说这有很大的局限性,但效率更高。 ''' ''' CodeEvent = 5 ''' ''' 以 WPF 方式缩放控件。 ''' ScaleTransform = 6 ''' ''' 以 WPF 方式旋转控件。 ''' RotateTransform = 7 End Enum ''' ''' 动画扩展种类。 ''' ''' Public Enum AniSubType As Byte X = 0 Y = 1 Width = 2 Height = 3 Opacity = 4 Value = 5 Radius = 10 BorderThickness = 11 Background = 6 BorderBrush = 7 Foreground = 8 Stroke = 9 End Enum #End Region #Region "种类" 'DoubleAnimation ''' ''' 移动X轴的动画。 ''' ''' 动画的对象。 ''' 进行移动的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaX(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.X, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 移动Y轴的动画。 ''' ''' 动画的对象。 ''' 进行移动的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaY(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Y, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变宽度的动画。 ''' ''' 动画的对象。 ''' 宽度改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaWidth(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Width, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变高度的动画。 ''' ''' 动画的对象。 ''' 高度改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaHeight(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Height, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变透明度的动画。 ''' ''' 动画的对象。 ''' 透明度改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaOpacity(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Opacity, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变对象的Value属性的动画。 ''' ''' 动画的对象。 ''' Value属性改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaValue(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Value, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变对象的Radius属性的动画。 ''' ''' 动画的对象。 ''' Radius属性改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaRadius(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.Radius, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function ''' ''' 改变对象的BorderThickness属性的动画。 ''' ''' 动画的对象。 ''' BorderThickness属性改变的值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaBorderThickness(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.DoubleAnimation, .AniSubType = AniSubType.BorderThickness, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function 'ColorAnimation ''' ''' 改变Background颜色属性的动画。 ''' ''' 动画的对象。 ''' 颜色改变的值。以RGB加减法进行计算。不用担心超额。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaBackGround(ByVal Obj As Object, ByVal Value As MyColor, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.ColorAnimation, .AniSubType = AniSubType.Background, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay, .Last = New MyColor} End Function ''' ''' 改变BorderBrush颜色属性的动画。 ''' ''' 动画的对象。 ''' 颜色改变的值。以RGB加减法进行计算。不用担心超额。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaBorderBrush(ByVal Obj As Object, ByVal Value As MyColor, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.ColorAnimation, .AniSubType = AniSubType.BorderBrush, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay, .Last = New MyColor} End Function ''' ''' 改变Foreground颜色属性的动画。 ''' ''' 动画的对象。 ''' 颜色改变的值。以RGB加减法进行计算。不用担心超额。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaForeGround(ByVal Obj As Object, ByVal Value As MyColor, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.ColorAnimation, .AniSubType = AniSubType.Foreground, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay, .Last = New MyColor} End Function ''' ''' 改变Stroke颜色属性的动画。 ''' ''' 动画的对象。 ''' 颜色改变的值。以RGB加减法进行计算。不用担心超额。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaStroke(ByVal Obj As Object, ByVal Value As MyColor, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.ColorAnimation, .AniSubType = AniSubType.Stroke, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay, .Last = New MyColor} End Function 'Scale ''' ''' 缩放控件的动画。 ''' ''' 动画的对象。 ''' 大小改变的百分比(如-0.6)或值。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' 大小改变是否为绝对值。若为False则会按照控件当前大小计算相对缩放值。 ''' ''' Public Function AaScale(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False, Optional ByVal Absolute As Boolean = False) As Animation Dim ChangeRect As New AdvancedRect If Absolute Then ChangeRect = New AdvancedRect(-0.5 * Value, -0.5 * Value, Value, Value) Else ChangeRect = New AdvancedRect(-0.5 * Obj.ActualWidth * Value, -0.5 * Obj.ActualHeight * Value, Obj.ActualWidth * Value, Obj.ActualHeight * Value) End If Return New Animation With {.AniType = AniType.Scale, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = ChangeRect, .After = After, .FinishLength = -Delay} End Function 'TextAppear ''' ''' 让一段文字一个个字出现或消失的动画。 ''' ''' 动画的对象。必须是Label或TextBlock。 ''' 是否为一个个字隐藏。默认为False(一个个字出现)。这些字必须已经存在了。 ''' 是否采用根据文本长度决定时间的方式。 ''' 动画长度(毫秒)。若TimePerText为True,这代表每个字所占据的时间。 ''' 动画延迟执行的时间(毫秒)。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaTextAppear(ByVal Obj As Object, Optional ByVal Hide As Boolean = False, Optional ByVal TimePerText As Boolean = True, Optional ByVal Time As Integer = 70, Optional ByVal Delay As Integer = 0, Optional ByVal After As Boolean = False) As Animation 'Are we cool yet? Return New Animation With {.AniType = AniType.TextAppear, .TotalLength = If(TimePerText, Time * Len(If(Obj.GetType.Name = "TextBlock", Obj.Text, Obj.Context.ToString)), Time), .Obj = Obj, .Value = {If(Obj.GetType.Name = "TextBlock", Obj.Text, Obj.Context.ToString), Hide}, .After = After, .FinishLength = -Delay} End Function 'Code ''' ''' 执行代码。 ''' ''' 一个ThreadStart。这将会在执行时在主线程调用。 ''' 代码延迟执行的时间(毫秒)。 ''' 是否等到以前的动画完成后才执行。 ''' ''' Public Function AaCode(ByVal Code As ThreadStart, Optional ByVal Delay As Integer = 0, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.Code, .TotalLength = 1, .Value = Code, .After = After, .FinishLength = -Delay} End Function 'CodeEvent ''' ''' 执行按照规定格式书写的伪代码。这有很大的局限性,但不会建立线程,性能也更好。 ''' ''' 伪代码元素数组。 ''' 代码延迟执行的时间(毫秒)。 ''' 是否等到以前的动画完成后才执行。 ''' ''' Public Function AaCode(ByVal Code As Array, Optional ByVal Delay As Integer = 0, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.CodeEvent, .TotalLength = 1, .Value = Code, .After = After, .FinishLength = -Delay} End Function 'ScaleTransform ''' ''' 按照 WPF 方式缩放控件的动画。 ''' ''' 动画的对象。它必须已经拥有了单一的 ScaleTransform 值。 ''' 大小改变的百分比(如-0.6)。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaScaleTransform(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.ScaleTransform, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function 'RotateTransform ''' ''' 按照 WPF 方式旋转控件的动画。 ''' ''' 动画的对象。它必须已经拥有了单一的 ScaleTransform 值。 ''' 大小改变的百分比(如-0.6)。 ''' 动画长度(毫秒)。 ''' 动画延迟执行的时间(毫秒)。 ''' 插值器类型。 ''' 是否等到以前的动画完成后才继续本动画。 ''' ''' Public Function AaRotateTransform(ByVal Obj As Object, ByVal Value As Double, Optional ByVal Time As Integer = 400, Optional ByVal Delay As Integer = 0, Optional ByVal Ease As AniEase = Nothing, Optional ByVal After As Boolean = False) As Animation Return New Animation With {.AniType = AniType.RotateTransform, .TotalLength = Time, .Ease = If(Ease, New AniEaseNone), .Obj = Obj, .Value = Value, .After = After, .FinishLength = -Delay} End Function '特殊 ''' ''' 将一个StackPanel中的各个项目依次显示。 ''' ''' Public Function AaStack(ByVal Stack As StackPanel) As ArrayList AaStack = New ArrayList Dim AniDelay As Integer = 0 For Each Item In Stack.Children Item.Opacity = 0 AaStack.Add(AaOpacity(Item, 1, 100, AniDelay)) AniDelay = AniDelay + 25 Next End Function #End Region #Region "插值器" '基类 Public MustInherit Class AniEase ''' ''' 获取增加值。 ''' ''' 较大的时间百分比。 ''' 较小的时间百分比。 ''' Public MustOverride Function GetDelta(t1 As Double, t0 As Double) As Double End Class ''' ''' 线性,无缓动。 ''' Public Class AniEaseNone Inherits AniEase Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double t1 = MathRange(t1, 0, 1) t0 = MathRange(t0, 0, 1) Return t1 - t0 End Function End Class ''' ''' 平滑开始。 ''' Public Class AniEaseStart Inherits AniEase Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double t1 = MathRange(t1, 0, 1) t0 = MathRange(t0, 0, 1) Return (t1 - t0) * (t1 + t0) End Function End Class ''' ''' 平滑结束。 ''' Public Class AniEaseEnd Inherits AniEase Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double t1 = MathRange(t1, 0, 1) t0 = MathRange(t0, 0, 1) Return (t1 - t0) * (2 - t1 - t0) End Function End Class ''' ''' 跳跃开始。 ''' Public Class AniEaseJumpStart Inherits AniEase ''' ''' ''' ''' 跳跃结束时占整个动画的百分比。 Public Sub New(JumpEndPercent As Double) o = JumpEndPercent End Sub Private o As Double Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double t1 = MathRange(t1, 0, 1) t0 = MathRange(t0, 0, 1) Return (t1 - t0) * (t1 + t0 - o) / (1 - o) End Function End Class ''' ''' 跳跃结束。 ''' Public Class AniEaseJumpEnd Inherits AniEase ''' ''' ''' ''' 跳跃开始时占整个动画的百分比。 Public Sub New(JumpStartPercent As Double) o = JumpStartPercent End Sub Private o As Double Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double t1 = MathRange(t1, 0, 1) t0 = MathRange(t0, 0, 1) Return (t1 - t0) * (o + 1 - t1 - t0) / o End Function End Class #End Region '等待处理的动画组列表 Private AniWaitingList As New ArrayList Private AniWaitingListName As New ArrayList ''' ''' 开始一个动画组。 ''' ''' 由Aa开头的函数初始化的Animation对象集合。 ''' 动画组的名称。如果重复会直接停止同名动画组。 ''' 是否重新开始这一帧的计时。如果该动画组连续执行请设置为False。 ''' Public Sub AniStart(ByVal AniGroup As ArrayList, ByVal Name As String, Optional ByVal refreshTime As Boolean = True) '把组动画(如 AniStack)分解 Dim AniGroupSet As New ArrayList For i = 0 To AniGroup.Count - 1 If AniGroup(i).GetType.ToString.Contains("ArrayList") Then AniGroupSet.AddRange(AniGroup(i)) Else AniGroupSet.Add(AniGroup(i)) End If Next i '添加到正在执行的动画组 If refreshTime Then AniLastTick = Computer.Clock.TickCount '避免处理动画时已经造成了极大的延迟,导致动画突然结束 AniStop(Name) AniGroups.Add(Name, AniGroupSet) End Sub ''' ''' 开始一个动画组。 ''' ''' 由Aa开头的函数初始化的Animation对象集合。 ''' 动画组的名称。如果重复会直接停止同名动画组。 ''' 是否重新开始这一帧的计时。如果该动画组连续执行请设置为False。 ''' Public Sub AniStart(ByVal aniGroup As Array, ByVal name As String, Optional ByVal refreshTime As Boolean = True) '让Array和ArrayList都可以添加 AniStart(New ArrayList(aniGroup), name, refreshTime) End Sub ''' ''' 停止一个动画组。 ''' ''' 需要停止的动画组的名称。 ''' Public Sub AniStop(ByVal Name As String) If AniGroups.ContainsKey(Name) Then AniGroups.Remove(Name) End Sub Private AniMissions As New ArrayList Private AniFPSCounter As Integer = 0 Private AniFPSTimer As Long = 0 ''' ''' 当前的动画 FPS。 ''' Public AniFPS As Integer = 0 ''' ''' 开始动画执行。 ''' ''' Public Sub AniStartRun() '初始化计时器 AniLastTick = My.Computer.Clock.TickCount AniFPSTimer = My.Computer.Clock.TickCount AniRunning = True '标记动画执行开始 Dim th = New Thread(Sub() Do While True '两帧之间的间隔时间 Dim DeltaTime As Integer = My.Computer.Clock.TickCount - AniLastTick If DeltaTime < 2 Then GoTo Sleeper AniLastTick = My.Computer.Clock.TickCount '执行动画 frmMain.Dispatcher.Invoke(Sub() AniTimer(DeltaTime)) '记录 FPS If AniLastTick - AniFPSTimer >= 1000 Then AniFPS = AniFPSCounter AniFPSCounter = 0 AniFPSTimer = AniLastTick If MODE_DEVELOPER Then frmMain.Dispatcher.Invoke(Sub() frmMain.Title = "PCL / FPS : " & AniFPS) End If AniFPSCounter = AniFPSCounter + 1 Sleeper: '控制 FPS Thread.Sleep(1) Loop End Sub) th.Start() End Sub Private AniCodeList As New ArrayList ''' ''' 动画定时器事件。 ''' ''' Public Sub AniTimer(DeltaTime As Integer) Rnd.NextDouble() '重置随机数发生器 For Each Code In AniCodeList frmMain.Dispatcher.Invoke(Code) Next AniCodeList.Clear() Try Dim i As Integer = 0 '循环每个动画组 Do While i < AniGroups.Count '初始化 Dim aniGroup = AniGroups.Values(i) Dim CanRemoveAfter = True '是否应该去除“之后”标记 Dim ii = 0 '循环每个动画 Do While ii < aniGroup.Count Dim Anim As Animation = aniGroup(ii) '执行种类 If Anim.After = False Then '之前 CanRemoveAfter = False '取消“之后”标记 '增加执行时间 Anim.FinishLength = Anim.FinishLength + DeltaTime '执行动画 If Anim.FinishLength > 0 Then Anim = AniRun(Anim) '执行动画 '如果当前动画已执行完毕 If Anim.FinishLength >= Anim.TotalLength Then '删除 aniGroup.RemoveAt(ii) GoTo NextAni End If aniGroup(ii) = Anim Else '之后 If CanRemoveAfter Then '之后改为之前 CanRemoveAfter = False Anim.After = False aniGroup(ii) = Anim '重新循环该动画 GoTo NextAni Else '不能去除该“之后”标记,结束该动画组 Exit Do End If End If ii = ii + 1 NextAni: Loop '如果当前动画组都执行完毕则删除 If aniGroup.Count = 0 Then AniGroups.Remove(AniGroups.Keys(i)) i = i - 1 End If '继续循环 i = i + 1 Loop Catch ex As Exception ExShow(ex, "动画刻执行失败", ErrorLevel.Slient) End Try End Sub ''' ''' 执行一个动画。 ''' ''' 执行的动画对象。 ''' Private Function AniRun(ByVal Ani As Animation) As Animation Try Select Case Ani.AniType Case AniType.DoubleAnimation Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, Ani.Percent)) Select Case Ani.AniSubType Case AniSubType.X SetLeft(Ani.Obj, GetLeft(Ani.Obj) + Delta) Case AniSubType.Y SetTop(Ani.Obj, GetTop(Ani.Obj) + Delta) Case AniSubType.Opacity Ani.Obj.Opacity = MathRange(Ani.Obj.Opacity + Delta, 0, 1) Case AniSubType.Width Ani.Obj.Width = MathRange(Ani.Obj.Width + Delta, 0) Case AniSubType.Height Ani.Obj.Height = MathRange(Ani.Obj.Height + Delta, 0) Case AniSubType.Value Ani.Obj.Value = Ani.Obj.Value + Delta Case AniSubType.Radius Ani.Obj.Radius = Ani.Obj.Radius + Delta Case AniSubType.BorderThickness Ani.Obj.BorderThickness = New Thickness(CType(Ani.Obj.BorderThickness, Thickness).Bottom + Delta) End Select Case AniType.ColorAnimation '利用Last记录了余下的小数值 Dim Delta As MyColor = MathPercent(New MyColor, Ani.Value, Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, Ani.Percent)) + Ani.Last Select Case Ani.AniSubType Case AniSubType.Background Dim NewColor As MyColor = New MyColor(Ani.Obj.Background.Color) + Delta Ani.Obj.Background = NewColor Ani.Last = NewColor - New MyColor(Ani.Obj.Background.Color) Case AniSubType.Foreground Dim NewColor As MyColor = New MyColor(Ani.Obj.Foreground.Color) + Delta Ani.Obj.Foreground = NewColor Ani.Last = NewColor - New MyColor(Ani.Obj.Foreground.Color) Case AniSubType.Stroke Dim NewColor As MyColor = New MyColor(Ani.Obj.Stroke.Color) + Delta Ani.Obj.Stroke = NewColor Ani.Last = NewColor - New MyColor(Ani.Obj.Stroke.Color) Case AniSubType.BorderBrush Dim NewColor As MyColor = New MyColor(Ani.Obj.BorderBrush.Color) + Delta Ani.Obj.BorderBrush = NewColor Ani.Last = NewColor - New MyColor(Ani.Obj.BorderBrush.Color) End Select Case AniType.Scale Dim Delta As Double = Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, Ani.Percent) Ani.Obj.Margin = New Thickness(Ani.Obj.Margin.Left + MathPercent(0, Ani.Value.Left, Delta), Ani.Obj.Margin.Top + MathPercent(0, Ani.Value.Top, Delta), Ani.Obj.Margin.Right, Ani.Obj.Margin.Bottom) Ani.Obj.Width = Math.Max(Ani.Obj.Width + MathPercent(0, Ani.Value.Width, Delta), 0) Ani.Obj.Height = Math.Max(Ani.Obj.Height + MathPercent(0, Ani.Value.Height, Delta), 0) Case AniType.TextAppear Dim NewText As String = Mid(Ani.Value(0), 1, If(Ani.Value(1), Len(Ani.Value(0)), 0) + Math.Round(Len(Ani.Value(0)) * If(Ani.Value(1), -1, 1) * Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, 0))) If Ani.Obj.GetType.Name = "TextBlock" Then Ani.Obj.Text = NewText Else Ani.Obj.Context = NewText End If Case AniType.Code AniCodeList.Add(Ani.Value) Case AniType.CodeEvent Select Case Ani.Value(0) Case "Close" Ani.Value(1).Close() Case "Clear" Ani.Value(1).Clear() Case "Remove" Ani.Value(1).Remove(Ani.Value(2)) Case "Add" Ani.Value(1).Add(Ani.Value(2)) Case "Set" Ani.Value(1) = Ani.Value(2) Case "Tag" Ani.Value(1).Tag = Ani.Value(2) Case "Enabled" Ani.Value(1).IsEnabled = Ani.Value(2) Case "State" Ani.Value(1).State = Ani.Value(2) Case "IsHitTestVisible" Ani.Value(1).IsHitTestVisible = Ani.Value(2) Case "ShowInTaskbar" Ani.Value(1).ShowInTaskbar = Ani.Value(2) Case "Nothing" Ani.Value(1) = Nothing Case "Visible" Ani.Value(1).Visibility = If(Ani.Value(2), Visibility.Visible, Visibility.Collapsed) Case "End" EndForce() Case Else AaCodeCall(Ani.Value(0), Ani.Value) End Select Case AniType.ScaleTransform Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, Ani.Percent)) CType(Ani.Obj.RenderTransform, ScaleTransform).ScaleX = CType(Ani.Obj.RenderTransform, ScaleTransform).ScaleX + Delta CType(Ani.Obj.RenderTransform, ScaleTransform).ScaleY = CType(Ani.Obj.RenderTransform, ScaleTransform).ScaleY + Delta Case AniType.RotateTransform Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.FinishLength / Ani.TotalLength, Ani.Percent)) CType(Ani.Obj.RenderTransform, RotateTransform).Angle = CType(Ani.Obj.RenderTransform, RotateTransform).Angle + Delta End Select Ani.Percent = Ani.FinishLength / Ani.TotalLength '修改执行百分比 Catch ex As Exception ExShow(ex, "执行动画失败:" & GetStringFromEnum(Ani.AniType), ErrorLevel.Slient) End Try Return Ani End Function End Module '动画 Public Module Web ''' ''' 正在下载的列表。 ''' ''' Public WebGroups As New Dictionary(Of String, WebGroup) Private WebDownloadingList As New ArrayList Public WebDownloadCountMax As Integer = ReadIni("setup", "DownMaxinum", "20") #Region "类与枚举" ''' ''' 下载文件的文件大小处理方式。 ''' Public Enum WebRequireSize As Integer ''' ''' 在下载前已经知道了文件大小。 ''' Known = 0 ''' ''' 下载前不知道文件大小,需要从网络中获取。 ''' Require = 1 ''' ''' 下载前不知道文件大小,也不获取大小。 ''' DontNeed = 2 ''' ''' 不需要知道准确大小,只要求文件至少有一定的大小。 ''' AtLeast = 3 End Enum Public Class WebGroup Public Name As String ''' ''' 文件列表(WebFile)。 ''' ''' Public Files As ArrayList ''' ''' 是否尝试从服务器确认文件大小。 ''' ''' Public GetServerFileSize As WebRequireSize ''' ''' 下载成功时执行的线程。 ''' ''' Public OnSuccess As System.Threading.ParameterizedThreadStart ''' ''' 下载失败时执行的线程。 ''' ''' Public OnFail As System.Threading.ParameterizedThreadStart ''' ''' 已下载的文件数目(下载成功或失败的)。(用于文件数较多情况下的快速进度计算) ''' Public FinishedCount As Integer = 0 ''' ''' 下载完成的文件数目的百分比。取值为0到1。 ''' ''' Public ReadOnly Property Percent As Double Get Select Case Files.Count Case 0 Percent = 0 Case 1 Percent = Math.Round(CType(Files(0), WebFile).Percent, 4) Case Is < 60 Dim TotalPercent As Double = 0 For i = 0 To Files.Count - 1 TotalPercent = TotalPercent + CType(Files(i), WebFile).Percent Next Percent = Math.Round(TotalPercent / Files.Count, 4) Case Else Percent = Math.Round(FinishedCount / Files.Count, 4) End Select End Get End Property ''' ''' 该文件组的唯一标识符。 ''' ''' Public UUID As Integer = GetUUID() End Class Public Class WebFile ''' ''' 文件所有可能的网络URL。 ''' ''' Public AllWebAddress As ArrayList ''' ''' 文件当前的网络URL。 ''' ''' Public ReadOnly Property WebAddress As String Get Return If(AllWebAddress(0), "") End Get End Property ''' ''' 文件在本地的完整地址。 ''' ''' Public ReadOnly Property LocalAddress As String Get Return LocalFolder & LocalName End Get End Property ''' ''' 文件在本地的所在的文件夹,以“\”结尾。 ''' ''' Public LocalFolder As String ''' ''' 是否需要刷新文件名。 ''' ''' Public RefreshName As Boolean = False Private _LocalName As String = "" ''' ''' 当前的文件名。如果需要刷新文件名,它有可能改变。 ''' ''' Public Property LocalName As String Get If _LocalName = "" Or IsNothing(_LocalName) Then _LocalName = GetFileNameFromPath(WebAddress) Return _LocalName End Get Set(ByVal value As String) _LocalName = value End Set End Property ''' ''' 使用下一个下载地址。 ''' ''' Public Function NextAddress() As Boolean If AllWebAddress.Count > 1 Then AllWebAddress.RemoveAt(0) If RefreshName Then LocalName = GetFileNameFromPath(WebAddress) Return True Else Return False End If End Function Private _State As WebDownloadStats = WebDownloadStats.FirstLoad ''' ''' 状态标记。 ''' ''' Public Property State As WebDownloadStats Get Return _State End Get Set(ByVal value As WebDownloadStats) If value = _State Then Exit Property '改变状态 _State = value Select Case value Case WebDownloadStats.FirstLoad '等待下载 GetSize = 0 Speed = 0 Percent = 0 SyncLock WebWaitingLockObj WebWaitingList.Add(Me) End SyncLock Case WebDownloadStats.Wait '等待下载 frmDownloadRight.RefreshFile(Me) GetSize = 0 Speed = 0 Percent = 0 SyncLock WebWaitingLockObj WebWaitingList.Insert(0, Me) End SyncLock WebWaitingToRun() Case WebDownloadStats.GetSize '获取大小 frmDownloadRight.RefreshFile(Me) WebStateGetSize(Me) Case WebDownloadStats.Download '下载 frmDownloadRight.RefreshFile(Me) WebStateDownload(Me) Case WebDownloadStats.Success '下载成功 Speed = 0 Percent = 1 WebStateSuccess(Me) WebGroups(GroupName).FinishedCount = WebGroups(GroupName).FinishedCount + 1 frmDownloadRight.RefreshFile(Me) WebFinishCheck(Me.GroupName) Case WebDownloadStats.Fail '下载失败 GetSize = 0 Speed = 0 Percent = 0 If Not WebStateFail(Me) Then WebGroups(GroupName).FinishedCount = WebGroups(GroupName).FinishedCount + 1 frmDownloadRight.RefreshFile(Me) WebFinishCheck(Me.GroupName) End If Case WebDownloadStats.Retry '重试 _State = value frmDownloadRight.RefreshFile(Me) Thread.Sleep(If(WebGroups(GroupName).Files.Count = 1, 200, 1000)) #Disable Warning BC42026 State = WebDownloadStats.Wait End Select End Set End Property ''' ''' 该文件的唯一标识符。 ''' ''' Public UUID As Integer = GetUUID() Public GetServerFileSize As WebRequireSize Public GroupName As String Public RefreshThread As Thread ''' ''' 文件总大小,可能为 0。 ''' ''' Public TotalSize As FileSize = 0 ''' ''' 已经下载了的部分的大小。 ''' ''' Public GetSize As FileSize = 0 ''' ''' 下载速度,单位为秒。 ''' ''' Public Speed As FileSize = 0 ''' ''' 已经下载的百分比。取值为 0 到 1。 ''' ''' Public Percent As Double = 0 ''' ''' 计算下载速度的计刻。 ''' ''' Public SpeedCount As Integer End Class Public Enum WebDownloadStats As Byte ''' ''' 第一次加入列表。等同于 Wait,但是不会刷新队列。 ''' FirstLoad = 6 ''' ''' 等待中。若队列有空闲,需要获取大小的转为 GetSize,不需要获取大小的转为 Download。 ''' ''' Wait = 0 ''' ''' 正在获取大小。获取大小成功的转为 Download,失败的转为 Fail。 ''' ''' GetSize = 1 ''' ''' 正在下载。下载成功的转为 Success,失败的转为 Fail。 ''' ''' Download = 2 ''' ''' 下载成功。 ''' ''' Success = 3 ''' ''' 下载失败。可以重试的转为 Retry。 ''' ''' Fail = 4 ''' ''' 等待重试。半秒后转为 Wait。 ''' Retry = 5 End Enum #End Region '开始下载 ''' ''' 需要下载的文件。 ''' Public Class WebRequireFile ''' ''' 所有可能的下载地址。若前一个地址下载失败会自动尝试下一个地址。 ''' Public WebURLs As ArrayList ''' ''' 本地文件夹地址。不包含文件名。 ''' Public LocalFolder As String = "" ''' ''' 本地文件名,不包含文件夹路径。若为空,则会从下载地址自动生成文件名。 ''' Public LocalName As String = "" ''' ''' 根据文件大小处理方式在这里提交准确大小或至少应有的大小。 ''' Public KnownFileSize As Integer = 0 End Class ''' ''' 开始下载一个列表中的文件。 ''' ''' WebRequireFile 的一维数组。 ''' 下载列表的名称。 ''' 下载成功时执行的代码。 ''' 下载失败时执行的代码。 ''' 文件大小处理方式。 ''' Public Sub WebStart(ByVal SourceFiles() As WebRequireFile, ByVal Name As String, ByVal OnSuccess As ParameterizedThreadStart, ByVal OnFail As ParameterizedThreadStart, ByVal RequireSize As WebRequireSize) Dim th As New Thread(Sub() Try '检查哪些文件需要下载,并将真正需要下载的文件整理为 CheckedFiles 数组 Dim CheckedFiles As New ArrayList(SourceFiles) '检查文件是否存在文件名,如果不存在文件名则跳过过滤 If Not CheckedFiles(0).LocalName = "" Then '过滤已经存在的文件 Select Case RequireSize Case WebRequireSize.Known '已知文件大小,检查文件:如果大小一致则不下载 For i = 0 To CheckedFiles.Count - 1 If CheckedFiles.Count - 1 < i Then Exit For If GetFileSize(CheckedFiles(i).LocalFolder & CheckedFiles(i).LocalName) = CheckedFiles(i).KnownFileSize Then CheckedFiles.RemoveAt(i) i = i - 1 End If Next Case WebRequireSize.Require '不知道文件大小,所以不进行过滤 Case WebRequireSize.DontNeed '不需要知道文件大小,检查文件:如果文件存在则不下载 For i = 0 To CheckedFiles.Count - 1 If CheckedFiles.Count - 1 < i Then Exit For If File.Exists(CheckedFiles(i).LocalFolder & CheckedFiles(i).LocalName) Then CheckedFiles.RemoveAt(i) i = i - 1 End If Next Case WebRequireSize.AtLeast '文件有大小要求,检查文件:如果文件大小大于指定值则不下载 For i = 0 To CheckedFiles.Count - 1 If CheckedFiles.Count - 1 < i Then Exit For If GetFileSize(CheckedFiles(i).LocalFolder & CheckedFiles(i).LocalName) >= Math.Max(1, CheckedFiles(i).KnownFileSize) Then CheckedFiles.RemoveAt(i) i = i - 1 End If Next End Select '去重 CheckedFiles = ArrayNoDouble(CheckedFiles.ToArray) '如果没有剩余文件直接成功 If CheckedFiles.Count = 0 Then '直接成功 frmMain.Dispatcher.Invoke(OnSuccess, Name) Exit Sub End If End If '初始化文件组 Dim NewGroup As New WebGroup With {.OnSuccess = OnSuccess, .OnFail = OnFail, .Files = New ArrayList, .GetServerFileSize = RequireSize, .Name = Name} '二次处理文件,将 WebRequireFile 转为 WebFile For i = 0 To CheckedFiles.Count - 1 Dim NewFile As WebFile If CheckedFiles(0).LocalName = "" Then NewFile = New WebFile With {.AllWebAddress = CheckedFiles(i).WebURLs, .GetServerFileSize = RequireSize, .GroupName = Name, .LocalFolder = CheckedFiles(i).LocalFolder, .TotalSize = New FileSize(CheckedFiles(i).KnownFileSize), .RefreshName = True} Else NewFile = New WebFile With {.AllWebAddress = CheckedFiles(i).WebURLs, .GetServerFileSize = RequireSize, .GroupName = Name, .LocalFolder = CheckedFiles(i).LocalFolder, .TotalSize = New FileSize(CheckedFiles(i).KnownFileSize), .LocalName = CheckedFiles(i).LocalName, .RefreshName = False} End If NewGroup.Files.Add(NewFile) Next WebWaitingList.AddRange(NewGroup.Files) '添加进总列表 If NewGroup.Files.Count = 0 Then Exit Sub If WebGroups.ContainsKey(Name) Then frmDownloadRight.RemoveGroup(WebGroups(Name)) WebGroups.Remove(Name) End If log("[System] 开始下载列表文件:" & Name) WebTaskbarShow = ReadIni("setup", "DownProcess", "True") = "True" WebGroups.Add(Name, NewGroup) frmDownloadRight.AddGroup(NewGroup) '要求执行刷新 WebWaitingToRun() Catch ex As Exception ExShow(ex, "处理下载列表失败:" & Name, ErrorLevel.MsgboxAndFeedback) frmMain.Dispatcher.Invoke(OnFail, Name) End Try End Sub) th.Start() End Sub '下载中 ''' ''' 处于 Waiting 状态的文件。 ''' Private WebWaitingList As New ArrayList Public WebWaitingLockObj As Object = New Object ''' ''' 若队列有空闲,尝试让 Waiting 状态的文件开始下载。 ''' Private Sub WebWaitingToRun() SyncLock WebWaitingLockObj For i = 0 To Math.Min(6, WebWaitingList.Count - 1) Try If WebDownloadingList.Count >= WebDownloadCountMax Or i > Math.Min(6, WebWaitingList.Count - 1) Then Exit Sub If WebDownloadingList.Contains(WebWaitingList(i).UUID) Then GoTo NextFile WebDownloadingList.Add(WebWaitingList(i).UUID) WebWaitingList(i).State = If(WebWaitingList(i).GetServerFileSize = WebRequireSize.Require Or WebWaitingList(i).GetServerFileSize = WebRequireSize.AtLeast, WebDownloadStats.GetSize, WebDownloadStats.Download) WebWaitingList.RemoveAt(i) '移出等待队列 i = i - 1 Catch i = i - 1 End Try NextFile: Next End SyncLock End Sub ''' ''' 获取文件大小,之后转入 Fail 或 Download 状态。 ''' Private Sub WebStateGetSize(File As WebFile) Dim th As New Thread(Sub() Try If MODE_OFFLINE Then File.State = WebDownloadStats.Fail : Exit Sub Select Case File.GetServerFileSize Case WebRequireSize.AtLeast '要求至少有一定大小 Dim TrueSize = GetFileSize(File.WebAddress, File) If TrueSize < 1 Then Throw New Exception("获取文件大小失败!") ElseIf TrueSize < File.TotalSize Then Throw New Exception("文件过小!(" & New FileSize(TrueSize).ToString & "/" & File.TotalSize.ToString & ")") End If File.TotalSize = TrueSize Case WebRequireSize.Require '只是需要知道大小 File.TotalSize = GetFileSize(File.WebAddress, File) If File.TotalSize < 1 Then Throw New Exception("获取文件大小失败!") End Select '获取大小成功,开始下载 File.State = WebDownloadStats.Download Catch ex As Exception ExShow(ex, "获取文件大小失败(" & File.LocalName & ")", ErrorLevel.Slient) File.State = WebDownloadStats.Fail End Try End Sub) th.Start() End Sub ''' ''' 下载文件,之后转入 Fail 或 Success 状态。 ''' Private Sub WebStateDownload(File As WebFile) Dim th As New Thread(Sub() Try '准备 If Not Directory.Exists(File.LocalFolder) Then Directory.CreateDirectory(File.LocalFolder) File.SpeedCount = My.Computer.Clock.TickCount '下载 My.Computer.Network.DownloadFile(File.WebAddress, File.LocalAddress & DOWNLOADING_END, "", "", False, 10000, True) '下载完毕检查 File.GetSize = GetFileSize(File.LocalAddress & DOWNLOADING_END) If File.GetServerFileSize = WebRequireSize.DontNeed Or (File.GetSize = File.TotalSize And File.GetSize > 1) Then '下载成功 IO.File.Delete(File.LocalAddress) Rename(File.LocalAddress & DOWNLOADING_END, File.LocalAddress) log("[Download] 下载文件成功:" & File.WebAddress) File.State = WebDownloadStats.Success Else '下载失败 log("[Download] 下载文件失败(文件大小不匹配):" & File.LocalAddress & ",所需大小:" & File.TotalSize.ToString & ",实际大小:" & File.GetSize.ToString & "(" & File.GetSize.Value & ")", True) IO.File.Delete(File.LocalAddress & DOWNLOADING_END) File.State = WebDownloadStats.Fail End If Catch ex As Exception ExShow(ex, "下载文件失败(" & File.LocalName & ")", ErrorLevel.Slient) File.State = WebDownloadStats.Fail End Try End Sub) th.Start() '刷新显示线程 File.RefreshThread = New Thread(Sub() Try Do While File.State = WebDownloadStats.Download Thread.Sleep(950) If Not File.State = WebDownloadStats.Download Then Exit Sub Dim NewSize = GetFileSize(File.LocalAddress & DOWNLOADING_END) File.Speed = (NewSize - File.GetSize) / (My.Computer.Clock.TickCount - File.SpeedCount) * 1000 File.SpeedCount = My.Computer.Clock.TickCount File.GetSize = NewSize File.Percent = If(File.TotalSize = 0, 0, Math.Round(File.GetSize / File.TotalSize, 3)) If Not File.State = WebDownloadStats.Download Then Exit Sub frmDownloadRight.RefreshFile(File) Loop Catch End Try End Sub) File.RefreshThread.Start() End Sub ''' ''' 下载失败,之后可能转入 Retry。若重试成功才返回 True。 ''' ''' Private Function WebStateFail(File As WebFile) '重试 If File.NextAddress() And Not MODE_OFFLINE Then '重试成功 log("[Download] 重试地址:" & File.WebAddress) File.State = WebDownloadStats.Retry WebStateFail = True Else '重试失败 WebStateFail = False End If '队列处理 WebDownloadingList.Remove(File.UUID) WebWaitingToRun() End Function ''' ''' 下载成功。 ''' Private Sub WebStateSuccess(File As WebFile) '队列处理 WebDownloadingList.Remove(File.UUID) WebWaitingToRun() End Sub '任务栏进度条 Public WebTaskbarShow As Boolean = True Private WebTaskbarLastState As TaskbarItemProgressState = TaskbarItemProgressState.None Private WebTaskbarLastValue As Double = 0 ''' ''' 下载完成检查。 ''' ''' Public Sub WebFinishCheck(ByVal Name As String) Try If Not WebGroups.Keys.Contains(Name) Then Exit Sub Dim Group As WebGroup = WebGroups(Name) If IsNothing(Group.Files) Then Exit Sub If Not Group.Files.Count = Group.FinishedCount Then Exit Sub '遍历文件:如果存在失败的文件,将 isFail 设置为 True Dim isFail As Boolean = False For i = Group.Files.Count - 1 To 0 Step -1 If Group.Files(i).State = WebDownloadStats.Fail Then isFail = True Next i '下载完成 log("[Download] 列表文件下载完成:" & Name) '结束 WebGroups.Remove(Name) frmDownloadRight.RemoveGroup(Group) If isFail Then frmMain.Dispatcher.Invoke(Group.OnFail, Name) Else frmMain.Dispatcher.Invoke(Group.OnSuccess, Name) End If Catch ex As Exception ExShow(ex, "检查下载列表出错") End Try End Sub ''' ''' 停止下载列表文件。 ''' ''' ''' Public Sub WebDownloadStop(ByVal Name As String) If WebGroups.ContainsKey(Name) Then frmDownloadRight.RemoveGroup(WebGroups(Name)) WebGroups.Remove(Name) End If End Sub Public Sub WebStartRun() Dim th As New Thread(Sub() Do While True WebTimer() Thread.Sleep(250) Loop End Sub) th.Priority = ThreadPriority.BelowNormal th.Start() End Sub Public Sub WebTimer() If Not WebTaskbarShow Then Exit Sub '应有值 Dim ShouldState As TaskbarItemProgressState Dim ShouldValue As Double '获取应有值 Try If WebGroups.Count > 0 Then Dim TotalPercent As Double = 0 For Each Group As WebGroup In WebGroups.Values TotalPercent = TotalPercent + Group.Percent Next ShouldState = If(TotalPercent = 0, TaskbarItemProgressState.Indeterminate, TaskbarItemProgressState.Normal) ShouldValue = TotalPercent / WebGroups.Count Else ShouldState = TaskbarItemProgressState.None ShouldValue = 0 End If Catch End Try '更新值 If Not WebTaskbarLastState = ShouldState Then frmMain.Dispatcher.Invoke(Sub() frmMain.TaskbarItemInfo.ProgressState = ShouldState) WebTaskbarLastState = ShouldState End If If Not WebTaskbarLastValue = ShouldValue Then frmMain.Dispatcher.Invoke(Sub() frmMain.TaskbarItemInfo.ProgressValue = ShouldValue) WebTaskbarLastValue = ShouldValue End If End Sub End Module 'HTTP下载 Public Module BitDrawer Public GifList As New ArrayList 'Gif列表 'Timer Public Sub GifStartRun() Dim time As New DispatcherTimer() time.Interval = TimeSpan.FromMilliseconds(1) AddHandler time.Tick, AddressOf GifTimer time.Start() End Sub Public Sub GifTimer() For i = 0 To GifList.Count - 1 Dim gif As MyBitmap = GifList(i) '切换Gif的帧 If My.Computer.Clock.TickCount - gif.RoundLastTime >= gif.RoundTime Then gif.RoundLastTime = My.Computer.Clock.TickCount If gif.CurrentFlame = gif.MaxFlame - 1 Then If gif.PlayOnce Then '如果只播放一次那么拜拜 GifList.Remove(gif) Else '如果不只播放一次那么继续 gif.SetGifFrame(0) End If Else gif.SetGifFrame(gif.CurrentFlame + 1) End If End If '刷新绑定控件的显示 If Not IsNothing(gif.Control) Then gif.Control.Source = New MyBitmap(gif.Pic) Next End Sub '类 Public Class MyBitmap ''' ''' 存储的图片 ''' ''' Public Pic As System.Drawing.Bitmap ''' ''' (Gif)绑定的控件 ''' ''' Public Control As System.Windows.Controls.Image = Nothing ''' ''' (Gif)每一帧的时间 ''' ''' Public RoundTime As Integer ''' ''' (Gif)上一次改变时的时间 ''' ''' Public RoundLastTime As Integer ''' ''' (Gif)是否只播放一次 ''' ''' Public PlayOnce As Boolean = False ''' ''' (Gif)当前帧,从0开始 ''' ''' Public CurrentFlame As Integer = 0 ''' ''' (Gif)最大帧数,从1开始 ''' ''' Public MaxFlame As Integer '类型转换 '支持的类:Image ImageSource Bitmap ImageBrush Public Shared Widening Operator CType(ByVal Image As System.Drawing.Image) As MyBitmap Return New MyBitmap(Image) End Operator Public Shared Widening Operator CType(ByVal Image As MyBitmap) As System.Drawing.Image Return Image.Pic End Operator Public Shared Widening Operator CType(ByVal Image As ImageSource) As MyBitmap Return New MyBitmap(Image) End Operator Public Shared Widening Operator CType(ByVal Image As MyBitmap) As ImageSource Dim ptr As IntPtr = Image.Pic.GetHbitmap() Dim re As ImageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()) DeleteObject(ptr) Return re End Operator Public Shared Widening Operator CType(ByVal Image As System.Drawing.Bitmap) As MyBitmap Return New MyBitmap(Image) End Operator Public Shared Widening Operator CType(ByVal Image As MyBitmap) As System.Drawing.Bitmap Return Image.Pic End Operator Public Shared Widening Operator CType(ByVal Image As ImageBrush) As MyBitmap Return New MyBitmap(Image) End Operator Public Shared Widening Operator CType(ByVal Image As MyBitmap) As ImageBrush Return New ImageBrush(New MyBitmap(Image.Pic)) End Operator '构造函数 Public Sub New() End Sub Public Sub New(ByVal FilePath As String) Try If FilePath.StartsWith(PATH_IMAGE) Then '使用缓存 If BitmapCache.ContainsKey(FilePath) Then Pic = BitmapCache(FilePath).Pic Else Pic = New MyBitmap(CType((New ImageSourceConverter).ConvertFromString(FilePath), ImageSource)) BitmapCache.Add(FilePath, Pic) End If Else Using inputStream As New FileStream(FilePath, FileMode.Open) Pic = New System.Drawing.Bitmap(inputStream) End Using End If Catch ex As Exception Pic = My.Application.TryFindResource(FilePath) If IsNothing(Pic) Then Pic = New System.Drawing.Bitmap(1, 1) ExShow(ex, "加载图片失败,该图片加载已被跳过:" & FilePath, ErrorLevel.DebugOnly) End If End Try End Sub Public Sub New(ByVal Image As ImageSource) Using MS = New MemoryStream() Dim Encoder = New PngBitmapEncoder() Encoder.Frames.Add(BitmapFrame.Create(Image)) Encoder.Save(MS) Pic = New System.Drawing.Bitmap(MS) End Using End Sub Public Sub New(ByVal Image As System.Drawing.Image) Pic = Image End Sub Public Sub New(ByVal Image As System.Drawing.Bitmap) Pic = Image End Sub Public Sub New(ByVal Image As ImageBrush) Using MS = New MemoryStream() Dim Encoder = New BmpBitmapEncoder() Encoder.Frames.Add(BitmapFrame.Create(Image.ImageSource)) Encoder.Save(MS) Pic = New System.Drawing.Bitmap(MS) End Using End Sub ''' ''' 设置Gif的当前帧 ''' ''' 帧的编号,从0开始,超限制报错 ''' Public Sub SetGifFrame(ByVal count As Integer) Pic.SelectActiveFrame(New FrameDimension(Pic.FrameDimensionsList()(0)), count) CurrentFlame = count End Sub ''' ''' 把这个对象视作Gif初始化 ''' ''' 两帧之间的间隔(毫秒)。 ''' 是否只播放一次,True为是。 ''' 绑定的控件。 ''' Public Sub GifLoad(ByVal round As Integer, ByVal once As Boolean, Optional ByVal control As System.Windows.Controls.Image = Nothing) RoundTime = round RoundLastTime = My.Computer.Clock.TickCount PlayOnce = once Me.Control = control GifList.Add(Me) MaxFlame = Pic.GetFrameCount(New FrameDimension(Pic.FrameDimensionsList()(0))) End Sub ''' ''' 获取旋转的图片,这个方法不会导致原对象改变 ''' ''' 旋转角度,单位为角度不是弧度啊 ''' 旋转后的Bitmap ''' Public Function Rotation(ByVal angle As Double) As System.Drawing.Bitmap With Me Dim img As System.Drawing.Image = Me.Pic Dim bitSize As Single = Math.Sqrt(img.Width ^ 2 + img.Height ^ 2) bitSize = img.Width Dim bmp As System.Drawing.Bitmap = New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize)) Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) g.TranslateTransform(bitSize / 2, bitSize / 2) g.RotateTransform(angle) g.TranslateTransform(-bitSize / 2, -bitSize / 2) g.DrawImage(img, New System.Drawing.Rectangle(0, 0, img.Width, img.Width)) End Using Return bmp End With End Function ''' ''' 获取左右翻转的图片,这个方法不会导致原对象改变 ''' ''' ''' Public Function LeftRightFilp() As System.Drawing.Bitmap Dim bmp As System.Drawing.Bitmap = New System.Drawing.Bitmap(Me.Pic) bmp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX) Return bmp End Function End Class '合并Bitmap Public Function BitmapAdd(ByVal background As System.Drawing.Bitmap, ByVal position As System.Drawing.Point, ByVal img As System.Drawing.Bitmap) As System.Drawing.Bitmap Dim graph As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(background) graph.DrawImage(img, position) Return background End Function '裁剪Bitmap Public Function BitmapCut(ByVal img As BitmapSource, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) As BitmapSource Return New CroppedBitmap(img, New Int32Rect(x, y, width, height)) End Function End Module '绘图 'Public Module FTP ' Public Function DownloadFTPFile(ByVal ftpclient As FTPDownloader, ByVal webPath As String, ByVal localPath As String) ' File.Delete(localPath) ' DownloadFTPFile = False ' Try ' If Not ftpclient.ftpIsLogin Then ftpclient.Login() ' If webPath.Contains("/") Then ftpclient.ChangeDirectory(Mid(webPath, 1, webPath.LastIndexOf("/"))) ' ftpclient.DownloadFile(Mid(webPath, webPath.LastIndexOf("/") + 2), localPath) ' Return True ' Catch ex As Exception ' End Try ' End Function '直接下载单个文件 ' Public Class FTPDownloader '#Region "变量声明" ' Private m_sRemoteHost, m_sRemotePath, m_sRemoteUser As String ' Private m_sRemotePassword, m_sMess As String ' Private m_iRemotePort, m_iBytes As Int32 ' Private m_objClientSocket As Socket ' Private m_iRetValue As Int32 ' Public ftpIsLogin As Boolean ' Private m_sMes, m_sReply As String ' '设置用户来对FTP服务器读取和写入数据的数据包的大小 ' Public Const BLOCK_SIZE = 512 ' Private m_aBuffer(BLOCK_SIZE) As Byte ' Private ASCII As Encoding = Encoding.ASCII ' Public flag_bool As Boolean ' '普通变量定义 ' Private m_sMessageString As String '#End Region '#Region "构造函数" ' Public Sub New() ' m_sRemoteHost = "localhost" ' m_sRemotePath = "." ' m_sRemoteUser = "anonymous" ' m_sRemotePassword = "" ' m_sMessageString = "" ' m_iRemotePort = 21 ' ftpIsLogin = False ' End Sub ' Public Sub New(ByVal sRemoteHost As String, ByVal sRemotePath As String, ByVal sRemoteUser As String, ByVal sRemotePassword As String, ByVal iRemotePort As Int32) ' m_sRemoteHost = sRemoteHost ' m_sRemotePath = sRemotePath ' m_sRemoteUser = sRemoteUser ' m_sRemotePassword = sRemotePassword ' m_sMessageString = "" ' m_iRemotePort = iRemotePort ' ftpIsLogin = False ' End Sub '#End Region '#Region "属性" ' '设置或获取FTP服务器的名称 ' Public Property RemoteHostFTPServer() As String ' Get ' Return m_sRemoteHost ' End Get ' Set(ByVal Value As String) ' m_sRemoteHost = Value ' End Set ' End Property ' '设置或获取FTP服务器的端口 ' Public Property RemotePort() As Int32 ' Get ' Return m_iRemotePort ' End Get ' Set(ByVal Value As Int32) ' m_iRemotePort = Value ' End Set ' End Property ' '设置或获取FTP服务器的远程路径 ' Public Property RemotePath() As String ' Get ' Return m_sRemotePath ' End Get ' Set(ByVal Value As String) ' m_sRemotePath = Value ' End Set ' End Property ' '设置或获取FTP服务器的用户 ' Public Property RemoteUser() As String ' Get ' Return m_sRemoteUser ' End Get ' Set(ByVal Value As String) ' m_sRemoteUser = Value ' End Set ' End Property ' '设置FTP服务器的密码 ' Public Property RemotePassword() As String ' Get ' Return m_sRemotePassword ' End Get ' Set(ByVal Value As String) ' m_sRemotePassword = Value ' End Set ' End Property ' '设置信息 ' Public Property MessageString() As String ' Get ' Return m_sMessageString ' End Get ' Set(ByVal Value As String) ' m_sMessageString = Value ' End Set ' End Property '#End Region ' '登录FTP服务器 ' Public Function Login() As Boolean ' If ftpIsLogin Then Return True ' m_objClientSocket = _ ' New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) ' Dim ep As New IPEndPoint(Dns.GetHostEntry(m_sRemoteHost).AddressList(0), m_iRemotePort) ' Try ' m_objClientSocket.Connect(ep) ' Catch ex As Exception ' MessageString = m_sReply ' Throw New IOException("无法链接到FTP服务器。") ' End Try ' ReadReply() ' If (m_iRetValue <> 220) Then ' CloseConnection() ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' '为了发送一个对服务器的用户登录ID,发送一个FTP命令 ' SendCommand("USER " & m_sRemoteUser) ' If (Not (m_iRetValue = 331 Or m_iRetValue = 230)) Then ' Cleanup() ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' If (m_iRetValue <> 230) Then ' '为了发送一个对服务器的用户密码,发送一个FTP命令 ' SendCommand("PASS " & m_sRemotePassword) ' If (Not (m_iRetValue = 230 Or m_iRetValue = 202)) Then ' Cleanup() ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' End If ' ftpIsLogin = True ' '为了改变映射的远程服务器的文件夹的目录,调用用户定义的ChangeDirectory函数 ' ChangeDirectory(m_sRemotePath) ' '返回最终结果 ' Return ftpIsLogin ' End Function ' '获取文件列表 ' Public Function GetFileList(ByVal sMask As String) As String() ' Dim cSocket As Socket ' Dim bytes As Int32 ' Dim seperator As Char = ControlChars.Lf ' Dim mess() As String ' m_sMes = "" ' '检查是否登录 ' If (Not (ftpIsLogin)) Then Login() ' cSocket = CreateDataSocket() ' '发送FTP命令 ' SendCommand("NLST " & sMask) ' If (Not (m_iRetValue = 150 Or m_iRetValue = 125)) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' m_sMes = "" ' Do While (True) ' Array.Clear(m_aBuffer, 0, m_aBuffer.Length) ' bytes = cSocket.Receive(m_aBuffer, m_aBuffer.Length, 0) ' m_sMes += ASCII.GetString(m_aBuffer, 0, bytes) ' If (bytes < m_aBuffer.Length) Then ' Exit Do ' End If ' Loop ' mess = m_sMes.Split(seperator) ' cSocket.Close() ' ReadReply() ' If (m_iRetValue <> 226) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' Return mess ' End Function ' '获取文件大小 ' Public Function GetFileSize(ByVal sFileName As String) As Integer ' Dim size As Integer ' '检查是否登录 ' If Not ftpIsLogin Then Login() ' '发送FTP命令 ' SetBinaryMode(True) ' SendCommand("SIZE " & sFileName) ' size = 0 ' If (m_iRetValue = 213) Then ' size = Int64.Parse(m_sReply.Substring(4)) ' Else ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' Return size ' End Function ' '下载文件 ' Public Sub DownloadFile(ByVal fileName As String, Optional ByVal localFile As String = "", Optional ByVal isResume As Boolean = False) ' Dim st As Stream ' Dim cSocket As Socket ' Dim offset, npos As Integer ' If Not ftpIsLogin Then Login() '检查是否登录 ' SetBinaryMode(True) '设置下载模式 ' '检查下载目录 ' If localFile = "" Then localFile = fileName ' If Not (File.Exists(localFile)) Then ' st = File.Create(localFile) ' st.Close() ' End If ' '下载 ' Using output As New FileStream(localFile, FileMode.Open) ' cSocket = CreateDataSocket() ' offset = 0 ' If isResume Then ' offset = output.Length ' If offset > 0 Then ' '发送一个FTP命令重新启动 ' SendCommand("REST " & offset) ' If (m_iRetValue <> 350) Then offset = 0 ' npos = output.Seek(offset, SeekOrigin.Begin) ' End If ' End If ' '发送一个FTP命令重新找到一个文件 ' SendCommand("RETR " & fileName) ' If (Not (m_iRetValue = 150 Or m_iRetValue = 125)) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' Do While (True) ' Array.Clear(m_aBuffer, 0, m_aBuffer.Length) ' m_iBytes = cSocket.Receive(m_aBuffer, m_aBuffer.Length, 0) ' output.Write(m_aBuffer, 0, m_iBytes) ' If (m_iBytes <= 0) Then ' Exit Do ' End If ' Loop ' End Using ' If (cSocket.Connected) Then ' cSocket.Close() ' End If ' ReadReply() ' If (Not (m_iRetValue = 226 Or m_iRetValue = 250)) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' End Sub ' '设置下载模式 ' Public Sub SetBinaryMode(ByVal bMode As Boolean) ' '发送FTP命令,设置为二进制模式(True)或ASCII模式(False) ' SendCommand(If(bMode, "TYPE I", "TYPE A")) ' If (m_iRetValue <> 200) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' End Sub ' 'Dim str_file_path As String = "C:\test.txt" '要上传的文件名及路径 ' 'Dim str_filename As String = IO.Path.GetFileName(str_file_path) '要上传的文件名 ' 'Dim MyFileSize As Integer = FileLen(str_file_path) '要上传得文件大小 ' 'If (ftpClient.Login() = True) Then ' ' '创建一个新文件夹 ' ' ftpClient.CreateDirectory("FTPFOLDERNEW") ' ' '将新的文件夹设置为活动文件夹。 ' ' ftpClient.ChangeDirectory("FTPFOLDERNEW") ' ' '设置FTP模式 ' ' ftpClient.SetBinaryMode(True) ' ' '从你的硬盘上上载一个文件到FTP网页 ' ' ftpClient.UploadFile(str_file_path) ' ' '获得刚刚上传的文件的大小 ' ' Dim lng_filesize As Integer ' ' lng_filesize = ftpClient.GetFileSize(str_filename) ' ' If lng_filesize = MyFileSize Then ' ' MsgBox("成功上传文件" & str_filename & " 文件大小" & lng_filesize) ' ' Else ' ' MsgBox("上传失败") ' ' End If ' ' '对上载文件重命名 ' ' 'ftpClient.RenameFile("SampleFile.xml", "SampleFile_new.xml") ' ' '删除一个文件 ' ' 'ftpClient.DeleteFile("SampleFile_new.xml") ' ' ftpClient.CloseConnection() ' 'End If ' ''这是一个从你的本地硬盘上向你的FTP文件夹中上载文件的函数 ' 'Public Sub UploadFile(ByVal sFileName As String) ' ' UploadFile(sFileName, False) ' 'End Sub ' ''这是一个从你的本地硬盘上向你的FTP网页上上载的函数和设置恢复标志 ' 'Public Sub UploadFile(ByVal sFileName As String, ' ' ByVal bResume As Boolean) ' ' Dim cSocket As Socket ' ' Dim offset As Integer ' ' Dim input As FileStream ' ' Dim bFileNotFound As Boolean ' ' If (Not (ftpIsLogin)) Then ' ' Login() ' ' End If ' ' cSocket = CreateDataSocket() ' ' offset = 0 ' ' If (bResume) Then ' ' Try ' ' SetBinaryMode(True) ' ' offset = GetFileSize(sFileName) ' ' Catch ex As Exception ' ' offset = 0 ' ' End Try ' ' End If ' ' If (offset > 0) Then ' ' SendCommand("REST " & offset) ' ' If (m_iRetValue <> 350) Then ' ' '远程服务器可能不支持恢复。 ' ' offset = 0 ' ' End If ' ' End If ' ' '发送一个FTP命令,存储一个文件。 ' ' SendCommand("STOR " & Path.GetFileName(sFileName)) ' ' If (Not (m_iRetValue = 125 Or m_iRetValue = 150)) Then ' ' MessageString = m_sReply ' ' Throw New IOException(m_sReply.Substring(4)) ' ' End If ' ' '在上载之前,检查文件是否存在。 ' ' bFileNotFound = False ' ' If (File.Exists(sFileName)) Then ' ' '打开输入流读取源文件 ' ' input = New FileStream(sFileName, FileMode.Open) ' ' If (offset <> 0) Then ' ' input.Seek(offset, SeekOrigin.Begin) ' ' End If ' ' '上载这个文件 ' ' m_iBytes = input.Read(m_aBuffer, 0, m_aBuffer.Length) ' ' Do While (m_iBytes > 0) ' ' cSocket.Send(m_aBuffer, m_iBytes, 0) ' ' m_iBytes = input.Read(m_aBuffer, 0, m_aBuffer.Length) ' ' Loop ' ' input.Close() ' ' Else ' ' bFileNotFound = True ' ' End If ' ' If (cSocket.Connected) Then ' ' cSocket.Close() ' ' End If ' ' '如果找不到文件,检查返回值 ' ' If (bFileNotFound) Then ' ' MessageString = m_sReply ' ' Throw New IOException("The file: " & sFileName & " was not found. " & _ ' ' "Cannot upload the file to the FTP site") ' ' End If ' ' ReadReply() ' ' If (Not (m_iRetValue = 226 Or m_iRetValue = 250)) Then ' ' MessageString = m_sReply ' ' Throw New IOException(m_sReply.Substring(4)) ' ' End If ' 'End Sub ' ''从远程FTP服务器上删除一个文件。 ' 'Public Function DeleteFile(ByVal sFileName As String) As Boolean ' ' Dim bResult As Boolean ' ' bResult = True ' ' If (Not (ftpIsLogin)) Then ' ' Login() ' ' End If ' ' '发送一个FTP命令,删除一个文件。 ' ' SendCommand("DELE " & sFileName) ' ' If (m_iRetValue <> 250) Then ' ' bResult = False ' ' MessageString = m_sReply ' ' End If ' ' '返回最终结果 ' ' Return bResult ' 'End Function ' ''在远程FTP服务器上重命名一个文件 ' 'Public Function RenameFile(ByVal sOldFileName As String, ' 'ByVal sNewFileName As String) As Boolean ' ' Dim bResult As Boolean ' ' bResult = True ' ' If (Not (ftpIsLogin)) Then ' ' Login() ' ' End If ' ' '发送一个FTP命令,对一个文件重命名 ' ' SendCommand("RNFR " & sOldFileName) ' ' If (m_iRetValue <> 350) Then ' ' MessageString = m_sReply ' ' Throw New IOException(m_sReply.Substring(4)) ' ' End If ' ' '发送一个FTP命令,对一个文件更改为新名称 ' ' '如果新的文件名存在,会被覆盖。 ' ' SendCommand("RNTO " & sNewFileName) ' ' If (m_iRetValue <> 250) Then ' ' MessageString = m_sReply ' ' Throw New IOException(m_sReply.Substring(4)) ' ' End If ' ' '返回最终结果 ' ' Return bResult ' 'End Function ' ''这是一个在远程服务器上创建目录的函数 ' 'Public Function CreateDirectory(ByVal sDirName As String) As Boolean ' ' Dim bResult As Boolean ' ' bResult = True ' ' If (Not (ftpIsLogin)) Then ' ' Login() ' ' End If ' ' '发送一个FTP命令,在FTP服务器上制作一个目录 ' ' SendCommand("MKD " & sDirName) ' ' If (m_iRetValue <> 257) Then ' ' bResult = False ' ' MessageString = m_sReply ' ' End If ' ' '返回最终结果 ' ' Return bResult ' 'End Function ' ''这是一个在远程FTP服务器上删除目录的函数 ' 'Public Function RemoveDirectory(ByVal sDirName As String) As Boolean ' ' Dim bResult As Boolean ' ' bResult = True ' ' '检查是否已登录FTP服务器 ' ' If (Not (ftpIsLogin)) Then ' ' Login() ' ' End If ' ' '发送一个FTP命令,删除在FTP服务器上的目录 ' ' SendCommand("RMD " & sDirName) ' ' If (m_iRetValue <> 250) Then ' ' bResult = False ' ' MessageString = m_sReply ' ' End If ' ' '返回最终结果 ' ' Return bResult ' 'End Function ' '改变当前工作目录 ' Public Function ChangeDirectory(ByVal sDirName As String) As Boolean ' Dim bResult As Boolean ' bResult = True ' '检查你是否在根目录 ' If (sDirName.Equals(".")) Then Return False ' '检查是否已登录FTP服务器 ' If Not (ftpIsLogin) Then Login() ' '发送FTP命令,改变在FTP服务器上的目录。 ' SendCommand("CWD " & sDirName) ' If (m_iRetValue <> 250) Then ' bResult = False ' MessageString = m_sReply ' End If ' Me.m_sRemotePath = sDirName ' '返回最终结果 ' Return bResult ' End Function ' '关闭FTP链接 ' Public Sub CloseConnection() ' If Not (m_objClientSocket Is Nothing) Then ' '发送一个FTP命令,结束FTP服务系统 ' SendCommand("QUIT") ' End If ' Cleanup() ' End Sub '#Region "内部函数" ' '从FTP服务器得到回应。 ' Private Sub ReadReply() ' m_sMes = "" ' m_sReply = ReadLine() ' m_iRetValue = Int32.Parse(m_sReply.Substring(0, 3)) ' End Sub ' '重置 ' Private Sub Cleanup() ' If Not (m_objClientSocket Is Nothing) Then ' m_objClientSocket.Close() ' m_objClientSocket = Nothing ' End If ' ftpIsLogin = False ' End Sub ' '从FTP服务器读取一行 ' Private Function ReadLine(Optional ByVal bClearMes As Boolean = False) As String ' Dim seperator As Char = ControlChars.Lf ' Dim mess() As String ' If (bClearMes) Then ' m_sMes = "" ' End If ' Do While (True) ' Array.Clear(m_aBuffer, 0, BLOCK_SIZE) ' Try ' m_iBytes = m_objClientSocket.Receive(m_aBuffer, m_aBuffer.Length, 0) ' Catch ex As Exception ' Login() ' m_iBytes = m_objClientSocket.Receive(m_aBuffer, m_aBuffer.Length, 0) ' End Try ' m_sMes += ASCII.GetString(m_aBuffer, 0, m_iBytes) ' If (m_iBytes < m_aBuffer.Length) Then ' Exit Do ' End If ' Loop ' mess = m_sMes.Split(seperator) ' If (m_sMes.Length > 2) Then ' m_sMes = mess(mess.Length - 2) ' Else ' m_sMes = mess(0) ' End If ' If (Not (m_sMes.Substring(3, 1).Equals(" "))) Then ' Return ReadLine(True) ' End If ' Return m_sMes ' End Function ' '向FTP服务器发送命令 ' Private Sub SendCommand(ByVal command As String) ' command = command & ControlChars.CrLf ' Dim cmdbytes As Byte() = ASCII.GetBytes(command) ' m_objClientSocket.Send(cmdbytes, cmdbytes.Length, 0) ' ReadReply() ' End Sub ' '创建一个数据包 ' Private Function CreateDataSocket() As Socket ' Dim index1, index2, len As Int32 ' Dim partCount, i, port As Int32 ' Dim ipData, buf, ipAddress As String ' Dim parts(6) As Int32 ' Dim ch As Char ' Dim s As Socket ' Dim ep As IPEndPoint ' '发送一个FTP命令,用于被动数据链接 ' SendCommand("PASV") ' If (m_iRetValue <> 227) Then ' MessageString = m_sReply ' Throw New IOException(m_sReply.Substring(4)) ' End If ' index1 = m_sReply.IndexOf("(") ' index2 = m_sReply.IndexOf(")") ' ipData = m_sReply.Substring(index1 + 1, index2 - index1 - 1) ' len = ipData.Length ' partCount = 0 ' buf = "" ' For i = 0 To ((len - 1) And partCount <= 6) ' ch = Char.Parse(ipData.Substring(i, 1)) ' If (Char.IsDigit(ch)) Then ' buf += ch ' ElseIf (ch <> ",") Then ' MessageString = m_sReply ' Throw New IOException("Malformed PASV reply: " & m_sReply) ' End If ' If ((ch = ",") Or (i + 1 = len)) Then ' Try ' parts(partCount) = Int32.Parse(buf) ' partCount += 1 ' buf = "" ' Catch ex As Exception ' MessageString = m_sReply ' Throw New IOException("Malformed PASV reply: " & m_sReply) ' End Try ' End If ' Next ' ipAddress = parts(0) & "." & parts(1) & "." & parts(2) & "." & parts(3) ' '在Visual Basic .Net 2002中进行调用。你想移动8位。在Visual Basic .NET 2002中,你必须将此数乘2的8次方。 ' '端口=parts(4)*(2^8) ' '进行这个调用,并且用Visual Basic .NET 2003解释当前行。 ' port = parts(4) << 8 ' '确定数据端口数 ' port = port + parts(5) ' s = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) ' ep = New IPEndPoint(Dns.GetHostEntry(ipAddress).AddressList(0), port) ' Try ' s.Connect(ep) ' Catch ex As Exception ' MessageString = m_sReply ' Throw New IOException("Cannot connect to remote server.") ' '如果你不能链接到特定的FTP服务器,也就是说,将其布尔值设置为False。 ' flag_bool = False ' End Try ' '如果你能够链接到特定的FTP服务器,将布尔值设置为True。 ' flag_bool = True ' Return s ' End Function '#End Region ' End Class 'End Module 'FTP协议下载