From b0626a0d600a23778a8167d241a08d548bc7f35c Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Thu, 7 Sep 2023 14:41:16 +0800 Subject: [PATCH] Add ability to switch powershell instance --- .../Snap.Hutao/Core/LifeCycle/Activation.cs | 1 - .../Model/Entity/SettingEntry.Constant.cs | 7 ++- src/Snap.Hutao/Snap.Hutao/NativeMethods.txt | 4 +- .../Resource/Localization/SH.Designer.cs | 18 ++++++++ .../Snap.Hutao/Resource/Localization/SH.resx | 6 +++ .../Snap.Hutao/Service/AppOptions.cs | 34 +++++++++++++++ .../Snap.Hutao/Service/Game/GameService.cs | 7 ++- .../Service/Game/RegistryInterop.cs | 43 ++++--------------- .../Snap.Hutao/View/Page/SettingPage.xaml | 8 ++++ .../AvatarProperty/AvatarProperty.cs | 1 + .../ViewModel/Home/AnnouncementViewModel.cs | 2 +- .../Snap.Hutao/ViewModel/SettingViewModel.cs | 25 ++++++----- .../Snap.Hutao/Web/HutaoEndpoints.cs | 2 +- 13 files changed, 106 insertions(+), 52 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs index 13442cb5..2e601ed8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs @@ -52,7 +52,6 @@ internal sealed partial class Activation : IActivation private readonly IServiceProvider serviceProvider; private readonly ICurrentWindowReference currentWindowReference; private readonly ITaskContext taskContext; - //private readonly WeakReference mainWindowReference = new(default!); private readonly SemaphoreSlim activateSemaphore = new(1); /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs index 73fef025..e0884f67 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs @@ -1,8 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using System.Runtime.Versioning; - namespace Snap.Hutao.Model.Entity; /// @@ -15,6 +13,11 @@ internal sealed partial class SettingEntry /// public const string GamePath = "GamePath"; + /// + /// PowerShell 路径 + /// + public const string PowerShellPath = "PowerShellPath"; + /// /// 空的历史记录卡池是否可见 /// diff --git a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt index 7c20f36a..75d7a42c 100644 --- a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt +++ b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt @@ -60,4 +60,6 @@ LPTHREAD_START_ROUTINE MINMAXINFO // System.Com -CWMO_FLAGS \ No newline at end of file +CWMO_FLAGS + +//IShellLinkW \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs index 71daae3a..f337db17 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs @@ -267,6 +267,15 @@ namespace Snap.Hutao.Resource.Localization { } } + /// + /// 查找类似 选择 PowerShell 的本地化字符串。 + /// + internal static string FilePickerPowerShellCommit { + get { + return ResourceManager.GetString("FilePickerPowerShellCommit", resourceCulture); + } + } + /// /// 查找类似 欢迎使用胡桃 的本地化字符串。 /// @@ -5415,6 +5424,15 @@ namespace Snap.Hutao.Resource.Localization { } } + /// + /// 查找类似 PowerShell 路径 的本地化字符串。 + /// + internal static string ViewPageSettingSetPowerShellPathHeader { + get { + return ResourceManager.GetString("ViewPageSettingSetPowerShellPathHeader", resourceCulture); + } + } + /// /// 查找类似 赞助我们 的本地化字符串。 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 968066b5..37269eaa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -186,6 +186,9 @@ 导入 + + 选择 PowerShell + 欢迎使用胡桃 @@ -1958,6 +1961,9 @@ 设置游戏路径时,请选择游戏本体(YuanShen.exe 或 GenshinImpact.exe)而不是启动器(launcher.exe) + + PowerShell 路径 + 赞助我们 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs index 7efb137a..068d1f53 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs @@ -1,11 +1,15 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Extensions.Primitives; +using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.Windowing; using Snap.Hutao.Model; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Abstraction; +using Snap.Hutao.Service.Game; using System.Globalization; +using System.IO; namespace Snap.Hutao.Service; @@ -34,6 +38,7 @@ internal sealed partial class AppOptions : DbStoreOptions }; private string? gamePath; + private string? powerShellPath; private bool? isEmptyHistoryWishVisible; private BackdropType? backdropType; private CultureInfo? currentCulture; @@ -49,6 +54,15 @@ internal sealed partial class AppOptions : DbStoreOptions set => SetOption(ref gamePath, SettingEntry.GamePath, value); } + /// + /// PowerShell 路径 + /// + public string PowerShellPath + { + get => GetOption(ref powerShellPath, SettingEntry.PowerShellPath, GetPowerShellLocationOrEmpty()); + set => SetOption(ref powerShellPath, SettingEntry.PowerShellPath, value); + } + /// /// 游戏路径 /// @@ -111,4 +125,24 @@ internal sealed partial class AppOptions : DbStoreOptions { return new(info.NativeName, info.Name); } + + private static string GetPowerShellLocationOrEmpty() + { + string? paths = Environment.GetEnvironmentVariable("Path"); + if (!string.IsNullOrEmpty(paths)) + { + foreach (StringSegment path in new StringTokenizer(paths, ';'.ToArray())) + { + if (path is { HasValue: true, Length: > 0 }) + { + if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase)) + { + return Path.Combine(path.Value, "powershell.exe"); + } + } + } + } + + return string.Empty; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs index 647588b3..cbff5252 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs @@ -346,7 +346,12 @@ internal sealed partial class GameService : IGameService /// public bool SetGameAccount(GameAccount account) { - return RegistryInterop.Set(account); + if (string.IsNullOrEmpty(appOptions.PowerShellPath)) + { + ThrowHelper.RuntimeEnvironment(SH.ServiceGameRegisteryInteropPowershellNotFound, default!); + } + + return RegistryInterop.Set(account, appOptions.PowerShellPath); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs index f4325979..fa7c7095 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.Extensions.Primitives; using Microsoft.Win32; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; @@ -16,15 +15,9 @@ namespace Snap.Hutao.Service.Game; /// internal static class RegistryInterop { - private const string GenshinKey = @"HKEY_CURRENT_USER\Software\miHoYo\原神"; - private const string SdkKey = "MIHOYOSDK_ADL_PROD_CN_h3123967166"; - - private static string? psExecutablePath; - - private static string PsExecutablePath - { - get => psExecutablePath ??= GetPowerShellLocation(); - } + private const string GenshinPath = @"Software\miHoYo\原神"; + private const string GenshinKey = $@"HKEY_CURRENT_USER\{GenshinPath}"; + private const string SdkChineseKey = "MIHOYOSDK_ADL_PROD_CN_h3123967166"; /// /// 设置键值 @@ -32,8 +25,9 @@ internal static class RegistryInterop /// https://learn.microsoft.com/zh-cn/windows/win32/fileio/maximum-file-path-limitation /// /// 账户 + /// PowerShell 路径 /// 账号是否设置 - public static bool Set(GameAccount? account) + public static bool Set(GameAccount? account, string powerShellPath) { if (account is not null) { @@ -41,15 +35,15 @@ internal static class RegistryInterop string path = $"HKCU:{GenshinKey[@"HKEY_CURRENT_USER\".Length..]}"; string command = $""" $value = [Convert]::FromBase64String('{base64}'); - Set-ItemProperty -Path '{path}' -Name '{SdkKey}' -Value $value -Force; + Set-ItemProperty -Path '{path}' -Name '{SdkChineseKey}' -Value $value -Force; """; ProcessStartInfo startInfo = new() { Arguments = command, - WorkingDirectory = Path.GetDirectoryName(PsExecutablePath), + WorkingDirectory = Path.GetDirectoryName(powerShellPath), CreateNoWindow = true, - FileName = PsExecutablePath, + FileName = powerShellPath, }; try @@ -76,7 +70,7 @@ internal static class RegistryInterop /// 当前注册表中的信息 public static string? Get() { - object? sdk = Registry.GetValue(GenshinKey, SdkKey, Array.Empty()); + object? sdk = Registry.GetValue(GenshinKey, SdkChineseKey, Array.Empty()); if (sdk is byte[] bytes) { @@ -85,23 +79,4 @@ internal static class RegistryInterop return null; } - - private static string GetPowerShellLocation() - { - string? paths = Environment.GetEnvironmentVariable("Path"); - ArgumentException.ThrowIfNullOrEmpty(paths); - - foreach (StringSegment path in new StringTokenizer(paths, ';'.ToArray())) - { - if (path is { HasValue: true, Length: > 0 }) - { - if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase)) - { - return Path.Combine(path.Value, "powershell.exe"); - } - } - } - - throw ThrowHelper.RuntimeEnvironment(SH.ServiceGameRegisteryInteropPowershellNotFound, default!); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml index 30902c3f..fd19b8b6 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml @@ -126,6 +126,14 @@ Header="{shcm:ResourceString Name=ViewPageSettingSetGamePathHeader}" HeaderIcon="{shcm:FontIcon Glyph=}" IsClickEnabled="True"/> + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs index c8082fd1..ecc41b5c 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs @@ -64,7 +64,7 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel } } - private async Task InitializeHutaoAnnouncementAsync() + private async ValueTask InitializeHutaoAnnouncementAsync() { try { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs index 1bfaf53f..1b3c6125 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs @@ -23,6 +23,7 @@ using Snap.Hutao.ViewModel.Guide; using System.Globalization; using System.IO; using System.Runtime.InteropServices; +using Windows.Storage.Pickers; using Windows.System; namespace Snap.Hutao.ViewModel; @@ -117,6 +118,19 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel } } + [Command("SetPowerShellPathCommand")] + private async Task SetPowerShellPathAsync() + { + FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.DocumentsLibrary, SH.FilePickerPowerShellCommit, ".exe"); + (bool isOk, ValueFile file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false); + + if (isOk && Path.GetFileNameWithoutExtension(file).Equals("POWERSHELL", StringComparison.OrdinalIgnoreCase)) + { + await taskContext.SwitchToMainThreadAsync(); + Options.PowerShellPath = file; + } + } + [Command("DeleteGameWebCacheCommand")] private void DeleteGameWebCache() { @@ -148,17 +162,6 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel } } - [Command("ShowSignInWebViewDialogCommand")] - private async Task ShowSignInWebViewDialogAsync() - { - // ContentDialog must be created by main thread. - await taskContext.SwitchToMainThreadAsync(); - - // TODO remove this. - // SignInWebViewDialog dialog = serviceProvider.CreateInstance(); - // await dialog.ShowAsync(); - } - [Command("UpdateCheckCommand")] private async Task CheckUpdateAsync() { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index 33025a55..e1f9e841 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -24,7 +24,7 @@ internal static class HutaoEndpoints return $"{HomaSnapGenshinApi}/Announcement/List?locale={locale}"; } - public static string AnnouncementUpload= $"{HomaSnapGenshinApi}/Service/Announcement/Upload"; + public const string AnnouncementUpload = $"{HomaSnapGenshinApi}/Service/Announcement/Upload"; public static string GachaLogCompensation(int days) {