Application.xaml.vb 17 KB

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