Add ability to switch powershell instance

This commit is contained in:
Lightczx
2023-09-07 14:41:16 +08:00
parent fd16521c94
commit b0626a0d60
13 changed files with 106 additions and 52 deletions

View File

@@ -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<MainWindow> mainWindowReference = new(default!);
private readonly SemaphoreSlim activateSemaphore = new(1);
/// <inheritdoc/>

View File

@@ -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;
/// <summary>
@@ -15,6 +13,11 @@ internal sealed partial class SettingEntry
/// </summary>
public const string GamePath = "GamePath";
/// <summary>
/// PowerShell 路径
/// </summary>
public const string PowerShellPath = "PowerShellPath";
/// <summary>
/// 空的历史记录卡池是否可见
/// </summary>

View File

@@ -60,4 +60,6 @@ LPTHREAD_START_ROUTINE
MINMAXINFO
// System.Com
CWMO_FLAGS
CWMO_FLAGS
//IShellLinkW

View File

@@ -267,6 +267,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 选择 PowerShell 的本地化字符串。
/// </summary>
internal static string FilePickerPowerShellCommit {
get {
return ResourceManager.GetString("FilePickerPowerShellCommit", resourceCulture);
}
}
/// <summary>
/// 查找类似 欢迎使用胡桃 的本地化字符串。
/// </summary>
@@ -5415,6 +5424,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 PowerShell 路径 的本地化字符串。
/// </summary>
internal static string ViewPageSettingSetPowerShellPathHeader {
get {
return ResourceManager.GetString("ViewPageSettingSetPowerShellPathHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 赞助我们 的本地化字符串。
/// </summary>

View File

@@ -186,6 +186,9 @@
<data name="FilePickerImportCommit" xml:space="preserve">
<value>导入</value>
</data>
<data name="FilePickerPowerShellCommit" xml:space="preserve">
<value>选择 PowerShell</value>
</data>
<data name="GuideWindowTitle" xml:space="preserve">
<value>欢迎使用胡桃</value>
</data>
@@ -1958,6 +1961,9 @@
<data name="ViewPageSettingSetGamePathHint" xml:space="preserve">
<value>设置游戏路径时请选择游戏本体YuanShen.exe 或 GenshinImpact.exe而不是启动器launcher.exe</value>
</data>
<data name="ViewPageSettingSetPowerShellPathHeader" xml:space="preserve">
<value>PowerShell 路径</value>
</data>
<data name="ViewPageSettingSponsorNavigate" xml:space="preserve">
<value>赞助我们</value>
</data>

View File

@@ -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);
}
/// <summary>
/// PowerShell 路径
/// </summary>
public string PowerShellPath
{
get => GetOption(ref powerShellPath, SettingEntry.PowerShellPath, GetPowerShellLocationOrEmpty());
set => SetOption(ref powerShellPath, SettingEntry.PowerShellPath, value);
}
/// <summary>
/// 游戏路径
/// </summary>
@@ -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;
}
}

View File

@@ -346,7 +346,12 @@ internal sealed partial class GameService : IGameService
/// <inheritdoc/>
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);
}
/// <inheritdoc/>

View File

@@ -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;
/// </summary>
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";
/// <summary>
/// 设置键值
@@ -32,8 +25,9 @@ internal static class RegistryInterop
/// https://learn.microsoft.com/zh-cn/windows/win32/fileio/maximum-file-path-limitation
/// </summary>
/// <param name="account">账户</param>
/// <param name="powerShellPath">PowerShell 路径</param>
/// <returns>账号是否设置</returns>
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
/// <returns>当前注册表中的信息</returns>
public static string? Get()
{
object? sdk = Registry.GetValue(GenshinKey, SdkKey, Array.Empty<byte>());
object? sdk = Registry.GetValue(GenshinKey, SdkChineseKey, Array.Empty<byte>());
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!);
}
}

View File

@@ -126,6 +126,14 @@
Header="{shcm:ResourceString Name=ViewPageSettingSetGamePathHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE7FC;}"
IsClickEnabled="True"/>
<cwc:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
Command="{Binding SetPowerShellPathCommand}"
Description="{Binding Options.PowerShellPath}"
Header="{shcm:ResourceString Name=ViewPageSettingSetPowerShellPathHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE756;}"
IsClickEnabled="True"/>
<cwc:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingDeleteCacheAction}"

View File

@@ -37,6 +37,7 @@ internal sealed class AvatarProperty : ObservableObject, INameIcon, IAlternating
[FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT] = Web.HutaoEndpoints.StaticFile("Property", "UI_Icon_PhysicalAttackUp.png").ToUri(),
[FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO] = Web.HutaoEndpoints.StaticFile("Property", "UI_Icon_ShieldCostMinus.png").ToUri(),
}.ToImmutableDictionary();
private Brush? background;
/// <summary>

View File

@@ -64,7 +64,7 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
}
}
private async Task InitializeHutaoAnnouncementAsync()
private async ValueTask InitializeHutaoAnnouncementAsync()
{
try
{

View File

@@ -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<SignInWebViewDialog>();
// await dialog.ShowAsync();
}
[Command("UpdateCheckCommand")]
private async Task CheckUpdateAsync()
{

View File

@@ -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)
{