Application.xaml.vb 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. Imports System.Reflection
  2. Imports System.Windows.Threading
  3. Public Class Application
  4. #If DEBUG Then
  5. ''' <summary>
  6. ''' 用于开始程序时的一些测试。
  7. ''' </summary>
  8. Private Sub Test()
  9. Try
  10. ModDevelop.Start()
  11. Catch ex As Exception
  12. Log(ex, "开发者模式测试出错", LogLevel.Msgbox)
  13. End Try
  14. End Sub
  15. #End If
  16. '开始
  17. Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
  18. Try
  19. '动态 DLL 调用(必须尽量在前面,否则模块加载 CacheCow 等 DLL 就可能导致崩溃)
  20. AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
  21. '开始
  22. SecretOnApplicationStart()
  23. '检查参数调用
  24. If e.Args.Length > 0 Then
  25. If e.Args(0) = "--update" Then
  26. '自动更新
  27. UpdateReplace(e.Args(1), e.Args(2).Trim(""""), e.Args(3).Trim(""""), e.Args(4))
  28. Environment.Exit(ProcessReturnValues.TaskDone)
  29. ElseIf e.Args(0) = "--gpu" Then
  30. '调整显卡设置
  31. Try
  32. SetGPUPreference(e.Args(1).Trim(""""))
  33. Environment.Exit(ProcessReturnValues.TaskDone)
  34. Catch ex As Exception
  35. Environment.Exit(ProcessReturnValues.Fail)
  36. End Try
  37. ElseIf e.Args(0).StartsWithF("--memory") Then
  38. '内存优化
  39. Dim Ram = My.Computer.Info.AvailablePhysicalMemory
  40. Try
  41. PageOtherTest.MemoryOptimizeInternal(False)
  42. Catch ex As Exception
  43. MsgBox(ex.Message, MsgBoxStyle.Critical, "内存优化失败")
  44. Environment.Exit(-1)
  45. End Try
  46. If My.Computer.Info.AvailablePhysicalMemory < Ram Then '避免 ULong 相减出现负数
  47. Environment.Exit(0)
  48. Else
  49. Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
  50. End If
  51. #If DEBUG Then
  52. '制作更新包
  53. ElseIf e.Args(0) = "--edit1" Then
  54. ExeEdit(e.Args(1), True)
  55. Environment.Exit(ProcessReturnValues.TaskDone)
  56. ElseIf e.Args(0) = "--edit2" Then
  57. ExeEdit(e.Args(1), False)
  58. Environment.Exit(ProcessReturnValues.TaskDone)
  59. #End If
  60. End If
  61. End If
  62. '初始化文件结构
  63. Directory.CreateDirectory(Path & "PCL\Pictures")
  64. Directory.CreateDirectory(Path & "PCL\Musics")
  65. Try
  66. Directory.CreateDirectory(PathTemp)
  67. If Not CheckPermission(PathTemp) Then Throw New Exception("PCL 没有对 " & PathTemp & " 的访问权限")
  68. Catch ex As Exception
  69. If PathTemp = IO.Path.GetTempPath() & "PCL\" Then
  70. MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDetail(), "缓存文件夹不可用")
  71. Else
  72. MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDetail(), "缓存文件夹不可用")
  73. Settings.Set("SystemSystemCache", "")
  74. PathTemp = IO.Path.GetTempPath() & "PCL\"
  75. End If
  76. End Try
  77. Directory.CreateDirectory(PathTemp & "Cache")
  78. Directory.CreateDirectory(PathAppdata)
  79. '检测单例
  80. #If Not DEBUG Then
  81. Dim ShouldWaitForExit As Boolean = e.Args.Length > 0 AndAlso e.Args(0) = "--wait" '要求等待已有的 PCL 退出
  82. Dim WaitRetryCount As Integer = 0
  83. WaitRetry:
  84. Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher ")
  85. If WindowHwnd = IntPtr.Zero Then FindWindow(Nothing, "Plain Craft Launcher 2 ")
  86. If WindowHwnd <> IntPtr.Zero Then
  87. If ShouldWaitForExit AndAlso WaitRetryCount < 20 Then '至多等待 10 秒
  88. WaitRetryCount += 1
  89. Thread.Sleep(500)
  90. GoTo WaitRetry
  91. End If
  92. '将已有的 PCL 窗口拖出来
  93. ShowWindowToTop(WindowHwnd)
  94. '播放提示音并退出
  95. Beep()
  96. Environment.[Exit](ProcessReturnValues.Cancel)
  97. End If
  98. #End If
  99. '设置 ToolTipService 默认值
  100. ToolTipService.InitialShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(300))
  101. ToolTipService.BetweenShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(400))
  102. ToolTipService.ShowDurationProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(9999999))
  103. ToolTipService.PlacementProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(Primitives.PlacementMode.Bottom))
  104. ToolTipService.HorizontalOffsetProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(8.0))
  105. ToolTipService.VerticalOffsetProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(4.0))
  106. '设置网络配置默认值
  107. ServicePointManager.Expect100Continue = False
  108. ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
  109. ServicePointManager.DefaultConnectionLimit = 10000
  110. ServicePointManager.UseNagleAlgorithm = False
  111. ServicePointManager.EnableDnsRoundRobin = True
  112. ServicePointManager.ReusePort = True
  113. '设置初始窗口
  114. If Settings.Get("UiLauncherLogo") Then
  115. FrmStart = New SplashScreen("Images\icon.ico")
  116. FrmStart.Show(False, True)
  117. End If
  118. '日志初始化
  119. LogStart()
  120. '添加日志
  121. Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode}{If(CommitHash = "", "", $",#{CommitHash}")})")
  122. #If RELEASE Then
  123. Log($"[Start] 识别码:{Identify}{If(ThemeCheckOne(9), ",正式版", "")}")
  124. #Else
  125. Log($"[Start] 识别码:{Identify}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
  126. #End If
  127. Log($"[Start] 程序路径:{PathWithName}")
  128. Log($"[Start] 系统编码:{Encoding.Default.HeaderName} ({Encoding.Default.CodePage}, GBK={IsGBKEncoding})")
  129. Log($"[Start] 管理员权限:{IsAdmin()}")
  130. '检测异常环境
  131. If Path.Contains(IO.Path.GetTempPath()) OrElse Path.Contains("AppData\Local\Temp\") Then
  132. MyMsgBox("请将 PCL 从压缩包中解压后再使用!" & vbCrLf & "如果不会解压,可以在网上寻找教程。", "尚未解压", "我知道了", IsWarn:=True)
  133. FormMain.EndProgramForce(ProcessReturnValues.Cancel)
  134. End If
  135. If Is32BitSystem Then
  136. MyMsgBox("PCL 和新版 Minecraft 均不再支持 32 位系统,部分功能将无法使用。" & vbCrLf & "非常建议重装为 64 位系统后再进行游戏!", "环境警告", "我知道了", IsWarn:=True)
  137. End If
  138. '计时
  139. Log("[Start] 第一阶段加载用时:" & GetTimeMs() - ApplicationStartTick & " ms")
  140. ApplicationStartTick = GetTimeMs()
  141. '执行测试
  142. #If DEBUG Then
  143. Test()
  144. #End If
  145. AniControlEnabled += 1
  146. Catch ex As Exception
  147. Dim FilePath As String = Nothing
  148. Try
  149. FilePath = PathWithName
  150. Catch
  151. End Try
  152. MsgBox(ex.GetDetail(True) & vbCrLf & "PCL 所在路径:" & If(String.IsNullOrEmpty(FilePath), "获取失败", FilePath), MsgBoxStyle.Critical, "PCL 初始化错误")
  153. FormMain.EndProgramForce(ProcessReturnValues.Exception)
  154. End Try
  155. End Sub
  156. '结束
  157. Private Sub Application_SessionEnding(sender As Object, e As SessionEndingCancelEventArgs) Handles Me.SessionEnding
  158. FrmMain.EndProgram(False)
  159. End Sub
  160. '异常
  161. Private Sub Application_DispatcherUnhandledException(sender As Object, e As DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
  162. On Error Resume Next
  163. '触发页面的 Dispatcher
  164. If FrmMain?.PageLeft IsNot Nothing AndAlso TypeOf FrmMain.PageLeft Is IDispatcherUnhandledException Then
  165. CType(FrmMain.PageLeft, IDispatcherUnhandledException).DispatcherUnhandledException(sender, e)
  166. If e.Handled Then Return
  167. End If
  168. If FrmMain?.PageRight IsNot Nothing AndAlso TypeOf FrmMain.PageRight Is IDispatcherUnhandledException Then
  169. CType(FrmMain.PageRight, IDispatcherUnhandledException).DispatcherUnhandledException(sender, e)
  170. If e.Handled Then Return
  171. End If
  172. '正常处理
  173. e.Handled = True
  174. If IsProgramEnded Then Return
  175. FeedbackInfo()
  176. Dim Detail As String = e.Exception.GetDetail(True)
  177. If Detail.Contains("System.Windows.Threading.Dispatcher.Invoke") OrElse Detail.Contains("MS.Internal.AppModel.ITaskbarList.HrInit") OrElse Detail.Contains("未能加载文件或程序集") OrElse
  178. Detail.Contains(".NET Framework") Then ' “自动错误判断” 的结果分析
  179. OpenWebsite("https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-offline-installer")
  180. Log(e.Exception, "你的 .NET Framework 版本过低或损坏,请下载并重新安装 .NET Framework 4.6.2!" & vbCrLf & "若无法安装,可在卸载高版本的 .NET Framework 后再试。", LogLevel.Critical, "运行环境错误")
  181. Else
  182. Log(e.Exception, "程序出现未知错误", LogLevel.Critical, "锟斤拷烫烫烫")
  183. End If
  184. End Sub
  185. '动态 DLL 调用
  186. Private Declare Function SetDllDirectory Lib "kernel32" Alias "SetDllDirectoryA" (lpPathName As String) As Boolean
  187. Public Shared Function AssemblyResolve(sender As Object, Args As ResolveEventArgs) As Assembly
  188. '缓存
  189. Static Prefixes As String() = {"NAudio", "Newtonsoft.Json", "Ookii.Dialogs.Wpf", "Imazen.WebP", "CacheCow.Common", "CacheCow.Client.FileStore", "CacheCow.Client", "System.Net.Http.Formatting", "System.ValueTuple"}
  190. Static Locks As New Dictionary(Of String, Object)(StringComparer.Ordinal)
  191. Static LoadedAssembly As New Dictionary(Of String, Assembly)(StringComparer.Ordinal)
  192. '查找对应的 DLL
  193. Dim Prefix As String = Prefixes.FirstOrDefault(Function(p) Args.Name.StartsWithF(p))
  194. If Prefix Is Nothing Then Return Nothing
  195. '加载 DLL
  196. If Not Locks.ContainsKey(Prefix) Then Locks(Prefix) = New Object()
  197. SyncLock Locks(Prefix)
  198. If Not LoadedAssembly.ContainsKey(Prefix) Then
  199. Log($"[Start] 加载 DLL:{Prefix}")
  200. LoadedAssembly(Prefix) = Assembly.Load(GetResources(Prefix))
  201. 'WebP 特判
  202. If Prefix = "Imazen.WebP" Then
  203. SetDllDirectory(PathPure.TrimEnd("\"c))
  204. Try
  205. WriteFile(PathPure & "libwebp.dll", GetResources("libwebp64"))
  206. Catch ex As Exception
  207. Log(ex, "写入 libwebp.dll 失败") '防止同时加载多个图片时,同时写入文件导致文件占用,进而导致崩溃
  208. End Try
  209. End If
  210. End If
  211. Return LoadedAssembly(Prefix)
  212. End SyncLock
  213. End Function
  214. '切换窗口
  215. '控件模板事件
  216. Private Sub MyIconButton_Click(sender As Object, e As EventArgs)
  217. Select Case Settings.Get("LoginType")
  218. Case McLoginType.Ms
  219. '微软
  220. Dim MsJson As JObject = GetJson(Settings.Get("LoginMsJson"))
  221. MsJson.Remove(sender.Tag)
  222. Settings.Set("LoginMsJson", MsJson.ToString(Newtonsoft.Json.Formatting.None))
  223. If FrmLoginMs.ComboAccounts.SelectedItem Is sender.Parent Then FrmLoginMs.ComboAccounts.SelectedIndex = 0
  224. FrmLoginMs.ComboAccounts.Items.Remove(sender.Parent)
  225. Case McLoginType.Legacy
  226. '离线
  227. Dim Names As New List(Of String)
  228. Names.AddRange(Settings.Get("LoginLegacyName").ToString.Split("¨"))
  229. Names.Remove(sender.Tag)
  230. Settings.Set("LoginLegacyName", Names.Join("¨"c))
  231. FrmLoginLegacy.ComboName.ItemsSource = Names
  232. FrmLoginLegacy.ComboName.Text = If(Names.Any, Names(0), "")
  233. Case Else
  234. '第三方
  235. Dim Token As String = GetStringFromEnum(Settings.Get("LoginType"))
  236. Dim Dict As New Dictionary(Of String, String)
  237. Dim Names As New List(Of String)
  238. Dim Passs As New List(Of String)
  239. If Not Settings.Get("Login" & Token & "Email") = "" Then Names.AddRange(Settings.Get("Login" & Token & "Email").ToString.Split("¨"))
  240. If Not Settings.Get("Login" & Token & "Pass") = "" Then Passs.AddRange(Settings.Get("Login" & Token & "Pass").ToString.Split("¨"))
  241. For i = 0 To Names.Count - 1
  242. Dict.Add(Names(i), Passs(i))
  243. Next
  244. Dict.Remove(sender.Tag)
  245. Settings.Set("Login" & Token & "Email", Dict.Keys.Join("¨"c))
  246. Settings.Set("Login" & Token & "Pass", Dict.Values.Join("¨"c))
  247. Select Case Token
  248. Case "Nide"
  249. FrmLoginNide.ComboName.ItemsSource = Dict.Keys
  250. FrmLoginNide.ComboName.Text = If(Dict.Keys.Any, Dict.Keys(0), "")
  251. FrmLoginNide.TextPass.Password = If(Dict.Values.Any, Dict.Values(0), "")
  252. Case "Auth"
  253. FrmLoginAuth.ComboName.ItemsSource = Dict.Keys
  254. FrmLoginAuth.ComboName.Text = If(Dict.Keys.Any, Dict.Keys(0), "")
  255. FrmLoginAuth.TextPass.Password = If(Dict.Values.Any, Dict.Values(0), "")
  256. End Select
  257. End Select
  258. End Sub
  259. Public Shared ShowingTooltips As New List(Of Border)
  260. Private Sub TooltipLoaded(sender As Border, e As EventArgs)
  261. ShowingTooltips.Add(sender)
  262. End Sub
  263. Private Sub TooltipUnloaded(sender As Border, e As RoutedEventArgs)
  264. ShowingTooltips.Remove(sender)
  265. End Sub
  266. End Class