diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs index 18286d7d..0e2a2cbc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using CommunityToolkit.Mvvm.Messaging; -using Snap.Hutao.Core.IO.Http.DynamicProxy; +using Snap.Hutao.Core.IO.Http.Proxy; using Snap.Hutao.Core.Logging; using Snap.Hutao.Service; using System.Globalization; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs index 565c2698..87c37f74 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs @@ -80,4 +80,4 @@ internal sealed unsafe class LoopbackManager : ObservableObject sids.Add(sidAndAttributes); IsLoopbackEnabled = NetworkIsolationSetAppContainerConfig(CollectionsMarshal.AsSpan(sids)) is WIN32_ERROR.ERROR_SUCCESS; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/DynamicHttpProxy.cs similarity index 98% rename from src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs rename to src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/DynamicHttpProxy.cs index 73a854c9..bd7dc3f2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/DynamicHttpProxy.cs @@ -7,7 +7,7 @@ using Snap.Hutao.Win32.Registry; using System.Net; using System.Reflection; -namespace Snap.Hutao.Core.IO.Http.DynamicProxy; +namespace Snap.Hutao.Core.IO.Http.Proxy; [Injection(InjectAs.Singleton)] internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, IDisposable diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs index 9992994b..a8bb4d9b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs @@ -40,7 +40,7 @@ internal static class AppInstanceExtension SetEvent(redirectEventHandle); }); - CoWaitForMultipleObjects((uint)CWMO_FLAGS.CWMO_DEFAULT, INFINITE, [redirectEventHandle], out uint _); + CoWaitForMultipleObjects(CWMO_FLAGS.CWMO_DEFAULT, INFINITE, [redirectEventHandle], out uint _); } finally { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs index 9dd3079a..6f31517c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs @@ -14,113 +14,99 @@ namespace Snap.Hutao.Core; [Injection(InjectAs.Singleton)] internal sealed class RuntimeOptions { - private readonly bool isWebView2Supported; - private readonly string webView2Version = SH.CoreWebView2HelperVersionUndetected; + private readonly Lazy<(Version Version, string UserAgent)> lazyVersionAndUserAgent = new(() => + { + Version version = Package.Current.Id.Version.ToVersion(); + return (version, $"Snap Hutao/{version}"); + }); - private bool? isElevated; + private readonly Lazy lazyDataFolder = new(() => + { + string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty); + + if (!string.IsNullOrEmpty(preferredPath)) + { + Directory.CreateDirectory(preferredPath); + return preferredPath; + } + + // Fallback to MyDocuments + string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + +#if RELEASE + // 将测试版与正式版的文件目录分离 + string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao"; +#else + // 使得迁移能正常生成 + string folderName = "Hutao"; +#endif + string path = Path.GetFullPath(Path.Combine(myDocuments, folderName)); + Directory.CreateDirectory(path); + return path; + }); + + private readonly Lazy lazyDeviceId = new(() => + { + string userName = Environment.UserName; + object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName); + return Convert.ToMd5HexString($"{userName}{machineGuid}"); + }); + + private readonly Lazy<(string Version, bool Supported)> lazyWebViewEnvironment = new(() => + { + try + { + string version = CoreWebView2Environment.GetAvailableBrowserVersionString(); + return (version, true); + } + catch (FileNotFoundException) + { + return (SH.CoreWebView2HelperVersionUndetected, false); + } + }); + + private readonly Lazy lazyElevated = new(() => + { + if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false)) + { + return true; + } + + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) + { + WindowsPrincipal principal = new(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + }); + + private readonly Lazy lazyLocalCache = new(() => ApplicationData.Current.LocalCacheFolder.Path); + private readonly Lazy lazyInstalledLocation = new(() => Package.Current.InstalledLocation.Path); + private readonly Lazy lazyFamilyName = new(() => Package.Current.Id.FamilyName); public RuntimeOptions(ILogger logger) { AppLaunchTime = DateTimeOffset.UtcNow; - - DataFolder = GetDataFolderPath(); - LocalCache = ApplicationData.Current.LocalCacheFolder.Path; - InstalledLocation = Package.Current.InstalledLocation.Path; - FamilyName = Package.Current.Id.FamilyName; - - Version = Package.Current.Id.Version.ToVersion(); - UserAgent = $"Snap Hutao/{Version}"; - - DeviceId = GetUniqueUserId(); - DetectWebView2Environment(logger, out webView2Version, out isWebView2Supported); - - static string GetDataFolderPath() - { - string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty); - - if (!string.IsNullOrEmpty(preferredPath)) - { - Directory.CreateDirectory(preferredPath); - return preferredPath; - } - - // Fallback to MyDocuments - string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - -#if RELEASE - // 将测试版与正式版的文件目录分离 - string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao"; -#else - // 使得迁移能正常生成 - string folderName = "Hutao"; -#endif - string path = Path.GetFullPath(Path.Combine(myDocuments, folderName)); - Directory.CreateDirectory(path); - return path; - } - - static string GetUniqueUserId() - { - string userName = Environment.UserName; - object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName); - return Convert.ToMd5HexString($"{userName}{machineGuid}"); - } - - static void DetectWebView2Environment(ILogger logger, out string webView2Version, out bool isWebView2Supported) - { - try - { - webView2Version = CoreWebView2Environment.GetAvailableBrowserVersionString(); - isWebView2Supported = true; - } - catch (FileNotFoundException ex) - { - webView2Version = SH.CoreWebView2HelperVersionUndetected; - isWebView2Supported = false; - logger.LogError(ex, "WebView2 Runtime not installed."); - } - } } - public Version Version { get; } + public Version Version { get => lazyVersionAndUserAgent.Value.Version; } - public string UserAgent { get; } + public string UserAgent { get => lazyVersionAndUserAgent.Value.UserAgent; } - public string InstalledLocation { get; } + public string InstalledLocation { get => lazyInstalledLocation.Value; } - public string DataFolder { get; } + public string DataFolder { get => lazyDataFolder.Value; } - public string LocalCache { get; } + public string LocalCache { get => lazyLocalCache.Value; } - public string FamilyName { get; } + public string FamilyName { get => lazyFamilyName.Value; } - public string DeviceId { get; } + public string DeviceId { get => lazyDeviceId.Value; } - public string WebView2Version { get => webView2Version; } + public string WebView2Version { get => lazyWebViewEnvironment.Value.Version; } - public bool IsWebView2Supported { get => isWebView2Supported; } + public bool IsWebView2Supported { get => lazyWebViewEnvironment.Value.Supported; } - public bool IsElevated - { - get - { - return isElevated ??= GetElevated(); - - static bool GetElevated() - { - if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false)) - { - return true; - } - - using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) - { - WindowsPrincipal principal = new(identity); - return principal.IsInRole(WindowsBuiltInRole.Administrator); - } - } - } - } + public bool IsElevated { get => lazyElevated.Value; } public DateTimeOffset AppLaunchTime { get; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs index ade66543..110500f0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Program.cs +++ b/src/Snap.Hutao/Snap.Hutao/Program.cs @@ -19,6 +19,22 @@ public static partial class Program { private static readonly ApplicationInitializationCallback AppInitializationCallback = InitializeApp; + [ModuleInitializer] + internal static void InitializeModule() + { + // Set base directory env var for PublishSingleFile support (referenced by SxS redirection) + Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", AppContext.BaseDirectory); + + // No error handling needed as the target function does nothing (just {return S_OK}). + // It's the act of calling the function causing the DllImport to load the DLL that + // matters. This provides the moral equivalent of a native DLL's Import Address + // Table (IAT) have an entry that's resolved when this module is loaded. + _ = WindowsAppRuntimeEnsureIsLoaded(); + } + + [LibraryImport("Microsoft.WindowsAppRuntime.dll", EntryPoint = "WindowsAppRuntime_EnsureIsLoaded")] + private static partial int WindowsAppRuntimeEnsureIsLoaded(); + [LibraryImport("Microsoft.ui.xaml.dll")] private static partial void XamlCheckProcessRequirements(); diff --git a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json index a5fb2a14..36a6b395 100644 --- a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json +++ b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Snap.Hutao": { "commandName": "MsixPackage", - "nativeDebugging": false, + "nativeDebugging": true, "doNotLaunchApp": false, "allowLocalNetworkLoopbackProperty": true }, diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index f68c9f7b..953e7b6c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -43,6 +43,7 @@ --> true true + false diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml index 943f8172..32191c30 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml @@ -65,7 +65,7 @@ Header="{shcm:ResourceString Name=ViewPageSettingDeviceIdHeader}" IsClickEnabled="True"/> - + handles, out uint dwindex) + public static unsafe HRESULT CoWaitForMultipleObjects(CWMO_FLAGS dwFlags, uint dwTimeout, ReadOnlySpan handles, out uint dwindex) { fixed (HANDLE* pHandles = handles) { fixed (uint* lpdwindex = &dwindex) { - return CoWaitForMultipleObjects(dwFlags, dwTimeout, (uint)handles.Length, pHandles, lpdwindex); + return CoWaitForMultipleObjects((uint)dwFlags, dwTimeout, (uint)handles.Length, pHandles, lpdwindex); } } }