From cc6af272fe9f769ac34b1176217580e8d8319378 Mon Sep 17 00:00:00 2001 From: Dime Date: Sun, 12 Apr 2026 16:23:03 +0800 Subject: [PATCH 1/2] feat: Update UWP Register. --- BedrockLauncher.Core/BedrockCore.cs | 3 +- .../UwpRegister/UwpRegister.cs | 312 ++++++++++-------- 2 files changed, 183 insertions(+), 132 deletions(-) diff --git a/BedrockLauncher.Core/BedrockCore.cs b/BedrockLauncher.Core/BedrockCore.cs index 902294e..e62e9e6 100644 --- a/BedrockLauncher.Core/BedrockCore.cs +++ b/BedrockLauncher.Core/BedrockCore.cs @@ -230,7 +230,8 @@ await ZipExtractor.ExtractWithProgressAsync(options.FileFullPath, options.Instal config.PackagePath = Path.Combine(options.InstallDstFolder, "AppxManifest.xml"); config.CancellationToken = options.CancellationToken.GetValueOrDefault(); config.Timeout = new TimeSpan(0, 3, 0); - config.DeploymentOptions = is_installed ? DeploymentOptions.DevelopmentMode | DeploymentOptions.ForceUpdateFromAnyVersion + config.DeploymentOptions = is_installed + ? DeploymentOptions.DevelopmentMode | DeploymentOptions.ForceUpdateFromAnyVersion : DeploymentOptions.DevelopmentMode; config.ProgressCallback = options.DeployProgress; diff --git a/BedrockLauncher.Core/UwpRegister/UwpRegister.cs b/BedrockLauncher.Core/UwpRegister/UwpRegister.cs index 648abfd..910e2af 100644 --- a/BedrockLauncher.Core/UwpRegister/UwpRegister.cs +++ b/BedrockLauncher.Core/UwpRegister/UwpRegister.cs @@ -1,137 +1,187 @@ +using System; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using Windows.Foundation; using Windows.Management.Deployment; -namespace BedrockLauncher.Core.UwpRegister; - -public class DeploymentOptionsConfig -{ - /// - /// Gets or sets the file system path to the package. - /// - public string PackagePath { get; set; } = string.Empty; - - /// - /// Gets or sets the options used to configure deployment behavior. - /// - public DeploymentOptions DeploymentOptions { get; set; } - - /// - /// Gets or sets the cancellation token that is used to observe cancellation requests for the associated operation. - /// - /// - /// Assign a cancellation token to enable cooperative cancellation of the operation. If no token is - /// provided, the operation cannot be cancelled through this property. - /// - public CancellationToken CancellationToken { get; set; } = default; - - /// - /// Progress callback - /// - public IProgress? ProgressCallback { get; set; } - - /// - /// Gets or sets the maximum duration to wait before the operation times out. - /// - public TimeSpan? Timeout { get; set; } -} - -public class UwpRegister +namespace BedrockLauncher.Core.UwpRegister { - /// - /// Registers an appx package with flexible configuration - /// - /// Deployment configuration options - /// Deployment result containing operation status - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageManager))] - public static async Task RegisterAppxAsync(DeploymentOptionsConfig config) - { - ValidateConfig(config); - var manager = new PackageManager(); - var asyncOperation = manager.RegisterPackageAsync( - new Uri(config.PackagePath), - null, - config.DeploymentOptions | DeploymentOptions.DevelopmentMode); - - return await ExecuteWithTimeout(asyncOperation, config); - } - - /// - /// Adds a framework appx package with flexible configuration - /// - /// Deployment configuration options - /// Deployment result containing operation status - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageManager))] - public static async Task AddAppxAsync(DeploymentOptionsConfig config) - { - ValidateConfig(config); - - var packageManager = new PackageManager(); - var asyncOperation = packageManager.AddPackageAsync( - new Uri(config.PackagePath), - null, - config.DeploymentOptions); - - return await ExecuteWithTimeout(asyncOperation, config); - } - - private static async Task ExecuteWithTimeout( - IAsyncOperationWithProgress asyncOperation, - DeploymentOptionsConfig config) - { - if (config.Timeout.HasValue) - { - using var timeoutCts = new CancellationTokenSource(config.Timeout.Value); - using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( - config.CancellationToken, timeoutCts.Token); - - return await asyncOperation.AsTask(linkedCts.Token, config.ProgressCallback); - } - - return await asyncOperation.AsTask(config.CancellationToken, config.ProgressCallback); - } - public static bool CheckForPackageVersion(string packageName, string version) - { - PackageManager packageManager = new PackageManager(); - - foreach (var package in packageManager.FindPackages()) - { - if (package.Id.Name == packageName) - { - var currentVersion = $"{package.Id.Version.Major}.{package.Id.Version.Minor}.{package.Id.Version.Build}.{package.Id.Version.Revision}"; - - if (currentVersion == version) - { - return true; - } - } - } - - return false; - } - public static bool IsPackageInstalled(string packageName) - { - if (string.IsNullOrWhiteSpace(packageName)) - throw new ArgumentException("Package name cannot be null or empty", nameof(packageName)); - - PackageManager packageManager = new PackageManager(); - - foreach (var package in packageManager.FindPackages()) - { - if (package.Id.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - private static void ValidateConfig(DeploymentOptionsConfig config) - { - if (string.IsNullOrWhiteSpace(config.PackagePath)) - throw new ArgumentException("Package path cannot be null or empty", nameof(config)); - - if (!Uri.TryCreate(config.PackagePath, UriKind.Absolute, out _)) - throw new ArgumentException("Package path must be a valid absolute URI", nameof(config)); - } + public class DeploymentOptionsConfig + { + /// + /// Gets or sets the file system path to the package. + /// + public string PackagePath { get; set; } = string.Empty; + + /// + /// Gets or sets the options used to configure deployment behavior. + /// + public DeploymentOptions DeploymentOptions { get; set; } + + /// + /// Gets or sets the cancellation token that is used to observe cancellation requests for the associated operation. + /// + /// + /// Assign a cancellation token to enable cooperative cancellation of the operation. If no token is + /// provided, the operation cannot be cancelled through this property. + /// + public CancellationToken CancellationToken { get; set; } = default; + + /// + /// Progress callback + /// + public IProgress? ProgressCallback { get; set; } + + /// + /// Gets or sets the maximum duration to wait before the operation times out. + /// + public TimeSpan? Timeout { get; set; } + } + + public class UwpRegister + { + /// + /// Registers an appx package with flexible configuration (no admin required) + /// + /// Deployment configuration options + /// Deployment result containing operation status + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageManager))] + public static async Task RegisterAppxAsync(DeploymentOptionsConfig config) + { + ValidateConfig(config); + + // 处理包路径,确保是文件夹路径 + string packageFolderPath = config.PackagePath; + if (packageFolderPath.StartsWith("file://")) + { + packageFolderPath = new Uri(packageFolderPath).LocalPath; + } + + // 移除签名文件(如果存在) + RemoveSignatureFile(packageFolderPath); + + var manager = new PackageManager(); + var asyncOperation = manager.RegisterPackageAsync( + new Uri(config.PackagePath), + null, + config.DeploymentOptions | DeploymentOptions.DevelopmentMode); + + return await ExecuteWithTimeout(asyncOperation, config); + } + + /// + /// Adds a framework appx package with flexible configuration + /// + /// Deployment configuration options + /// Deployment result containing operation status + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PackageManager))] + public static async Task AddAppxAsync(DeploymentOptionsConfig config) + { + ValidateConfig(config); + + // 处理包路径,确保是文件夹路径 + string packageFolderPath = config.PackagePath; + if (packageFolderPath.StartsWith("file://")) + { + packageFolderPath = new Uri(packageFolderPath).LocalPath; + } + + // 移除签名文件(如果存在) + RemoveSignatureFile(packageFolderPath); + + var packageManager = new PackageManager(); + var asyncOperation = packageManager.AddPackageAsync( + new Uri(config.PackagePath), + null, + config.DeploymentOptions | DeploymentOptions.DevelopmentMode); + + return await ExecuteWithTimeout(asyncOperation, config); + } + + /// + /// Removes the signature file from the package folder + /// + /// Path to the unpacked package folder + private static void RemoveSignatureFile(string packageFolderPath) + { + try + { + string signaturePath = Path.Combine(packageFolderPath, "AppxSignature.p7x"); + if (File.Exists(signaturePath)) + { + File.Delete(signaturePath); + } + } + catch (Exception ex) + { + // 记录错误但继续执行 + Console.WriteLine($"Error removing signature file: {ex.Message}"); + } + } + + private static async Task ExecuteWithTimeout( + IAsyncOperationWithProgress asyncOperation, + DeploymentOptionsConfig config) + { + if (config.Timeout.HasValue) + { + using var timeoutCts = new CancellationTokenSource(config.Timeout.Value); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( + config.CancellationToken, timeoutCts.Token); + + return await asyncOperation.AsTask(linkedCts.Token, config.ProgressCallback); + } + + return await asyncOperation.AsTask(config.CancellationToken, config.ProgressCallback); + } + + public static bool CheckForPackageVersion(string packageName, string version) + { + PackageManager packageManager = new PackageManager(); + + foreach (var package in packageManager.FindPackages()) + { + if (package.Id.Name == packageName) + { + var currentVersion = $"{package.Id.Version.Major}.{package.Id.Version.Minor}.{package.Id.Version.Build}.{package.Id.Version.Revision}"; + + if (currentVersion == version) + { + return true; + } + } + } + + return false; + } + + public static bool IsPackageInstalled(string packageName) + { + if (string.IsNullOrWhiteSpace(packageName)) + throw new ArgumentException("Package name cannot be null or empty", nameof(packageName)); + + PackageManager packageManager = new PackageManager(); + + foreach (var package in packageManager.FindPackages()) + { + if (package.Id.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static void ValidateConfig(DeploymentOptionsConfig config) + { + if (string.IsNullOrWhiteSpace(config.PackagePath)) + throw new ArgumentException("Package path cannot be null or empty", nameof(config)); + + if (!Uri.TryCreate(config.PackagePath, UriKind.Absolute, out _)) + throw new ArgumentException("Package path must be a valid absolute URI", nameof(config)); + } + } } \ No newline at end of file From 7410e6912ca4698b5a11c205265b4e53e39a6859 Mon Sep 17 00:00:00 2001 From: Dime Date: Sun, 12 Apr 2026 16:25:15 +0800 Subject: [PATCH 2/2] chore: update version. --- BedrockLauncher.Core/BedrockLauncher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockLauncher.Core/BedrockLauncher.Core.csproj b/BedrockLauncher.Core/BedrockLauncher.Core.csproj index c188aa9..4a7bcc7 100644 --- a/BedrockLauncher.Core/BedrockLauncher.Core.csproj +++ b/BedrockLauncher.Core/BedrockLauncher.Core.csproj @@ -20,7 +20,7 @@ true - 2.0.4.1-dev + 2.0.4.2