From 1211ca4ca8c50d16618d3842ad82ced1ed3d9b4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 10:50:07 +0000 Subject: [PATCH 1/5] Initial plan From 1f1084e03fa63fd4b7ff7bb6c644786c8a9e7b47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:02:11 +0000 Subject: [PATCH 2/5] Add GeneralUpdate.Extension documentation in Chinese and English Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --- website/docs/doc/GeneralUpdate.Extension.md | 523 ++++++++++++++++++ .../docs/doc/GeneralUpdate.Extension.md | 523 ++++++++++++++++++ .../current/doc/GeneralUpdate.Extension.md | 523 ++++++++++++++++++ 3 files changed, 1569 insertions(+) create mode 100644 website/docs/doc/GeneralUpdate.Extension.md create mode 100644 website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md create mode 100644 website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md diff --git a/website/docs/doc/GeneralUpdate.Extension.md b/website/docs/doc/GeneralUpdate.Extension.md new file mode 100644 index 0000000..945d69d --- /dev/null +++ b/website/docs/doc/GeneralUpdate.Extension.md @@ -0,0 +1,523 @@ +--- +sidebar_position: 12 +--- + +### 定义 + +命名空间:GeneralUpdate.Extension + +程序集:GeneralUpdate.ClientCore.dll、GeneralUpdate.Core.dll + + + +GeneralUpdate.Extension 提供了一个全面的扩展框架,允许开发人员自定义和扩展更新过程。它使您能够注入自定义逻辑、处理事件,并根据特定应用程序需求定制更新行为。对于需要对更新工作流进行细粒度控制的开发人员来说,此组件至关重要。 + +```c# +// 扩展方法通过 GeneralClientBootstrap 和 GeneralUpdateBootstrap 提供 +public class GeneralClientBootstrap : AbstractBootstrap +``` + + + +### 示例 + +Extension 框架提供多种扩展功能的方法 [[查看示例]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 + +```c# +var configinfo = new Configinfo +{ + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + AppName = "YourApp.exe", + ClientVersion = "1.0.0.0", + // ... 其他配置 +}; + +await new GeneralClientBootstrap() + // 添加自定义事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(CheckEnvironment) + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 核心扩展能力 + +GeneralUpdate.Extension 提供以下扩展功能。 + +#### 事件监听器 + +| 方法 | 说明 | +| -------------------------------------- | ------------------------------------------------------------ | +| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | +| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | +| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | +| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | +| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | + +#### 自定义操作 + +| 方法 | 说明 | +| --------------------- | ------------------------------------------------------------ | +| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task,返回 false 将取消更新。 | +| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | +| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | + +#### 配置选项 + +| 方法 | 说明 | +| ------------- | ------------------------------------------------------------ | +| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | +| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | +| GetOption() | 检索当前更新配置设置以供检查或修改。 | +| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | + + + +### 事件参数 + +#### MultiDownloadStatisticsEventArgs + +提供详细的下载进度信息。 + +| 属性 | 类型 | 说明 | +| ------------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 当前正在下载的版本 | +| Speed | string | 下载速度(例如 "2.5 MB/s") | +| Remaining | string | 预计剩余时间(例如 "00:02:30") | +| BytesReceived | long | 到目前为止已下载的字节数 | +| TotalBytesToReceive| long | 下载的总大小(字节) | +| ProgressPercentage | double | 下载进度百分比(0-100) | + +#### MultiDownloadCompletedEventArgs + +指示单个更新包的完成状态。 + +| 属性 | 类型 | 说明 | +| ------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 已完成下载的版本信息 | +| IsComplated | bool | 下载是否成功完成 | + +#### MultiAllDownloadCompletedEventArgs + +所有下载任务的摘要。 + +| 属性 | 类型 | 说明 | +| ---------------------- | --------------- | ------------------------------------------ | +| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| +| FailedVersions | List| 下载失败的版本列表 | + +#### MultiDownloadErrorEventArgs + +下载失败的错误信息。 + +| 属性 | 类型 | 说明 | +| --------- | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 遇到错误的版本 | +| Exception | Exception | 下载期间发生的异常 | + +#### ExceptionEventArgs + +全局异常信息。 + +| 属性 | 类型 | 说明 | +| --------- | --------- | ---------------------------------------------------- | +| Exception | Exception | 更新过程中发生的异常 | + + + +### 自定义操作示例 + +以下是使用自定义操作的完整示例: + +```c# +private async Task CheckEnvironment() +{ + try + { + // 检查是否安装了所需的依赖项 + if (!IsDependencyInstalled("SomeLibrary")) + { + Console.WriteLine("缺少必需的依赖项!"); + return false; // 取消更新 + } + + // 验证磁盘空间 + var requiredSpace = 500 * 1024 * 1024; // 500 MB + if (GetAvailableDiskSpace() < requiredSpace) + { + Console.WriteLine("磁盘空间不足!"); + return false; // 取消更新 + } + + // 创建关键文件的备份 + await BackupUserData(); + + return true; // 继续更新 + } + catch (Exception ex) + { + Console.WriteLine($"环境检查失败:{ex.Message}"); + return false; + } +} + +// 注册自定义操作 +await new GeneralClientBootstrap() + .AddCustomOption(CheckEnvironment) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 跳过选项示例 + +允许用户跳过非强制更新: + +```c# +private async Task ShowSkipDialog() +{ + // 向用户显示自定义对话框 + var result = await ShowUpdateDialog( + "有新的更新可用!", + "您想现在更新吗?", + new[] { "立即更新", "跳过" } + ); + + return result == "立即更新"; +} + +// 注册跳过选项处理程序 +await new GeneralClientBootstrap() + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 黑名单配置 + +从更新中排除特定文件或格式: + +```c# +var configinfo = new Configinfo +{ + // ... 其他配置 + BlackFiles = new List + { + "userconfig.json", + "license.dat", + "custom.db" + }, + BlackFormats = new List + { + ".log", + ".temp", + ".cache" + }, + BlackDirectories = new List + { + "UserData", + "Plugins\\Custom" + } +}; + +// 或使用 SetBlacklist 方法 +await new GeneralClientBootstrap() + .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 完整扩展示例 + +以下是使用多个扩展功能的综合示例: + +```c# +using GeneralUpdate.ClientCore; +using GeneralUpdate.Common.Shared.Object; + +public class UpdateManager +{ + public async Task StartUpdateWithExtensions() + { + var configinfo = new Configinfo + { + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", + AppName = "MyApp.exe", + MainAppName = "MyApp.exe", + ClientVersion = "1.0.0.0", + ProductId = "your-product-id", + AppSecretKey = "your-secret-key", + InstallPath = AppDomain.CurrentDomain.BaseDirectory, + BlackFiles = new List { "userdata.db" } + }; + + try + { + await new GeneralClientBootstrap() + // 注册事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) + .AddListenerMultiDownloadError(OnDownloadError) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(PerformPreUpdateChecks) + .SetCustomSkipOption(AskUserForUpdatePermission) + // 配置选项 + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 120) + .Option(UpdateOption.Patch, true) + .Option(UpdateOption.BackUp, true) + // 启动更新 + .LaunchAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"更新失败:{ex.Message}"); + } + } + + private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); + // 在此更新 UI 进度条 + } + + private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + var version = e.Version as VersionInfo; + if (e.IsComplated) + { + Console.WriteLine($"版本 {version.Version} 下载成功!"); + } + else + { + Console.WriteLine($"版本 {version.Version} 下载失败!"); + } + } + + private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + if (e.IsAllDownloadCompleted) + { + Console.WriteLine("所有更新下载成功!"); + } + else + { + Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); + } + } + + private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); + } + + private void OnException(object sender, ExceptionEventArgs e) + { + Console.WriteLine($"更新异常:{e.Exception.Message}"); + // 将异常记录到文件或监控系统 + } + + private async Task PerformPreUpdateChecks() + { + // 检查系统要求 + Console.WriteLine("正在执行更新前检查..."); + + // 验证网络连接 + if (!await CheckNetworkConnection()) + { + Console.WriteLine("没有可用的网络连接!"); + return false; + } + + // 检查磁盘空间 + if (!HasSufficientDiskSpace(500 * 1024 * 1024)) + { + Console.WriteLine("磁盘空间不足!"); + return false; + } + + Console.WriteLine("更新前检查通过!"); + return true; + } + + private async Task AskUserForUpdatePermission() + { + Console.WriteLine("有新更新可用。立即更新?(y/n)"); + var response = Console.ReadLine(); + return response?.ToLower() == "y"; + } + + private async Task CheckNetworkConnection() + { + // 实现网络检查 + return true; + } + + private bool HasSufficientDiskSpace(long requiredBytes) + { + // 实现磁盘空间检查 + return true; + } +} +``` + + + +### 最佳实践 + +1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 + +2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 + +3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 + +4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 + +5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 + +6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 + +7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 + +8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 + + + +### 常见用例 + +#### 用例 1:环境验证 + +更新前,验证是否满足所有先决条件: + +```c# +private async Task ValidateEnvironment() +{ + // 检查 .NET 运行时版本 + if (!IsRuntimeVersionSupported()) + return false; + + // 验证所需服务是否正在运行 + if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) + return false; + + // 检查文件权限 + if (!HasWritePermission(installPath)) + return false; + + return true; +} +``` + +#### 用例 2:自定义更新通知 + +在更新过程中向用户显示自定义通知: + +```c# +private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +{ + // 显示系统托盘通知 + ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); + + // 更新应用程序标题 + UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); +} +``` + +#### 用例 3:条件更新 + +根据特定条件应用更新: + +```c# +private async Task ShouldApplyUpdate() +{ + // 检查是否在营业时间内 + var now = DateTime.Now; + if (now.Hour >= 9 && now.Hour <= 17) + { + // 在营业时间推迟更新 + return false; + } + + // 检查是否有关键操作正在运行 + if (IsCriticalOperationInProgress()) + { + return false; + } + + return true; +} +``` + + + +### 故障排除 + +#### 问题:自定义操作无限期地阻止更新 + +**解决方案**:确保您的自定义操作具有超时和适当的错误处理: + +```c# +private async Task CustomOperationWithTimeout() +{ + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + try + { + await YourAsyncOperation(cts.Token); + return true; + } + catch (OperationCanceledException) + { + Console.WriteLine("操作超时"); + return false; + } +} +``` + +#### 问题:事件未触发 + +**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: + +```c# +// 正确的顺序 +await new GeneralClientBootstrap() + .AddListenerException(OnException) // 先注册 + .SetConfig(configinfo) + .LaunchAsync(); // 最后启动 +``` + +#### 问题:跳过选项不起作用 + +**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 + + + +### 适用于 + +| 产品 | 版本 | +| -------------- | ---------------- | +| .NET | 5, 6, 7, 8, 9, 10| +| .NET Framework | 4.6.1 | +| .NET Standard | 2.0 | +| .NET Core | 2.0 | + +### 另请参阅 + +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 +- [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 +- [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门 diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md new file mode 100644 index 0000000..28f9976 --- /dev/null +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md @@ -0,0 +1,523 @@ +--- +sidebar_position: 12 +--- + +### Definition + +Namespace: GeneralUpdate.Extension + +Assembly: GeneralUpdate.ClientCore.dll, GeneralUpdate.Core.dll + + + +GeneralUpdate.Extension provides a comprehensive extensibility framework that allows developers to customize and extend the update process. It enables you to inject custom logic, handle events, and tailor the update behavior to meet specific application requirements. This component is essential for developers who need fine-grained control over the update workflow. + +```c# +// Extension methods are available through GeneralClientBootstrap and GeneralUpdateBootstrap +public class GeneralClientBootstrap : AbstractBootstrap +``` + + + +### Example + +The Extension framework provides various ways to extend functionality [[View Example]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 + +```c# +var configinfo = new Configinfo +{ + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + AppName = "YourApp.exe", + ClientVersion = "1.0.0.0", + // ... other configuration +}; + +await new GeneralClientBootstrap() + // Add custom event listeners + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerException(OnException) + // Add custom operations + .AddCustomOption(CheckEnvironment) + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### Core Extension Capabilities + +GeneralUpdate.Extension provides the following extensibility features. + +#### Event Listeners + +| Method | Description | +| -------------------------------------- | ------------------------------------------------------------ | +| AddListenerMultiDownloadStatistics() | Subscribe to download progress notifications including speed, remaining time, bytes received, and progress percentage. Ideal for displaying real-time download information to users. | +| AddListenerMultiDownloadCompleted() | Event triggered when a single update package download completes (successfully or unsuccessfully). Allows you to handle per-version download completion. | +| AddListenerMultiAllDownloadCompleted() | Notification when all update packages have been downloaded. This is the final checkpoint before the update installation begins. | +| AddListenerMultiDownloadError() | Listen for download errors for each version. Provides exception details to help diagnose network or server issues. | +| AddListenerException() | Global exception handler that catches any errors during the entire update process. Critical for error logging and user notification. | + +#### Custom Operations + +| Method | Description | +| --------------------- | ------------------------------------------------------------ | +| AddCustomOption() | Inject custom asynchronous operations into the update workflow. Executed before the update starts. Perfect for environment checks, pre-update backups, or any preparatory tasks. Returns a Task where false cancels the update. | +| SetCustomSkipOption() | Allows users to decide whether to skip non-mandatory updates. Provides a way to show custom UI for user consent. Only effective when the server doesn't enforce mandatory updates. | +| SetBlacklist() | Define files or file formats that should never be updated. Useful for preserving user data, configuration files, or third-party dependencies. | + +#### Configuration Options + +| Method | Description | +| ------------- | ------------------------------------------------------------ | +| Option() | Configure update behavior such as timeout duration, encoding format, patch enablement, and backup settings. Provides granular control over the update process. | +| Config() | Set core update parameters including server URLs, version information, authentication keys, and installation paths. | +| GetOption() | Retrieve current update configuration settings for inspection or modification. | +| SetConfig() | Apply a complete Configinfo object to configure the update client. | + + + +### Event Arguments + +#### MultiDownloadStatisticsEventArgs + +Provides detailed download progress information. + +| Property | Type | Description | +| ------------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | Current version being downloaded | +| Speed | string | Download speed (e.g., "2.5 MB/s") | +| Remaining | string | Estimated time remaining (e.g., "00:02:30") | +| BytesReceived | long | Number of bytes downloaded so far | +| TotalBytesToReceive| long | Total size of the download in bytes | +| ProgressPercentage | double | Download progress as a percentage (0-100) | + +#### MultiDownloadCompletedEventArgs + +Indicates completion status of a single update package. + +| Property | Type | Description | +| ------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | Version information for the completed download | +| IsComplated | bool | Whether the download completed successfully | + +#### MultiAllDownloadCompletedEventArgs + +Summary of all download tasks. + +| Property | Type | Description | +| ---------------------- | --------------- | ------------------------------------------ | +| IsAllDownloadCompleted | bool | Whether all downloads completed successfully| +| FailedVersions | List| List of versions that failed to download | + +#### MultiDownloadErrorEventArgs + +Error information for failed downloads. + +| Property | Type | Description | +| --------- | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | Version that encountered an error | +| Exception | Exception | The exception that occurred during download | + +#### ExceptionEventArgs + +Global exception information. + +| Property | Type | Description | +| --------- | --------- | ---------------------------------------------------- | +| Exception | Exception | Exception that occurred during the update process | + + + +### Custom Operation Example + +Here's a complete example showing how to use custom operations: + +```c# +private async Task CheckEnvironment() +{ + try + { + // Check if required dependencies are installed + if (!IsDependencyInstalled("SomeLibrary")) + { + Console.WriteLine("Required dependency is missing!"); + return false; // Cancel update + } + + // Verify disk space + var requiredSpace = 500 * 1024 * 1024; // 500 MB + if (GetAvailableDiskSpace() < requiredSpace) + { + Console.WriteLine("Insufficient disk space!"); + return false; // Cancel update + } + + // Create backup of critical files + await BackupUserData(); + + return true; // Proceed with update + } + catch (Exception ex) + { + Console.WriteLine($"Environment check failed: {ex.Message}"); + return false; + } +} + +// Register the custom operation +await new GeneralClientBootstrap() + .AddCustomOption(CheckEnvironment) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### Skip Option Example + +Allow users to skip non-mandatory updates: + +```c# +private async Task ShowSkipDialog() +{ + // Show a custom dialog to the user + var result = await ShowUpdateDialog( + "A new update is available!", + "Would you like to update now?", + new[] { "Update Now", "Skip" } + ); + + return result == "Update Now"; +} + +// Register the skip option handler +await new GeneralClientBootstrap() + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### Blacklist Configuration + +Exclude specific files or formats from updates: + +```c# +var configinfo = new Configinfo +{ + // ... other configuration + BlackFiles = new List + { + "userconfig.json", + "license.dat", + "custom.db" + }, + BlackFormats = new List + { + ".log", + ".temp", + ".cache" + }, + BlackDirectories = new List + { + "UserData", + "Plugins\\Custom" + } +}; + +// Or use the SetBlacklist method +await new GeneralClientBootstrap() + .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### Complete Extension Example + +Here's a comprehensive example using multiple extension features: + +```c# +using GeneralUpdate.ClientCore; +using GeneralUpdate.Common.Shared.Object; + +public class UpdateManager +{ + public async Task StartUpdateWithExtensions() + { + var configinfo = new Configinfo + { + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", + AppName = "MyApp.exe", + MainAppName = "MyApp.exe", + ClientVersion = "1.0.0.0", + ProductId = "your-product-id", + AppSecretKey = "your-secret-key", + InstallPath = AppDomain.CurrentDomain.BaseDirectory, + BlackFiles = new List { "userdata.db" } + }; + + try + { + await new GeneralClientBootstrap() + // Register event listeners + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) + .AddListenerMultiDownloadError(OnDownloadError) + .AddListenerException(OnException) + // Add custom operations + .AddCustomOption(PerformPreUpdateChecks) + .SetCustomSkipOption(AskUserForUpdatePermission) + // Configure options + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 120) + .Option(UpdateOption.Patch, true) + .Option(UpdateOption.BackUp, true) + // Launch the update + .LaunchAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"Update failed: {ex.Message}"); + } + } + + private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"Downloading {version.Version}: {e.ProgressPercentage:F2}% at {e.Speed}"); + // Update UI progress bar here + } + + private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + var version = e.Version as VersionInfo; + if (e.IsComplated) + { + Console.WriteLine($"Version {version.Version} downloaded successfully!"); + } + else + { + Console.WriteLine($"Failed to download version {version.Version}"); + } + } + + private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + if (e.IsAllDownloadCompleted) + { + Console.WriteLine("All updates downloaded successfully!"); + } + else + { + Console.WriteLine($"Update failed. {e.FailedVersions.Count} versions failed."); + } + } + + private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"Error downloading {version.Version}: {e.Exception.Message}"); + } + + private void OnException(object sender, ExceptionEventArgs e) + { + Console.WriteLine($"Update exception: {e.Exception.Message}"); + // Log exception to file or monitoring system + } + + private async Task PerformPreUpdateChecks() + { + // Check system requirements + Console.WriteLine("Performing pre-update checks..."); + + // Verify network connectivity + if (!await CheckNetworkConnection()) + { + Console.WriteLine("No network connection available!"); + return false; + } + + // Check disk space + if (!HasSufficientDiskSpace(500 * 1024 * 1024)) + { + Console.WriteLine("Insufficient disk space!"); + return false; + } + + Console.WriteLine("Pre-update checks passed!"); + return true; + } + + private async Task AskUserForUpdatePermission() + { + Console.WriteLine("New update available. Update now? (y/n)"); + var response = Console.ReadLine(); + return response?.ToLower() == "y"; + } + + private async Task CheckNetworkConnection() + { + // Implement network check + return true; + } + + private bool HasSufficientDiskSpace(long requiredBytes) + { + // Implement disk space check + return true; + } +} +``` + + + +### Best Practices + +1. **Always handle exceptions**: Use `AddListenerException()` to catch and log all update errors. + +2. **Validate environment before updates**: Use `AddCustomOption()` to check system requirements, dependencies, and available disk space. + +3. **Provide user feedback**: Subscribe to download progress events to show real-time updates to users. + +4. **Protect user data**: Use blacklist features to exclude user configuration files and data from updates. + +5. **Implement retry logic**: In error handlers, implement intelligent retry mechanisms for transient failures. + +6. **Test custom operations**: Thoroughly test all custom operations to ensure they don't block legitimate updates. + +7. **Use appropriate timeouts**: Set reasonable timeout values based on expected file sizes and network conditions. + +8. **Log everything**: Maintain detailed logs of the update process for troubleshooting. + + + +### Common Use Cases + +#### Use Case 1: Environment Validation + +Before updating, verify that all prerequisites are met: + +```c# +private async Task ValidateEnvironment() +{ + // Check .NET runtime version + if (!IsRuntimeVersionSupported()) + return false; + + // Verify required services are running + if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) + return false; + + // Check file permissions + if (!HasWritePermission(installPath)) + return false; + + return true; +} +``` + +#### Use Case 2: Custom Update Notification + +Show custom notifications to users during the update process: + +```c# +private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +{ + // Show system tray notification + ShowNotification($"Updating: {e.ProgressPercentage:F0}%"); + + // Update application title + UpdateWindowTitle($"Downloading update... {e.ProgressPercentage:F0}%"); +} +``` + +#### Use Case 3: Conditional Updates + +Apply updates based on specific conditions: + +```c# +private async Task ShouldApplyUpdate() +{ + // Check if it's business hours + var now = DateTime.Now; + if (now.Hour >= 9 && now.Hour <= 17) + { + // Defer update during business hours + return false; + } + + // Check if critical operations are running + if (IsCriticalOperationInProgress()) + { + return false; + } + + return true; +} +``` + + + +### Troubleshooting + +#### Problem: Custom operation blocks the update indefinitely + +**Solution**: Ensure your custom operations have timeouts and proper error handling: + +```c# +private async Task CustomOperationWithTimeout() +{ + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + try + { + await YourAsyncOperation(cts.Token); + return true; + } + catch (OperationCanceledException) + { + Console.WriteLine("Operation timed out"); + return false; + } +} +``` + +#### Problem: Events not firing + +**Solution**: Ensure you register event listeners before calling `LaunchAsync()`: + +```c# +// Correct order +await new GeneralClientBootstrap() + .AddListenerException(OnException) // Register first + .SetConfig(configinfo) + .LaunchAsync(); // Launch last +``` + +#### Problem: Skip option not working + +**Solution**: Verify the server hasn't set the update as mandatory. The skip option only works for non-mandatory updates. + + + +### Applicable To + +| Product | Version | +| -------------- | ---------------- | +| .NET | 5, 6, 7, 8, 9, 10| +| .NET Framework | 4.6.1 | +| .NET Standard | 2.0 | +| .NET Core | 2.0 | + +### See Also + +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - Main client update component +- [GeneralUpdate.Core](./GeneralUpdate.Core.md) - Core update logic +- [Quick Start Guide](../quickstart/Quik%20start.md) - Getting started with GeneralUpdate diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md new file mode 100644 index 0000000..945d69d --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md @@ -0,0 +1,523 @@ +--- +sidebar_position: 12 +--- + +### 定义 + +命名空间:GeneralUpdate.Extension + +程序集:GeneralUpdate.ClientCore.dll、GeneralUpdate.Core.dll + + + +GeneralUpdate.Extension 提供了一个全面的扩展框架,允许开发人员自定义和扩展更新过程。它使您能够注入自定义逻辑、处理事件,并根据特定应用程序需求定制更新行为。对于需要对更新工作流进行细粒度控制的开发人员来说,此组件至关重要。 + +```c# +// 扩展方法通过 GeneralClientBootstrap 和 GeneralUpdateBootstrap 提供 +public class GeneralClientBootstrap : AbstractBootstrap +``` + + + +### 示例 + +Extension 框架提供多种扩展功能的方法 [[查看示例]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 + +```c# +var configinfo = new Configinfo +{ + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + AppName = "YourApp.exe", + ClientVersion = "1.0.0.0", + // ... 其他配置 +}; + +await new GeneralClientBootstrap() + // 添加自定义事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(CheckEnvironment) + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 核心扩展能力 + +GeneralUpdate.Extension 提供以下扩展功能。 + +#### 事件监听器 + +| 方法 | 说明 | +| -------------------------------------- | ------------------------------------------------------------ | +| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | +| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | +| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | +| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | +| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | + +#### 自定义操作 + +| 方法 | 说明 | +| --------------------- | ------------------------------------------------------------ | +| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task,返回 false 将取消更新。 | +| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | +| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | + +#### 配置选项 + +| 方法 | 说明 | +| ------------- | ------------------------------------------------------------ | +| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | +| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | +| GetOption() | 检索当前更新配置设置以供检查或修改。 | +| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | + + + +### 事件参数 + +#### MultiDownloadStatisticsEventArgs + +提供详细的下载进度信息。 + +| 属性 | 类型 | 说明 | +| ------------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 当前正在下载的版本 | +| Speed | string | 下载速度(例如 "2.5 MB/s") | +| Remaining | string | 预计剩余时间(例如 "00:02:30") | +| BytesReceived | long | 到目前为止已下载的字节数 | +| TotalBytesToReceive| long | 下载的总大小(字节) | +| ProgressPercentage | double | 下载进度百分比(0-100) | + +#### MultiDownloadCompletedEventArgs + +指示单个更新包的完成状态。 + +| 属性 | 类型 | 说明 | +| ------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 已完成下载的版本信息 | +| IsComplated | bool | 下载是否成功完成 | + +#### MultiAllDownloadCompletedEventArgs + +所有下载任务的摘要。 + +| 属性 | 类型 | 说明 | +| ---------------------- | --------------- | ------------------------------------------ | +| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| +| FailedVersions | List| 下载失败的版本列表 | + +#### MultiDownloadErrorEventArgs + +下载失败的错误信息。 + +| 属性 | 类型 | 说明 | +| --------- | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 遇到错误的版本 | +| Exception | Exception | 下载期间发生的异常 | + +#### ExceptionEventArgs + +全局异常信息。 + +| 属性 | 类型 | 说明 | +| --------- | --------- | ---------------------------------------------------- | +| Exception | Exception | 更新过程中发生的异常 | + + + +### 自定义操作示例 + +以下是使用自定义操作的完整示例: + +```c# +private async Task CheckEnvironment() +{ + try + { + // 检查是否安装了所需的依赖项 + if (!IsDependencyInstalled("SomeLibrary")) + { + Console.WriteLine("缺少必需的依赖项!"); + return false; // 取消更新 + } + + // 验证磁盘空间 + var requiredSpace = 500 * 1024 * 1024; // 500 MB + if (GetAvailableDiskSpace() < requiredSpace) + { + Console.WriteLine("磁盘空间不足!"); + return false; // 取消更新 + } + + // 创建关键文件的备份 + await BackupUserData(); + + return true; // 继续更新 + } + catch (Exception ex) + { + Console.WriteLine($"环境检查失败:{ex.Message}"); + return false; + } +} + +// 注册自定义操作 +await new GeneralClientBootstrap() + .AddCustomOption(CheckEnvironment) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 跳过选项示例 + +允许用户跳过非强制更新: + +```c# +private async Task ShowSkipDialog() +{ + // 向用户显示自定义对话框 + var result = await ShowUpdateDialog( + "有新的更新可用!", + "您想现在更新吗?", + new[] { "立即更新", "跳过" } + ); + + return result == "立即更新"; +} + +// 注册跳过选项处理程序 +await new GeneralClientBootstrap() + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 黑名单配置 + +从更新中排除特定文件或格式: + +```c# +var configinfo = new Configinfo +{ + // ... 其他配置 + BlackFiles = new List + { + "userconfig.json", + "license.dat", + "custom.db" + }, + BlackFormats = new List + { + ".log", + ".temp", + ".cache" + }, + BlackDirectories = new List + { + "UserData", + "Plugins\\Custom" + } +}; + +// 或使用 SetBlacklist 方法 +await new GeneralClientBootstrap() + .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 完整扩展示例 + +以下是使用多个扩展功能的综合示例: + +```c# +using GeneralUpdate.ClientCore; +using GeneralUpdate.Common.Shared.Object; + +public class UpdateManager +{ + public async Task StartUpdateWithExtensions() + { + var configinfo = new Configinfo + { + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", + AppName = "MyApp.exe", + MainAppName = "MyApp.exe", + ClientVersion = "1.0.0.0", + ProductId = "your-product-id", + AppSecretKey = "your-secret-key", + InstallPath = AppDomain.CurrentDomain.BaseDirectory, + BlackFiles = new List { "userdata.db" } + }; + + try + { + await new GeneralClientBootstrap() + // 注册事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) + .AddListenerMultiDownloadError(OnDownloadError) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(PerformPreUpdateChecks) + .SetCustomSkipOption(AskUserForUpdatePermission) + // 配置选项 + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 120) + .Option(UpdateOption.Patch, true) + .Option(UpdateOption.BackUp, true) + // 启动更新 + .LaunchAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"更新失败:{ex.Message}"); + } + } + + private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); + // 在此更新 UI 进度条 + } + + private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + var version = e.Version as VersionInfo; + if (e.IsComplated) + { + Console.WriteLine($"版本 {version.Version} 下载成功!"); + } + else + { + Console.WriteLine($"版本 {version.Version} 下载失败!"); + } + } + + private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + if (e.IsAllDownloadCompleted) + { + Console.WriteLine("所有更新下载成功!"); + } + else + { + Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); + } + } + + private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); + } + + private void OnException(object sender, ExceptionEventArgs e) + { + Console.WriteLine($"更新异常:{e.Exception.Message}"); + // 将异常记录到文件或监控系统 + } + + private async Task PerformPreUpdateChecks() + { + // 检查系统要求 + Console.WriteLine("正在执行更新前检查..."); + + // 验证网络连接 + if (!await CheckNetworkConnection()) + { + Console.WriteLine("没有可用的网络连接!"); + return false; + } + + // 检查磁盘空间 + if (!HasSufficientDiskSpace(500 * 1024 * 1024)) + { + Console.WriteLine("磁盘空间不足!"); + return false; + } + + Console.WriteLine("更新前检查通过!"); + return true; + } + + private async Task AskUserForUpdatePermission() + { + Console.WriteLine("有新更新可用。立即更新?(y/n)"); + var response = Console.ReadLine(); + return response?.ToLower() == "y"; + } + + private async Task CheckNetworkConnection() + { + // 实现网络检查 + return true; + } + + private bool HasSufficientDiskSpace(long requiredBytes) + { + // 实现磁盘空间检查 + return true; + } +} +``` + + + +### 最佳实践 + +1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 + +2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 + +3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 + +4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 + +5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 + +6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 + +7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 + +8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 + + + +### 常见用例 + +#### 用例 1:环境验证 + +更新前,验证是否满足所有先决条件: + +```c# +private async Task ValidateEnvironment() +{ + // 检查 .NET 运行时版本 + if (!IsRuntimeVersionSupported()) + return false; + + // 验证所需服务是否正在运行 + if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) + return false; + + // 检查文件权限 + if (!HasWritePermission(installPath)) + return false; + + return true; +} +``` + +#### 用例 2:自定义更新通知 + +在更新过程中向用户显示自定义通知: + +```c# +private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +{ + // 显示系统托盘通知 + ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); + + // 更新应用程序标题 + UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); +} +``` + +#### 用例 3:条件更新 + +根据特定条件应用更新: + +```c# +private async Task ShouldApplyUpdate() +{ + // 检查是否在营业时间内 + var now = DateTime.Now; + if (now.Hour >= 9 && now.Hour <= 17) + { + // 在营业时间推迟更新 + return false; + } + + // 检查是否有关键操作正在运行 + if (IsCriticalOperationInProgress()) + { + return false; + } + + return true; +} +``` + + + +### 故障排除 + +#### 问题:自定义操作无限期地阻止更新 + +**解决方案**:确保您的自定义操作具有超时和适当的错误处理: + +```c# +private async Task CustomOperationWithTimeout() +{ + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + try + { + await YourAsyncOperation(cts.Token); + return true; + } + catch (OperationCanceledException) + { + Console.WriteLine("操作超时"); + return false; + } +} +``` + +#### 问题:事件未触发 + +**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: + +```c# +// 正确的顺序 +await new GeneralClientBootstrap() + .AddListenerException(OnException) // 先注册 + .SetConfig(configinfo) + .LaunchAsync(); // 最后启动 +``` + +#### 问题:跳过选项不起作用 + +**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 + + + +### 适用于 + +| 产品 | 版本 | +| -------------- | ---------------- | +| .NET | 5, 6, 7, 8, 9, 10| +| .NET Framework | 4.6.1 | +| .NET Standard | 2.0 | +| .NET Core | 2.0 | + +### 另请参阅 + +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 +- [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 +- [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门 From 04571b0b578a630830636c69a414337817da767f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:05:13 +0000 Subject: [PATCH 3/5] Fix HTML tag escaping in GeneralUpdate.Extension documentation Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --- website/docs/doc/GeneralUpdate.Extension.md | 14 +++++++------- .../docs/doc/GeneralUpdate.Extension.md | 14 +++++++------- .../current/doc/GeneralUpdate.Extension.md | 14 +++++++------- website/package-lock.json | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/website/docs/doc/GeneralUpdate.Extension.md b/website/docs/doc/GeneralUpdate.Extension.md index 945d69d..be533c8 100644 --- a/website/docs/doc/GeneralUpdate.Extension.md +++ b/website/docs/doc/GeneralUpdate.Extension.md @@ -64,7 +64,7 @@ GeneralUpdate.Extension 提供以下扩展功能。 | 方法 | 说明 | | --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task,返回 false 将取消更新。 | +| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | | SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | | SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | @@ -110,7 +110,7 @@ GeneralUpdate.Extension 提供以下扩展功能。 | 属性 | 类型 | 说明 | | ---------------------- | --------------- | ------------------------------------------ | | IsAllDownloadCompleted | bool | 是否所有下载都成功完成| -| FailedVersions | List| 下载失败的版本列表 | +| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | #### MultiDownloadErrorEventArgs @@ -210,19 +210,19 @@ await new GeneralClientBootstrap() var configinfo = new Configinfo { // ... 其他配置 - BlackFiles = new List + BlackFiles = new List<string> { "userconfig.json", "license.dat", "custom.db" }, - BlackFormats = new List + BlackFormats = new List<string> { ".log", ".temp", ".cache" }, - BlackDirectories = new List + BlackDirectories = new List<string> { "UserData", "Plugins\\Custom" @@ -231,7 +231,7 @@ var configinfo = new Configinfo // 或使用 SetBlacklist 方法 await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetBlacklist(blackFiles: new List<string> { "config.ini" }) .SetConfig(configinfo) .LaunchAsync(); ``` @@ -260,7 +260,7 @@ public class UpdateManager ProductId = "your-product-id", AppSecretKey = "your-secret-key", InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List { "userdata.db" } + BlackFiles = new List<string> { "userdata.db" } }; try diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md index 28f9976..860d31d 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md @@ -64,7 +64,7 @@ GeneralUpdate.Extension provides the following extensibility features. | Method | Description | | --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | Inject custom asynchronous operations into the update workflow. Executed before the update starts. Perfect for environment checks, pre-update backups, or any preparatory tasks. Returns a Task where false cancels the update. | +| AddCustomOption() | Inject custom asynchronous operations into the update workflow. Executed before the update starts. Perfect for environment checks, pre-update backups, or any preparatory tasks. Returns a Task<bool> where false cancels the update. | | SetCustomSkipOption() | Allows users to decide whether to skip non-mandatory updates. Provides a way to show custom UI for user consent. Only effective when the server doesn't enforce mandatory updates. | | SetBlacklist() | Define files or file formats that should never be updated. Useful for preserving user data, configuration files, or third-party dependencies. | @@ -110,7 +110,7 @@ Summary of all download tasks. | Property | Type | Description | | ---------------------- | --------------- | ------------------------------------------ | | IsAllDownloadCompleted | bool | Whether all downloads completed successfully| -| FailedVersions | List| List of versions that failed to download | +| FailedVersions | List<VersionInfo>| List of versions that failed to download | #### MultiDownloadErrorEventArgs @@ -210,19 +210,19 @@ Exclude specific files or formats from updates: var configinfo = new Configinfo { // ... other configuration - BlackFiles = new List + BlackFiles = new List<string> { "userconfig.json", "license.dat", "custom.db" }, - BlackFormats = new List + BlackFormats = new List<string> { ".log", ".temp", ".cache" }, - BlackDirectories = new List + BlackDirectories = new List<string> { "UserData", "Plugins\\Custom" @@ -231,7 +231,7 @@ var configinfo = new Configinfo // Or use the SetBlacklist method await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetBlacklist(blackFiles: new List<string> { "config.ini" }) .SetConfig(configinfo) .LaunchAsync(); ``` @@ -260,7 +260,7 @@ public class UpdateManager ProductId = "your-product-id", AppSecretKey = "your-secret-key", InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List { "userdata.db" } + BlackFiles = new List<string> { "userdata.db" } }; try diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md index 945d69d..be533c8 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md @@ -64,7 +64,7 @@ GeneralUpdate.Extension 提供以下扩展功能。 | 方法 | 说明 | | --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task,返回 false 将取消更新。 | +| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | | SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | | SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | @@ -110,7 +110,7 @@ GeneralUpdate.Extension 提供以下扩展功能。 | 属性 | 类型 | 说明 | | ---------------------- | --------------- | ------------------------------------------ | | IsAllDownloadCompleted | bool | 是否所有下载都成功完成| -| FailedVersions | List| 下载失败的版本列表 | +| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | #### MultiDownloadErrorEventArgs @@ -210,19 +210,19 @@ await new GeneralClientBootstrap() var configinfo = new Configinfo { // ... 其他配置 - BlackFiles = new List + BlackFiles = new List<string> { "userconfig.json", "license.dat", "custom.db" }, - BlackFormats = new List + BlackFormats = new List<string> { ".log", ".temp", ".cache" }, - BlackDirectories = new List + BlackDirectories = new List<string> { "UserData", "Plugins\\Custom" @@ -231,7 +231,7 @@ var configinfo = new Configinfo // 或使用 SetBlacklist 方法 await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List { "config.ini" }) + .SetBlacklist(blackFiles: new List<string> { "config.ini" }) .SetConfig(configinfo) .LaunchAsync(); ``` @@ -260,7 +260,7 @@ public class UpdateManager ProductId = "your-product-id", AppSecretKey = "your-secret-key", InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List { "userdata.db" } + BlackFiles = new List<string> { "userdata.db" } }; try diff --git a/website/package-lock.json b/website/package-lock.json index 7a10580..eaf4825 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -130,6 +130,7 @@ "version": "4.22.1", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "peer": true, "dependencies": { "@algolia/client-common": "4.22.1", "@algolia/requester-common": "4.22.1", @@ -285,6 +286,7 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", @@ -3177,6 +3179,7 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "peer": true, "dependencies": { "@babel/core": "^7.19.6", "@svgr/babel-preset": "^6.5.1", @@ -3528,6 +3531,7 @@ "version": "18.2.66", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.66.tgz", "integrity": "sha512-OYTmMI4UigXeFMF/j4uv0lBBEbongSgptPrHBxqME44h9+yNov+oL6Z3ocJKo0WyXR84sQUNeyIp9MRfckvZpg==", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3823,6 +3827,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3878,6 +3883,7 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3920,6 +3926,7 @@ "version": "4.22.1", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", + "peer": true, "dependencies": { "@algolia/cache-browser-local-storage": "4.22.1", "@algolia/cache-common": "4.22.1", @@ -4344,6 +4351,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -6254,6 +6262,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6443,6 +6452,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -10997,6 +11007,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -11803,6 +11814,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -11932,6 +11944,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -11987,6 +12000,7 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "peer": true, "dependencies": { "@types/react": "*", "prop-types": "^15.6.2" @@ -12014,6 +12028,7 @@ "version": "5.3.4", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -13473,6 +13488,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14017,6 +14033,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14197,6 +14214,7 @@ "version": "5.90.3", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -14425,6 +14443,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", From f178cf75269b6a7e5ba4d1ebd0d1385f82f5df6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:20:55 +0000 Subject: [PATCH 4/5] Complete rewrite: GeneralUpdate.Extension documentation for actual extension management system Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --- website/docs/doc/GeneralUpdate.Extension.md | 1053 +++++++++++------ .../doc/GeneralUpdate.Extension.md.backup | 528 +++++++++ .../docs/doc/GeneralUpdate.Extension.md | 1053 +++++++++++------ .../current/doc/GeneralUpdate.Extension.md | 1053 +++++++++++------ 4 files changed, 2640 insertions(+), 1047 deletions(-) create mode 100644 website/docs/doc/GeneralUpdate.Extension.md.backup diff --git a/website/docs/doc/GeneralUpdate.Extension.md b/website/docs/doc/GeneralUpdate.Extension.md index be533c8..ad1e7ee 100644 --- a/website/docs/doc/GeneralUpdate.Extension.md +++ b/website/docs/doc/GeneralUpdate.Extension.md @@ -6,518 +6,873 @@ sidebar_position: 12 命名空间:GeneralUpdate.Extension -程序集:GeneralUpdate.ClientCore.dll、GeneralUpdate.Core.dll +程序集:GeneralUpdate.Extension.dll +**NuGet 包**:GeneralUpdate.Extension -GeneralUpdate.Extension 提供了一个全面的扩展框架,允许开发人员自定义和扩展更新过程。它使您能够注入自定义逻辑、处理事件,并根据特定应用程序需求定制更新行为。对于需要对更新工作流进行细粒度控制的开发人员来说,此组件至关重要。 + +GeneralUpdate.Extension 是一个受 VS Code 启发的 .NET 应用程序扩展管理系统。它提供完整的插件/扩展管理功能,包括扩展下载、安装、更新、版本兼容性检查、平台支持、依赖关系解析和回滚机制。 ```c# -// 扩展方法通过 GeneralClientBootstrap 和 GeneralUpdateBootstrap 提供 -public class GeneralClientBootstrap : AbstractBootstrap +public class GeneralExtensionHost : IExtensionHost +{ + public IExtensionCatalog ExtensionCatalog { get; } + public event EventHandler? ExtensionUpdateStatusChanged; +} ``` -### 示例 +### 快速开始 -Extension 框架提供多种扩展功能的方法 [[查看示例]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 +最基本的使用示例,初始化扩展主机并查询远程扩展 [[查看完整示例]](https://github.com/GeneralLibrary/GeneralUpdate/blob/master/src/c%23/GeneralUpdate.Extension/Examples/ExtensionExample.cs)。 ```c# -var configinfo = new Configinfo +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; + +// 1. 初始化扩展主机 +var options = new ExtensionHostOptions { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - AppName = "YourApp.exe", - ClientVersion = "1.0.0.0", - // ... 其他配置 + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", // 可选:用于身份验证 + HostVersion = "1.0.0", // 您的应用程序版本 + ExtensionsDirectory = "./extensions" // 扩展安装目录 }; -await new GeneralClientBootstrap() - // 添加自定义事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(CheckEnvironment) - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); -``` +var host = new GeneralExtensionHost(options); +// 2. 订阅更新事件 +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + Console.WriteLine($"扩展: {e.ExtensionName}"); + Console.WriteLine($"状态: {e.Status}"); + Console.WriteLine($"进度: {e.Progress}%"); +}; +// 3. 查询可用扩展 +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows, + HostVersion = "1.0.0", + PageNumber = 1, + PageSize = 20 +}; -### 核心扩展能力 +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + foreach (var ext in result.Data.Items) + { + Console.WriteLine($"{ext.DisplayName} v{ext.Version}"); + Console.WriteLine($"兼容: {ext.IsCompatible}"); + } +} + +// 4. 更新扩展 +bool success = await host.UpdateExtensionAsync("extension-guid"); +``` -GeneralUpdate.Extension 提供以下扩展功能。 -#### 事件监听器 -| 方法 | 说明 | -| -------------------------------------- | ------------------------------------------------------------ | -| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | -| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | -| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | -| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | -| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | +### 核心功能 -#### 自定义操作 +GeneralUpdate.Extension 提供以下核心功能。 -| 方法 | 说明 | -| --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | -| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | -| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | +#### 扩展主机 (GeneralExtensionHost) -#### 配置选项 +主要的扩展管理容器,提供所有扩展操作的入口点。 -| 方法 | 说明 | -| ------------- | ------------------------------------------------------------ | -| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | -| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | -| GetOption() | 检索当前更新配置设置以供检查或修改。 | -| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | +| 方法 | 说明 | +| --------------------------- | ------------------------------------------------------------ | +| QueryExtensionsAsync() | 从远程服务器查询可用扩展。支持按名称、平台、版本、状态等条件过滤。 | +| UpdateExtensionAsync() | 更新指定扩展。自动处理下载、兼容性检查、平台验证和安装。 | +| DownloadExtensionAsync() | 下载扩展包到指定路径。支持断点续传(HTTP Range 请求)。 | +| InstallExtensionAsync() | 安装扩展包。支持自动回滚功能,安装失败时恢复到之前状态。 | +| IsExtensionCompatible() | 检查扩展与当前主机版本是否兼容。 | +| SetAutoUpdate() | 为指定扩展启用/禁用自动更新。 | +| SetGlobalAutoUpdate() | 启用/禁用所有扩展的全局自动更新。 | +| IsAutoUpdateEnabled() | 检查扩展是否启用自动更新。 | +#### 扩展目录 (ExtensionCatalog) +管理本地已安装扩展的目录,使用 JSON 文件持久化存储。 -### 事件参数 +| 方法 | 说明 | +| --------------------------------- | ------------------------------------------------------------ | +| LoadInstalledExtensions() | 从 catalog.json 加载已安装的扩展列表。 | +| GetInstalledExtensions() | 获取所有已安装扩展的列表。 | +| GetInstalledExtensionById() | 根据扩展 ID(GUID)获取特定扩展。 | +| GetInstalledExtensionsByPlatform()| 获取支持指定平台的扩展列表。 | +| AddOrUpdateExtension() | 添加新扩展或更新现有扩展的元数据。 | +| RemoveExtension() | 从目录中删除扩展记录。 | +| SaveCatalog() | 将目录保存到 JSON 文件。 | -#### MultiDownloadStatisticsEventArgs +#### 下载队列 (DownloadQueueManager) -提供详细的下载进度信息。 +管理扩展下载队列,支持并发下载和状态跟踪。 -| 属性 | 类型 | 说明 | -| ------------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 当前正在下载的版本 | -| Speed | string | 下载速度(例如 "2.5 MB/s") | -| Remaining | string | 预计剩余时间(例如 "00:02:30") | -| BytesReceived | long | 到目前为止已下载的字节数 | -| TotalBytesToReceive| long | 下载的总大小(字节) | -| ProgressPercentage | double | 下载进度百分比(0-100) | +| 方法 | 说明 | +| ------------------------ | ------------------------------------------------------------ | +| EnqueueDownload() | 将下载任务添加到队列。任务将自动排队并处理。 | +| GetDownloadStatus() | 获取指定下载任务的当前状态。 | +| CancelDownload() | 取消正在进行的下载任务。 | -#### MultiDownloadCompletedEventArgs +下载队列特性: +- 默认并发下载数:3 个 +- 自动状态跟踪(排队、下载中、已完成、失败) +- 支持断点续传 +- 事件通知下载状态变化 -指示单个更新包的完成状态。 -| 属性 | 类型 | 说明 | -| ------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 已完成下载的版本信息 | -| IsComplated | bool | 下载是否成功完成 | -#### MultiAllDownloadCompletedEventArgs +### 扩展元数据 -所有下载任务的摘要。 +每个扩展必须包含以下元数据信息: -| 属性 | 类型 | 说明 | -| ---------------------- | --------------- | ------------------------------------------ | -| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| -| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | +| 属性 | 类型 | 说明 | +| ------------------ | --------------- | ---------------------------------------------------- | +| Id | string | 扩展唯一标识符(GUID 格式) | +| Name | string | 扩展名称(唯一,小写,无空格) | +| DisplayName | string | 人类可读的显示名称 | +| Version | string | 语义化版本号(例如 "1.2.3") | +| Publisher | string | 发布者标识符 | +| Description | string | 扩展描述 | +| SupportedPlatforms | TargetPlatform | 支持的平台标志(Windows/Linux/MacOS/All) | +| MinHostVersion | string | 最低兼容的主机版本 | +| MaxHostVersion | string | 最高兼容的主机版本 | +| Dependencies | string | 依赖的扩展 ID 列表(逗号分隔) | +| Format | string | 文件格式(.dll、.zip 等) | +| Categories | string | 扩展分类(逗号分隔) | +| IsPreRelease | bool | 是否为预发布版本 | +| License | string | 许可证标识符(例如 "MIT"、"Apache-2.0") | +| FileSize | long | 文件大小(字节) | +| Hash | string | 文件哈希值(SHA256) | +| DownloadUrl | string | 下载 URL | -#### MultiDownloadErrorEventArgs -下载失败的错误信息。 -| 属性 | 类型 | 说明 | -| --------- | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 遇到错误的版本 | -| Exception | Exception | 下载期间发生的异常 | +### 初始化和配置 -#### ExceptionEventArgs +#### 基本初始化 -全局异常信息。 +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" +}; -| 属性 | 类型 | 说明 | -| --------- | --------- | ---------------------------------------------------- | -| Exception | Exception | 更新过程中发生的异常 | +var host = new GeneralExtensionHost(options); +``` +#### 带身份验证的初始化 +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions", + CatalogPath = "./extensions/catalog.json" // 可选:自定义目录文件路径 +}; -### 自定义操作示例 +var host = new GeneralExtensionHost(options); +``` -以下是使用自定义操作的完整示例: +#### 订阅事件 ```c# -private async Task CheckEnvironment() +host.ExtensionUpdateStatusChanged += (sender, e) => { - try + switch (e.Status) { - // 检查是否安装了所需的依赖项 - if (!IsDependencyInstalled("SomeLibrary")) - { - Console.WriteLine("缺少必需的依赖项!"); - return false; // 取消更新 - } - - // 验证磁盘空间 - var requiredSpace = 500 * 1024 * 1024; // 500 MB - if (GetAvailableDiskSpace() < requiredSpace) - { - Console.WriteLine("磁盘空间不足!"); - return false; // 取消更新 - } - - // 创建关键文件的备份 - await BackupUserData(); - - return true; // 继续更新 + case ExtensionUpdateStatus.Queued: + Console.WriteLine($"{e.ExtensionName} 已加入队列"); + break; + case ExtensionUpdateStatus.Updating: + Console.WriteLine($"{e.ExtensionName} 更新中... {e.Progress}%"); + break; + case ExtensionUpdateStatus.UpdateSuccessful: + Console.WriteLine($"{e.ExtensionName} 更新成功!"); + break; + case ExtensionUpdateStatus.UpdateFailed: + Console.WriteLine($"{e.ExtensionName} 更新失败: {e.ErrorMessage}"); + break; } - catch (Exception ex) +}; +``` + + + +### 查询远程扩展 + +#### 基本查询 + +```c# +var query = new ExtensionQueryDTO +{ + PageNumber = 1, + PageSize = 20 +}; + +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + Console.WriteLine($"找到 {result.Data.TotalCount} 个扩展"); + foreach (var ext in result.Data.Items) { - Console.WriteLine($"环境检查失败:{ex.Message}"); - return false; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" 发布者: {ext.Publisher}"); + Console.WriteLine($" 兼容: {ext.IsCompatible}"); } } +``` -// 注册自定义操作 -await new GeneralClientBootstrap() - .AddCustomOption(CheckEnvironment) - .SetConfig(configinfo) - .LaunchAsync(); +#### 高级查询(带过滤条件) + +```c# +var query = new ExtensionQueryDTO +{ + Name = "my-extension", // 按名称搜索 + Platform = TargetPlatform.Windows, // 只查询 Windows 扩展 + HostVersion = "1.0.0", // 检查与此版本的兼容性 + Status = true, // 只查询已启用的扩展 + BeginDate = DateTime.Now.AddMonths(-1), // 最近一个月的扩展 + EndDate = DateTime.Now, + PageNumber = 1, + PageSize = 50 +}; + +var result = await host.QueryExtensionsAsync(query); ``` -### 跳过选项示例 +### 安装和更新扩展 + +#### 自动更新扩展 -允许用户跳过非强制更新: +最简单的方式,一次调用完成所有操作: ```c# -private async Task ShowSkipDialog() +// UpdateExtensionAsync 自动执行以下步骤: +// 1. 从服务器查询扩展信息 +// 2. 检查版本兼容性 +// 3. 检查平台支持 +// 4. 下载扩展包 +// 5. 安装扩展 +// 6. 更新本地目录 + +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +bool success = await host.UpdateExtensionAsync(extensionId); + +if (success) { - // 向用户显示自定义对话框 - var result = await ShowUpdateDialog( - "有新的更新可用!", - "您想现在更新吗?", - new[] { "立即更新", "跳过" } - ); - - return result == "立即更新"; + Console.WriteLine("扩展更新成功!"); +} +else +{ + Console.WriteLine("扩展更新失败,请检查事件获取详细信息"); } - -// 注册跳过选项处理程序 -await new GeneralClientBootstrap() - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); ``` +#### 手动下载和安装 +分步骤控制安装过程: -### 黑名单配置 +```c# +// 步骤 1:下载扩展 +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +string savePath = "./downloads/my-extension.zip"; -从更新中排除特定文件或格式: +bool downloaded = await host.DownloadExtensionAsync(extensionId, savePath); -```c# -var configinfo = new Configinfo +if (downloaded) { - // ... 其他配置 - BlackFiles = new List<string> - { - "userconfig.json", - "license.dat", - "custom.db" - }, - BlackFormats = new List<string> - { - ".log", - ".temp", - ".cache" - }, - BlackDirectories = new List<string> + // 步骤 2:安装扩展(带回滚功能) + bool installed = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // 失败时自动回滚 + ); + + if (installed) { - "UserData", - "Plugins\\Custom" + Console.WriteLine("扩展安装成功!"); } -}; - -// 或使用 SetBlacklist 方法 -await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List<string> { "config.ini" }) - .SetConfig(configinfo) - .LaunchAsync(); + else + { + Console.WriteLine("安装失败,已自动回滚"); + } +} ``` -### 完整扩展示例 +### 管理已安装扩展 -以下是使用多个扩展功能的综合示例: +#### 列出所有扩展 ```c# -using GeneralUpdate.ClientCore; -using GeneralUpdate.Common.Shared.Object; +var extensions = host.ExtensionCatalog.GetInstalledExtensions(); -public class UpdateManager +Console.WriteLine($"已安装 {extensions.Count} 个扩展:"); +foreach (var ext in extensions) { - public async Task StartUpdateWithExtensions() - { - var configinfo = new Configinfo - { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", - AppName = "MyApp.exe", - MainAppName = "MyApp.exe", - ClientVersion = "1.0.0.0", - ProductId = "your-product-id", - AppSecretKey = "your-secret-key", - InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List<string> { "userdata.db" } - }; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" 状态: {(ext.Status == true ? "启用" : "禁用")}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); +} +``` - try - { - await new GeneralClientBootstrap() - // 注册事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) - .AddListenerMultiDownloadError(OnDownloadError) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(PerformPreUpdateChecks) - .SetCustomSkipOption(AskUserForUpdatePermission) - // 配置选项 - .SetConfig(configinfo) - .Option(UpdateOption.DownloadTimeOut, 120) - .Option(UpdateOption.Patch, true) - .Option(UpdateOption.BackUp, true) - // 启动更新 - .LaunchAsync(); - } - catch (Exception ex) - { - Console.WriteLine($"更新失败:{ex.Message}"); - } - } +#### 获取特定扩展 - private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); - // 在此更新 UI 进度条 - } +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); - private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var version = e.Version as VersionInfo; - if (e.IsComplated) - { - Console.WriteLine($"版本 {version.Version} 下载成功!"); - } - else - { - Console.WriteLine($"版本 {version.Version} 下载失败!"); - } - } +if (extension != null) +{ + Console.WriteLine($"扩展名称: {extension.DisplayName}"); + Console.WriteLine($"版本: {extension.Version}"); + Console.WriteLine($"发布者: {extension.Publisher}"); + Console.WriteLine($"描述: {extension.Description}"); +} +else +{ + Console.WriteLine("未找到扩展"); +} +``` - private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - if (e.IsAllDownloadCompleted) - { - Console.WriteLine("所有更新下载成功!"); - } - else - { - Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); - } - } +#### 按平台筛选扩展 - private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); - } +```c# +var windowsExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Windows); - private void OnException(object sender, ExceptionEventArgs e) - { - Console.WriteLine($"更新异常:{e.Exception.Message}"); - // 将异常记录到文件或监控系统 - } +Console.WriteLine($"Windows 扩展: {windowsExtensions.Count} 个"); - private async Task PerformPreUpdateChecks() - { - // 检查系统要求 - Console.WriteLine("正在执行更新前检查..."); - - // 验证网络连接 - if (!await CheckNetworkConnection()) - { - Console.WriteLine("没有可用的网络连接!"); - return false; - } - - // 检查磁盘空间 - if (!HasSufficientDiskSpace(500 * 1024 * 1024)) - { - Console.WriteLine("磁盘空间不足!"); - return false; - } - - Console.WriteLine("更新前检查通过!"); - return true; - } +var linuxExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Linux); - private async Task AskUserForUpdatePermission() - { - Console.WriteLine("有新更新可用。立即更新?(y/n)"); - var response = Console.ReadLine(); - return response?.ToLower() == "y"; - } +Console.WriteLine($"Linux 扩展: {linuxExtensions.Count} 个"); +``` + + + +### 版本兼容性 + +GeneralUpdate.Extension 自动检查版本兼容性,确保扩展与主机应用程序版本匹配。 + +#### 兼容性规则 + +扩展必须满足以下条件才被视为兼容: +1. 扩展的 MinHostVersion ≤ 主机版本 +2. 扩展的 MaxHostVersion ≥ 主机版本 +3. 两个条件必须同时满足 + +#### 示例 - private async Task CheckNetworkConnection() +假设主机版本为 1.5.0: + +| 扩展 MinHostVersion | 扩展 MaxHostVersion | 结果 | +| ------------------- | ------------------- | -------- | +| 1.0.0 | 2.0.0 | ✓ 兼容 | +| 1.6.0 | 2.0.0 | ✗ 不兼容 | +| 1.0.0 | 1.4.0 | ✗ 不兼容 | +| 1.5.0 | 1.5.0 | ✓ 兼容 | + +#### 检查兼容性 + +```c# +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null) +{ + bool isCompatible = host.IsExtensionCompatible(extension); + + if (isCompatible) { - // 实现网络检查 - return true; + Console.WriteLine("✓ 扩展与当前版本兼容"); } - - private bool HasSufficientDiskSpace(long requiredBytes) + else { - // 实现磁盘空间检查 - return true; + Console.WriteLine("✗ 扩展与当前版本不兼容"); + Console.WriteLine($" 需要主机版本: {extension.MinHostVersion} - {extension.MaxHostVersion}"); + Console.WriteLine($" 当前主机版本: {options.HostVersion}"); } } ``` -### 最佳实践 +### 平台支持 -1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 +GeneralUpdate.Extension 支持多平台扩展,可以为不同操作系统提供不同的扩展版本。 -2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 +#### 平台标志 -3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 +```c# +// 单个平台 +TargetPlatform.Windows // 仅 Windows +TargetPlatform.Linux // 仅 Linux +TargetPlatform.MacOS // 仅 macOS + +// 多平台(使用位标志组合) +TargetPlatform.Windows | TargetPlatform.Linux // Windows 和 Linux +TargetPlatform.All // 所有平台 +``` -4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 +#### 自动平台检测 -5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 +系统自动检测当前运行平台,并筛选兼容的扩展: -6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 +```c# +// 查询时自动按平台过滤 +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows // 只返回支持 Windows 的扩展 +}; -7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 +var result = await host.QueryExtensionsAsync(query); +``` -8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 +#### 平台特定扩展 +```c# +// 示例:为不同平台设置扩展元数据 +var extension = new ExtensionMetadata +{ + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // 仅支持 Windows 和 Linux + SupportedPlatforms = TargetPlatform.Windows | TargetPlatform.Linux +}; +``` + + + +### 依赖关系解析 + +GeneralUpdate.Extension 自动处理扩展之间的依赖关系。 +#### 依赖关系特性 -### 常见用例 +1. **自动解析传递依赖**:如果 A 依赖 B,B 依赖 C,系统会自动识别并安装 C。 +2. **循环依赖检测**:自动检测并防止循环依赖。 +3. **正确的安装顺序**:按依赖顺序安装(先安装被依赖项)。 +4. **缺失依赖检查**:安装前检查所有依赖是否可用。 -#### 用例 1:环境验证 +#### 定义依赖关系 -更新前,验证是否满足所有先决条件: +在扩展元数据中使用逗号分隔的 GUID 列表: ```c# -private async Task ValidateEnvironment() +var extension = new ExtensionMetadata { - // 检查 .NET 运行时版本 - if (!IsRuntimeVersionSupported()) - return false; - - // 验证所需服务是否正在运行 - if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) - return false; - - // 检查文件权限 - if (!HasWritePermission(installPath)) - return false; - - return true; -} + Id = "550e8400-e29b-41d4-a716-446655440001", + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // 依赖两个其他扩展 + Dependencies = "550e8400-e29b-41d4-a716-446655440002,550e8400-e29b-41d4-a716-446655440003" +}; +``` + +#### 依赖自动处理 + +```c# +// UpdateExtensionAsync 自动处理依赖: +// 1. 识别所有依赖项 +// 2. 检查是否已安装 +// 3. 下载并安装缺失的依赖 +// 4. 按正确顺序安装 + +bool success = await host.UpdateExtensionAsync(extensionId); +// 系统会自动安装所有必需的依赖扩展 ``` -#### 用例 2:自定义更新通知 -在更新过程中向用户显示自定义通知: + +### 回滚机制 + +InstallExtensionAsync 支持自动回滚功能,在安装失败时恢复到之前的状态。 + +#### 工作原理 + +1. **创建备份**:安装前,系统备份现有扩展(如果存在)。 +2. **尝试安装**:执行扩展安装操作。 +3. **成功时**:删除备份文件。 +4. **失败时**:自动从备份恢复,撤销所有更改。 + +#### 使用回滚功能 ```c# -private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +bool success = await host.InstallExtensionAsync( + extensionPath: "./downloads/extension.zip", + rollbackOnFailure: true // 启用自动回滚 +); + +if (!success) { - // 显示系统托盘通知 - ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); - - // 更新应用程序标题 - UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); + Console.WriteLine("安装失败,但已自动回滚到之前的版本"); + Console.WriteLine("您的扩展目录保持不变"); } ``` -#### 用例 3:条件更新 +#### 最佳实践 + +- 生产环境中**始终启用回滚**(`rollbackOnFailure: true`) +- 确保有足够的磁盘空间用于备份 +- 监控备份目录(默认为 `/.backup`) + + + +### 自动更新设置 + +GeneralUpdate.Extension 支持扩展的自动更新功能。 + +#### 为单个扩展启用自动更新 + +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + +// 启用自动更新 +host.SetAutoUpdate(extensionId, true); + +// 禁用自动更新 +host.SetAutoUpdate(extensionId, false); + +// 检查状态 +bool autoUpdateEnabled = host.IsAutoUpdateEnabled(extensionId); +Console.WriteLine($"自动更新: {(autoUpdateEnabled ? "启用" : "禁用")}"); +``` + +#### 全局自动更新 -根据特定条件应用更新: +```c# +// 为所有扩展启用自动更新 +host.SetGlobalAutoUpdate(true); + +// 禁用全局自动更新 +host.SetGlobalAutoUpdate(false); +``` + + + +### 下载队列管理 + +下载队列管理器处理并发下载,提供状态跟踪和取消功能。 + +#### 队列特性 + +- 默认并发下载限制:3 个同时下载 +- 自动状态跟踪 +- 支持取消正在进行的下载 +- 支持断点续传(HTTP Range 请求) + +#### 下载状态 + +下载任务可能处于以下状态之一: + +```c# +public enum ExtensionUpdateStatus +{ + Queued, // 已加入队列,等待下载 + Updating, // 正在下载/更新 + UpdateSuccessful, // 下载/更新成功 + UpdateFailed // 下载/更新失败 +} +``` + +#### 监控下载 ```c# -private async Task ShouldApplyUpdate() +host.ExtensionUpdateStatusChanged += (sender, e) => { - // 检查是否在营业时间内 - var now = DateTime.Now; - if (now.Hour >= 9 && now.Hour <= 17) + Console.WriteLine($"扩展: {e.ExtensionName ?? e.ExtensionId}"); + Console.WriteLine($"状态: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) { - // 在营业时间推迟更新 - return false; + Console.WriteLine($"进度: {e.Progress}%"); } - // 检查是否有关键操作正在运行 - if (IsCriticalOperationInProgress()) + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - return false; + Console.WriteLine($"错误: {e.ErrorMessage}"); } - - return true; -} +}; ``` -### 故障排除 +### 服务器 API 要求 + +GeneralUpdate.Extension 需要服务器提供以下 API 端点: + +#### 查询扩展 + +``` +POST {ServerUrl}/extensions +Content-Type: application/json +Authorization: Bearer {BearerToken} + +请求体: ExtensionQueryDTO +响应: HttpResponseDTO> +``` + +#### 下载扩展 + +``` +GET {ServerUrl}/extensions/{id} +Authorization: Bearer {BearerToken} + +响应: 文件流(支持 HTTP Range 请求以实现断点续传) +``` + -#### 问题:自定义操作无限期地阻止更新 -**解决方案**:确保您的自定义操作具有超时和适当的错误处理: +### 完整使用示例 + +以下是一个完整的示例,展示了扩展管理的主要操作: ```c# -private async Task CustomOperationWithTimeout() +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; +using GeneralUpdate.Extension.Common.Enums; +using System; +using System.Threading.Tasks; + +public class ExtensionManagerExample { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - try + public static async Task Main() { - await YourAsyncOperation(cts.Token); - return true; + // 1. 初始化扩展主机 + var options = new ExtensionHostOptions + { + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" + }; + + var host = new GeneralExtensionHost(options); + + // 2. 订阅更新事件 + host.ExtensionUpdateStatusChanged += (sender, e) => + { + Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {e.ExtensionName}"); + Console.WriteLine($" 状态: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) + { + Console.WriteLine($" 进度: {e.Progress}%"); + } + + if (e.Status == ExtensionUpdateStatus.UpdateFailed) + { + Console.WriteLine($" 错误: {e.ErrorMessage}"); + } + }; + + // 3. 查询远程扩展 + Console.WriteLine("=== 查询远程扩展 ===\n"); + var query = new ExtensionQueryDTO + { + Platform = TargetPlatform.Windows, + HostVersion = options.HostVersion, + Status = true, + PageNumber = 1, + PageSize = 10 + }; + + var queryResult = await host.QueryExtensionsAsync(query); + if (queryResult.Success && queryResult.Data != null) + { + Console.WriteLine($"找到 {queryResult.Data.TotalCount} 个扩展:\n"); + + foreach (var ext in queryResult.Data.Items) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" 发布者: {ext.Publisher}"); + Console.WriteLine($" 兼容: {ext.IsCompatible}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); + Console.WriteLine(); + } + } + + // 4. 更新特定扩展 + Console.WriteLine("\n=== 更新扩展 ===\n"); + string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + + bool updateSuccess = await host.UpdateExtensionAsync(extensionId); + if (updateSuccess) + { + Console.WriteLine("✓ 扩展更新成功!"); + } + else + { + Console.WriteLine("✗ 扩展更新失败"); + } + + // 5. 列出已安装的扩展 + Console.WriteLine("\n=== 已安装的扩展 ===\n"); + var installed = host.ExtensionCatalog.GetInstalledExtensions(); + Console.WriteLine($"总共 {installed.Count} 个已安装扩展:\n"); + + foreach (var ext in installed) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" 状态: {(ext.Status == true ? "启用" : "禁用")}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); + + // 检查兼容性 + bool compatible = host.IsExtensionCompatible(ext); + Console.WriteLine($" 兼容: {(compatible ? "是" : "否")}"); + Console.WriteLine(); + } + + // 6. 配置自动更新 + Console.WriteLine("\n=== 配置自动更新 ===\n"); + host.SetGlobalAutoUpdate(true); + Console.WriteLine("✓ 已启用全局自动更新"); + + // 为特定扩展禁用自动更新 + host.SetAutoUpdate(extensionId, false); + Console.WriteLine($"✓ 已为扩展 {extensionId} 禁用自动更新"); } - catch (OperationCanceledException) +} +``` + + + +### 最佳实践 + +1. **始终订阅事件**:在执行任何操作前订阅 `ExtensionUpdateStatusChanged` 事件,以监控进度和错误。 + +2. **使用回滚功能**:生产环境中安装扩展时始终启用 `rollbackOnFailure: true`。 + +3. **检查兼容性**:安装前使用 `IsExtensionCompatible()` 检查扩展兼容性。 + +4. **合理分页**:查询扩展时使用适当的 `PageSize`(建议 10-50),避免一次加载过多数据。 + +5. **安全存储令牌**:Bearer Token 应安全存储,不要硬编码在代码中。 + +6. **监控磁盘空间**:确保有足够的磁盘空间用于下载和备份。 + +7. **验证扩展元数据**:安装前验证扩展的元数据完整性和哈希值。 + +8. **处理网络错误**:实现重试逻辑处理网络临时故障。 + +9. **日志记录**:记录所有扩展操作和错误,便于故障排查。 + +10. **定期清理**:定期清理下载缓存和备份目录,释放磁盘空间。 + + + +### 故障排除 + +#### 问题:扩展下载失败 + +**可能原因**: +- 网络连接问题 +- 服务器不可用 +- Bearer Token 无效或过期 + +**解决方案**: +```c# +// 检查事件参数中的错误信息 +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - Console.WriteLine("操作超时"); - return false; + Console.WriteLine($"下载失败: {e.ErrorMessage}"); + // 记录详细信息用于调试 } +}; + +// 实现重试逻辑 +int maxRetries = 3; +for (int i = 0; i < maxRetries; i++) +{ + bool success = await host.UpdateExtensionAsync(extensionId); + if (success) break; + + await Task.Delay(TimeSpan.FromSeconds(5)); // 等待后重试 } ``` -#### 问题:事件未触发 - -**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: +#### 问题:扩展显示不兼容 +**解决方案**: ```c# -// 正确的顺序 -await new GeneralClientBootstrap() - .AddListenerException(OnException) // 先注册 - .SetConfig(configinfo) - .LaunchAsync(); // 最后启动 +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null && !host.IsExtensionCompatible(extension)) +{ + Console.WriteLine("扩展版本要求:"); + Console.WriteLine($" 最低主机版本: {extension.MinHostVersion}"); + Console.WriteLine($" 最高主机版本: {extension.MaxHostVersion}"); + Console.WriteLine($" 当前主机版本: {options.HostVersion}"); + + // 检查是否有兼容的版本可用 + var query = new ExtensionQueryDTO + { + Name = extension.Name, + HostVersion = options.HostVersion + }; + var result = await host.QueryExtensionsAsync(query); + // 检查是否有兼容版本 +} ``` -#### 问题:跳过选项不起作用 +#### 问题:安装失败但未回滚 + +**解决方案**: +确保启用了回滚选项: +```c# +bool success = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // 必须设置为 true +); +``` -**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 +检查备份目录权限: +```c# +string backupDir = Path.Combine(options.ExtensionsDirectory, ".backup"); +if (!Directory.Exists(backupDir)) +{ + Directory.CreateDirectory(backupDir); +} +// 确保有写入权限 +``` ### 适用于 -| 产品 | 版本 | -| -------------- | ---------------- | -| .NET | 5, 6, 7, 8, 9, 10| -| .NET Framework | 4.6.1 | -| .NET Standard | 2.0 | -| .NET Core | 2.0 | +| 产品 | 版本 | +| -------------- | ------------------------------------- | +| .NET Standard | 2.0 | +| .NET Framework | 4.6.1+ | +| .NET Core | 2.0+ | +| .NET | 5, 6, 7, 8, 9, 10 | +| Mono | 5.4+ | +| Xamarin.iOS | 10.14+ | +| Xamarin.Android| 8.0+ | ### 另请参阅 -- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主客户端更新组件 - [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 +- [GitHub 源代码](https://github.com/GeneralLibrary/GeneralUpdate/tree/master/src/c%23/GeneralUpdate.Extension) - 完整源代码和更多示例 - [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门 diff --git a/website/docs/doc/GeneralUpdate.Extension.md.backup b/website/docs/doc/GeneralUpdate.Extension.md.backup new file mode 100644 index 0000000..d45f148 --- /dev/null +++ b/website/docs/doc/GeneralUpdate.Extension.md.backup @@ -0,0 +1,528 @@ +--- +sidebar_position: 12 +--- + +### 定义 + +命名空间:GeneralUpdate.Extension + +程序集:GeneralUpdate.Extension.dll + +**NuGet 包**:GeneralUpdate.Extension + + + +GeneralUpdate.Extension 是一个受 VS Code 启发的 .NET 应用程序扩展管理系统。它提供完整的插件/扩展管理功能,包括扩展下载、安装、更新、版本兼容性检查、平台支持、依赖关系解析和回滚机制。 + +```c# +public class GeneralExtensionHost : IExtensionHost +{ + public IExtensionCatalog ExtensionCatalog { get; } + public event EventHandler? ExtensionUpdateStatusChanged; +} +``` + + + +### 快速开始 + +最基本的使用示例,初始化扩展主机并查询远程扩展 [[查看完整示例]](https://github.com/GeneralLibrary/GeneralUpdate/blob/master/src/c%23/GeneralUpdate.Extension/Examples/ExtensionExample.cs)。 + +```c# +var configinfo = new Configinfo +{ + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + AppName = "YourApp.exe", + ClientVersion = "1.0.0.0", + // ... 其他配置 +}; + +await new GeneralClientBootstrap() + // 添加自定义事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(CheckEnvironment) + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 核心扩展能力 + +GeneralUpdate.Extension 提供以下扩展功能。 + +#### 事件监听器 + +| 方法 | 说明 | +| -------------------------------------- | ------------------------------------------------------------ | +| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | +| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | +| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | +| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | +| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | + +#### 自定义操作 + +| 方法 | 说明 | +| --------------------- | ------------------------------------------------------------ | +| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | +| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | +| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | + +#### 配置选项 + +| 方法 | 说明 | +| ------------- | ------------------------------------------------------------ | +| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | +| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | +| GetOption() | 检索当前更新配置设置以供检查或修改。 | +| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | + + + +### 事件参数 + +#### MultiDownloadStatisticsEventArgs + +提供详细的下载进度信息。 + +| 属性 | 类型 | 说明 | +| ------------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 当前正在下载的版本 | +| Speed | string | 下载速度(例如 "2.5 MB/s") | +| Remaining | string | 预计剩余时间(例如 "00:02:30") | +| BytesReceived | long | 到目前为止已下载的字节数 | +| TotalBytesToReceive| long | 下载的总大小(字节) | +| ProgressPercentage | double | 下载进度百分比(0-100) | + +#### MultiDownloadCompletedEventArgs + +指示单个更新包的完成状态。 + +| 属性 | 类型 | 说明 | +| ------------ | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 已完成下载的版本信息 | +| IsComplated | bool | 下载是否成功完成 | + +#### MultiAllDownloadCompletedEventArgs + +所有下载任务的摘要。 + +| 属性 | 类型 | 说明 | +| ---------------------- | --------------- | ------------------------------------------ | +| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| +| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | + +#### MultiDownloadErrorEventArgs + +下载失败的错误信息。 + +| 属性 | 类型 | 说明 | +| --------- | ----------- | ---------------------------------------------------- | +| Version | VersionInfo | 遇到错误的版本 | +| Exception | Exception | 下载期间发生的异常 | + +#### ExceptionEventArgs + +全局异常信息。 + +| 属性 | 类型 | 说明 | +| --------- | --------- | ---------------------------------------------------- | +| Exception | Exception | 更新过程中发生的异常 | + + + +### 自定义操作示例 + +以下是使用自定义操作的完整示例: + +```c# +private async Task CheckEnvironment() +{ + try + { + // 检查是否安装了所需的依赖项 + if (!IsDependencyInstalled("SomeLibrary")) + { + Console.WriteLine("缺少必需的依赖项!"); + return false; // 取消更新 + } + + // 验证磁盘空间 + var requiredSpace = 500 * 1024 * 1024; // 500 MB + if (GetAvailableDiskSpace() < requiredSpace) + { + Console.WriteLine("磁盘空间不足!"); + return false; // 取消更新 + } + + // 创建关键文件的备份 + await BackupUserData(); + + return true; // 继续更新 + } + catch (Exception ex) + { + Console.WriteLine($"环境检查失败:{ex.Message}"); + return false; + } +} + +// 注册自定义操作 +await new GeneralClientBootstrap() + .AddCustomOption(CheckEnvironment) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 跳过选项示例 + +允许用户跳过非强制更新: + +```c# +private async Task ShowSkipDialog() +{ + // 向用户显示自定义对话框 + var result = await ShowUpdateDialog( + "有新的更新可用!", + "您想现在更新吗?", + new[] { "立即更新", "跳过" } + ); + + return result == "立即更新"; +} + +// 注册跳过选项处理程序 +await new GeneralClientBootstrap() + .SetCustomSkipOption(ShowSkipDialog) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 黑名单配置 + +从更新中排除特定文件或格式: + +```c# +var configinfo = new Configinfo +{ + // ... 其他配置 + BlackFiles = new List<string> + { + "userconfig.json", + "license.dat", + "custom.db" + }, + BlackFormats = new List<string> + { + ".log", + ".temp", + ".cache" + }, + BlackDirectories = new List<string> + { + "UserData", + "Plugins\\Custom" + } +}; + +// 或使用 SetBlacklist 方法 +await new GeneralClientBootstrap() + .SetBlacklist(blackFiles: new List<string> { "config.ini" }) + .SetConfig(configinfo) + .LaunchAsync(); +``` + + + +### 完整扩展示例 + +以下是使用多个扩展功能的综合示例: + +```c# +using GeneralUpdate.ClientCore; +using GeneralUpdate.Common.Shared.Object; + +public class UpdateManager +{ + public async Task StartUpdateWithExtensions() + { + var configinfo = new Configinfo + { + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", + AppName = "MyApp.exe", + MainAppName = "MyApp.exe", + ClientVersion = "1.0.0.0", + ProductId = "your-product-id", + AppSecretKey = "your-secret-key", + InstallPath = AppDomain.CurrentDomain.BaseDirectory, + BlackFiles = new List<string> { "userdata.db" } + }; + + try + { + await new GeneralClientBootstrap() + // 注册事件监听器 + .AddListenerMultiDownloadStatistics(OnDownloadProgress) + .AddListenerMultiDownloadCompleted(OnDownloadCompleted) + .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) + .AddListenerMultiDownloadError(OnDownloadError) + .AddListenerException(OnException) + // 添加自定义操作 + .AddCustomOption(PerformPreUpdateChecks) + .SetCustomSkipOption(AskUserForUpdatePermission) + // 配置选项 + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 120) + .Option(UpdateOption.Patch, true) + .Option(UpdateOption.BackUp, true) + // 启动更新 + .LaunchAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"更新失败:{ex.Message}"); + } + } + + private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); + // 在此更新 UI 进度条 + } + + private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + var version = e.Version as VersionInfo; + if (e.IsComplated) + { + Console.WriteLine($"版本 {version.Version} 下载成功!"); + } + else + { + Console.WriteLine($"版本 {version.Version} 下载失败!"); + } + } + + private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + if (e.IsAllDownloadCompleted) + { + Console.WriteLine("所有更新下载成功!"); + } + else + { + Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); + } + } + + private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + var version = e.Version as VersionInfo; + Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); + } + + private void OnException(object sender, ExceptionEventArgs e) + { + Console.WriteLine($"更新异常:{e.Exception.Message}"); + // 将异常记录到文件或监控系统 + } + + private async Task PerformPreUpdateChecks() + { + // 检查系统要求 + Console.WriteLine("正在执行更新前检查..."); + + // 验证网络连接 + if (!await CheckNetworkConnection()) + { + Console.WriteLine("没有可用的网络连接!"); + return false; + } + + // 检查磁盘空间 + if (!HasSufficientDiskSpace(500 * 1024 * 1024)) + { + Console.WriteLine("磁盘空间不足!"); + return false; + } + + Console.WriteLine("更新前检查通过!"); + return true; + } + + private async Task AskUserForUpdatePermission() + { + Console.WriteLine("有新更新可用。立即更新?(y/n)"); + var response = Console.ReadLine(); + return response?.ToLower() == "y"; + } + + private async Task CheckNetworkConnection() + { + // 实现网络检查 + return true; + } + + private bool HasSufficientDiskSpace(long requiredBytes) + { + // 实现磁盘空间检查 + return true; + } +} +``` + + + +### 最佳实践 + +1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 + +2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 + +3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 + +4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 + +5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 + +6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 + +7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 + +8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 + + + +### 常见用例 + +#### 用例 1:环境验证 + +更新前,验证是否满足所有先决条件: + +```c# +private async Task ValidateEnvironment() +{ + // 检查 .NET 运行时版本 + if (!IsRuntimeVersionSupported()) + return false; + + // 验证所需服务是否正在运行 + if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) + return false; + + // 检查文件权限 + if (!HasWritePermission(installPath)) + return false; + + return true; +} +``` + +#### 用例 2:自定义更新通知 + +在更新过程中向用户显示自定义通知: + +```c# +private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +{ + // 显示系统托盘通知 + ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); + + // 更新应用程序标题 + UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); +} +``` + +#### 用例 3:条件更新 + +根据特定条件应用更新: + +```c# +private async Task ShouldApplyUpdate() +{ + // 检查是否在营业时间内 + var now = DateTime.Now; + if (now.Hour >= 9 && now.Hour <= 17) + { + // 在营业时间推迟更新 + return false; + } + + // 检查是否有关键操作正在运行 + if (IsCriticalOperationInProgress()) + { + return false; + } + + return true; +} +``` + + + +### 故障排除 + +#### 问题:自定义操作无限期地阻止更新 + +**解决方案**:确保您的自定义操作具有超时和适当的错误处理: + +```c# +private async Task CustomOperationWithTimeout() +{ + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + try + { + await YourAsyncOperation(cts.Token); + return true; + } + catch (OperationCanceledException) + { + Console.WriteLine("操作超时"); + return false; + } +} +``` + +#### 问题:事件未触发 + +**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: + +```c# +// 正确的顺序 +await new GeneralClientBootstrap() + .AddListenerException(OnException) // 先注册 + .SetConfig(configinfo) + .LaunchAsync(); // 最后启动 +``` + +#### 问题:跳过选项不起作用 + +**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 + + + +### 适用于 + +| 产品 | 版本 | +| -------------- | ---------------- | +| .NET | 5, 6, 7, 8, 9, 10| +| .NET Framework | 4.6.1 | +| .NET Standard | 2.0 | +| .NET Core | 2.0 | + +### 另请参阅 + +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 +- [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 +- [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门 diff --git a/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md index 860d31d..92c6614 100644 --- a/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md +++ b/website/i18n/en/docusaurus-plugin-content-docs/current/docs/doc/GeneralUpdate.Extension.md @@ -6,518 +6,873 @@ sidebar_position: 12 Namespace: GeneralUpdate.Extension -Assembly: GeneralUpdate.ClientCore.dll, GeneralUpdate.Core.dll +Assembly: GeneralUpdate.Extension.dll +**NuGet Package**: GeneralUpdate.Extension -GeneralUpdate.Extension provides a comprehensive extensibility framework that allows developers to customize and extend the update process. It enables you to inject custom logic, handle events, and tailor the update behavior to meet specific application requirements. This component is essential for developers who need fine-grained control over the update workflow. + +GeneralUpdate.Extension is a VS Code-inspired extension management system for .NET applications. It provides complete plugin/extension management capabilities, including extension download, installation, updates, version compatibility checking, platform support, dependency resolution, and rollback mechanisms. ```c# -// Extension methods are available through GeneralClientBootstrap and GeneralUpdateBootstrap -public class GeneralClientBootstrap : AbstractBootstrap +public class GeneralExtensionHost : IExtensionHost +{ + public IExtensionCatalog ExtensionCatalog { get; } + public event EventHandler? ExtensionUpdateStatusChanged; +} ``` -### Example +### Quick Start -The Extension framework provides various ways to extend functionality [[View Example]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 +The most basic usage example: initialize the extension host and query remote extensions [[View Full Example]](https://github.com/GeneralLibrary/GeneralUpdate/blob/master/src/c%23/GeneralUpdate.Extension/Examples/ExtensionExample.cs). ```c# -var configinfo = new Configinfo +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; + +// 1. Initialize extension host +var options = new ExtensionHostOptions { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - AppName = "YourApp.exe", - ClientVersion = "1.0.0.0", - // ... other configuration + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", // Optional: for authentication + HostVersion = "1.0.0", // Your application version + ExtensionsDirectory = "./extensions" // Extension installation directory }; -await new GeneralClientBootstrap() - // Add custom event listeners - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerException(OnException) - // Add custom operations - .AddCustomOption(CheckEnvironment) - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); -``` +var host = new GeneralExtensionHost(options); +// 2. Subscribe to update events +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + Console.WriteLine($"Extension: {e.ExtensionName}"); + Console.WriteLine($"Status: {e.Status}"); + Console.WriteLine($"Progress: {e.Progress}%"); +}; +// 3. Query available extensions +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows, + HostVersion = "1.0.0", + PageNumber = 1, + PageSize = 20 +}; -### Core Extension Capabilities +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + foreach (var ext in result.Data.Items) + { + Console.WriteLine($"{ext.DisplayName} v{ext.Version}"); + Console.WriteLine($"Compatible: {ext.IsCompatible}"); + } +} + +// 4. Update extension +bool success = await host.UpdateExtensionAsync("extension-guid"); +``` -GeneralUpdate.Extension provides the following extensibility features. -#### Event Listeners -| Method | Description | -| -------------------------------------- | ------------------------------------------------------------ | -| AddListenerMultiDownloadStatistics() | Subscribe to download progress notifications including speed, remaining time, bytes received, and progress percentage. Ideal for displaying real-time download information to users. | -| AddListenerMultiDownloadCompleted() | Event triggered when a single update package download completes (successfully or unsuccessfully). Allows you to handle per-version download completion. | -| AddListenerMultiAllDownloadCompleted() | Notification when all update packages have been downloaded. This is the final checkpoint before the update installation begins. | -| AddListenerMultiDownloadError() | Listen for download errors for each version. Provides exception details to help diagnose network or server issues. | -| AddListenerException() | Global exception handler that catches any errors during the entire update process. Critical for error logging and user notification. | +### Core Features -#### Custom Operations +GeneralUpdate.Extension provides the following core features. -| Method | Description | -| --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | Inject custom asynchronous operations into the update workflow. Executed before the update starts. Perfect for environment checks, pre-update backups, or any preparatory tasks. Returns a Task<bool> where false cancels the update. | -| SetCustomSkipOption() | Allows users to decide whether to skip non-mandatory updates. Provides a way to show custom UI for user consent. Only effective when the server doesn't enforce mandatory updates. | -| SetBlacklist() | Define files or file formats that should never be updated. Useful for preserving user data, configuration files, or third-party dependencies. | +#### Extension Host (GeneralExtensionHost) -#### Configuration Options +The main extension management container that provides the entry point for all extension operations. -| Method | Description | -| ------------- | ------------------------------------------------------------ | -| Option() | Configure update behavior such as timeout duration, encoding format, patch enablement, and backup settings. Provides granular control over the update process. | -| Config() | Set core update parameters including server URLs, version information, authentication keys, and installation paths. | -| GetOption() | Retrieve current update configuration settings for inspection or modification. | -| SetConfig() | Apply a complete Configinfo object to configure the update client. | +| Method | Description | +| --------------------------- | ------------------------------------------------------------ | +| QueryExtensionsAsync() | Query available extensions from remote server. Supports filtering by name, platform, version, status, etc. | +| UpdateExtensionAsync() | Update specified extension. Automatically handles download, compatibility check, platform validation, and installation. | +| DownloadExtensionAsync() | Download extension package to specified path. Supports resumable download (HTTP Range requests). | +| InstallExtensionAsync() | Install extension package. Supports automatic rollback functionality, restoring to previous state on installation failure. | +| IsExtensionCompatible() | Check if extension is compatible with current host version. | +| SetAutoUpdate() | Enable/disable auto-update for specified extension. | +| SetGlobalAutoUpdate() | Enable/disable global auto-update for all extensions. | +| IsAutoUpdateEnabled() | Check if auto-update is enabled for extension. | +#### Extension Catalog (ExtensionCatalog) +Manages the catalog of locally installed extensions with persistent storage using JSON files. -### Event Arguments +| Method | Description | +| ----------------------------------- | ------------------------------------------------------------ | +| LoadInstalledExtensions() | Load list of installed extensions from catalog.json. | +| GetInstalledExtensions() | Get list of all installed extensions. | +| GetInstalledExtensionById() | Get specific extension by extension ID (GUID). | +| GetInstalledExtensionsByPlatform() | Get list of extensions that support specified platform. | +| AddOrUpdateExtension() | Add new extension or update existing extension metadata. | +| RemoveExtension() | Remove extension record from catalog. | +| SaveCatalog() | Save catalog to JSON file. | -#### MultiDownloadStatisticsEventArgs +#### Download Queue (DownloadQueueManager) -Provides detailed download progress information. +Manages extension download queue with support for concurrent downloads and status tracking. -| Property | Type | Description | -| ------------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | Current version being downloaded | -| Speed | string | Download speed (e.g., "2.5 MB/s") | -| Remaining | string | Estimated time remaining (e.g., "00:02:30") | -| BytesReceived | long | Number of bytes downloaded so far | -| TotalBytesToReceive| long | Total size of the download in bytes | -| ProgressPercentage | double | Download progress as a percentage (0-100) | +| Method | Description | +| ----------------------- | ------------------------------------------------------------ | +| EnqueueDownload() | Add download task to queue. Task will be automatically queued and processed. | +| GetDownloadStatus() | Get current status of specified download task. | +| CancelDownload() | Cancel ongoing download task. | -#### MultiDownloadCompletedEventArgs +Download queue features: +- Default concurrent download limit: 3 +- Automatic status tracking (Queued, Downloading, Completed, Failed) +- Supports resumable downloads +- Event notification for download status changes -Indicates completion status of a single update package. -| Property | Type | Description | -| ------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | Version information for the completed download | -| IsComplated | bool | Whether the download completed successfully | -#### MultiAllDownloadCompletedEventArgs +### Extension Metadata -Summary of all download tasks. +Each extension must include the following metadata information: -| Property | Type | Description | -| ---------------------- | --------------- | ------------------------------------------ | -| IsAllDownloadCompleted | bool | Whether all downloads completed successfully| -| FailedVersions | List<VersionInfo>| List of versions that failed to download | +| Property | Type | Description | +| ------------------ | --------------- | ---------------------------------------------------- | +| Id | string | Extension unique identifier (GUID format) | +| Name | string | Extension name (unique, lowercase, no spaces) | +| DisplayName | string | Human-readable display name | +| Version | string | Semantic version number (e.g., "1.2.3") | +| Publisher | string | Publisher identifier | +| Description | string | Extension description | +| SupportedPlatforms | TargetPlatform | Supported platform flags (Windows/Linux/MacOS/All) | +| MinHostVersion | string | Minimum compatible host version | +| MaxHostVersion | string | Maximum compatible host version | +| Dependencies | string | List of dependent extension IDs (comma-separated) | +| Format | string | File format (.dll, .zip, etc.) | +| Categories | string | Extension categories (comma-separated) | +| IsPreRelease | bool | Whether this is a pre-release version | +| License | string | License identifier (e.g., "MIT", "Apache-2.0") | +| FileSize | long | File size (bytes) | +| Hash | string | File hash value (SHA256) | +| DownloadUrl | string | Download URL | -#### MultiDownloadErrorEventArgs -Error information for failed downloads. -| Property | Type | Description | -| --------- | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | Version that encountered an error | -| Exception | Exception | The exception that occurred during download | +### Initialization and Configuration -#### ExceptionEventArgs +#### Basic Initialization -Global exception information. +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" +}; -| Property | Type | Description | -| --------- | --------- | ---------------------------------------------------- | -| Exception | Exception | Exception that occurred during the update process | +var host = new GeneralExtensionHost(options); +``` +#### Initialization with Authentication +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions", + CatalogPath = "./extensions/catalog.json" // Optional: custom catalog file path +}; -### Custom Operation Example +var host = new GeneralExtensionHost(options); +``` -Here's a complete example showing how to use custom operations: +#### Subscribing to Events ```c# -private async Task CheckEnvironment() +host.ExtensionUpdateStatusChanged += (sender, e) => { - try + switch (e.Status) { - // Check if required dependencies are installed - if (!IsDependencyInstalled("SomeLibrary")) - { - Console.WriteLine("Required dependency is missing!"); - return false; // Cancel update - } - - // Verify disk space - var requiredSpace = 500 * 1024 * 1024; // 500 MB - if (GetAvailableDiskSpace() < requiredSpace) - { - Console.WriteLine("Insufficient disk space!"); - return false; // Cancel update - } - - // Create backup of critical files - await BackupUserData(); - - return true; // Proceed with update + case ExtensionUpdateStatus.Queued: + Console.WriteLine($"{e.ExtensionName} added to queue"); + break; + case ExtensionUpdateStatus.Updating: + Console.WriteLine($"{e.ExtensionName} updating... {e.Progress}%"); + break; + case ExtensionUpdateStatus.UpdateSuccessful: + Console.WriteLine($"{e.ExtensionName} update successful!"); + break; + case ExtensionUpdateStatus.UpdateFailed: + Console.WriteLine($"{e.ExtensionName} update failed: {e.ErrorMessage}"); + break; } - catch (Exception ex) +}; +``` + + + +### Querying Remote Extensions + +#### Basic Query + +```c# +var query = new ExtensionQueryDTO +{ + PageNumber = 1, + PageSize = 20 +}; + +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + Console.WriteLine($"Found {result.Data.TotalCount} extensions"); + foreach (var ext in result.Data.Items) { - Console.WriteLine($"Environment check failed: {ex.Message}"); - return false; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" Publisher: {ext.Publisher}"); + Console.WriteLine($" Compatible: {ext.IsCompatible}"); } } +``` -// Register the custom operation -await new GeneralClientBootstrap() - .AddCustomOption(CheckEnvironment) - .SetConfig(configinfo) - .LaunchAsync(); +#### Advanced Query (with Filters) + +```c# +var query = new ExtensionQueryDTO +{ + Name = "my-extension", // Search by name + Platform = TargetPlatform.Windows, // Only query Windows extensions + HostVersion = "1.0.0", // Check compatibility with this version + Status = true, // Only query enabled extensions + BeginDate = DateTime.Now.AddMonths(-1), // Extensions from last month + EndDate = DateTime.Now, + PageNumber = 1, + PageSize = 50 +}; + +var result = await host.QueryExtensionsAsync(query); ``` -### Skip Option Example +### Installing and Updating Extensions + +#### Auto-Update Extension -Allow users to skip non-mandatory updates: +The simplest way, completing all operations with one call: ```c# -private async Task ShowSkipDialog() +// UpdateExtensionAsync automatically performs the following steps: +// 1. Query extension information from server +// 2. Check version compatibility +// 3. Check platform support +// 4. Download extension package +// 5. Install extension +// 6. Update local catalog + +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +bool success = await host.UpdateExtensionAsync(extensionId); + +if (success) { - // Show a custom dialog to the user - var result = await ShowUpdateDialog( - "A new update is available!", - "Would you like to update now?", - new[] { "Update Now", "Skip" } - ); - - return result == "Update Now"; + Console.WriteLine("Extension updated successfully!"); +} +else +{ + Console.WriteLine("Extension update failed, check events for details"); } - -// Register the skip option handler -await new GeneralClientBootstrap() - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); ``` +#### Manual Download and Install +Step-by-step control of the installation process: -### Blacklist Configuration +```c# +// Step 1: Download extension +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +string savePath = "./downloads/my-extension.zip"; -Exclude specific files or formats from updates: +bool downloaded = await host.DownloadExtensionAsync(extensionId, savePath); -```c# -var configinfo = new Configinfo +if (downloaded) { - // ... other configuration - BlackFiles = new List<string> - { - "userconfig.json", - "license.dat", - "custom.db" - }, - BlackFormats = new List<string> - { - ".log", - ".temp", - ".cache" - }, - BlackDirectories = new List<string> + // Step 2: Install extension (with rollback functionality) + bool installed = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // Automatic rollback on failure + ); + + if (installed) { - "UserData", - "Plugins\\Custom" + Console.WriteLine("Extension installed successfully!"); } -}; - -// Or use the SetBlacklist method -await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List<string> { "config.ini" }) - .SetConfig(configinfo) - .LaunchAsync(); + else + { + Console.WriteLine("Installation failed, automatically rolled back"); + } +} ``` -### Complete Extension Example +### Managing Installed Extensions -Here's a comprehensive example using multiple extension features: +#### List All Extensions ```c# -using GeneralUpdate.ClientCore; -using GeneralUpdate.Common.Shared.Object; +var extensions = host.ExtensionCatalog.GetInstalledExtensions(); -public class UpdateManager +Console.WriteLine($"{extensions.Count} extensions installed:"); +foreach (var ext in extensions) { - public async Task StartUpdateWithExtensions() - { - var configinfo = new Configinfo - { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", - AppName = "MyApp.exe", - MainAppName = "MyApp.exe", - ClientVersion = "1.0.0.0", - ProductId = "your-product-id", - AppSecretKey = "your-secret-key", - InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List<string> { "userdata.db" } - }; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" Status: {(ext.Status == true ? "Enabled" : "Disabled")}"); + Console.WriteLine($" Platform: {ext.SupportedPlatforms}"); +} +``` - try - { - await new GeneralClientBootstrap() - // Register event listeners - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) - .AddListenerMultiDownloadError(OnDownloadError) - .AddListenerException(OnException) - // Add custom operations - .AddCustomOption(PerformPreUpdateChecks) - .SetCustomSkipOption(AskUserForUpdatePermission) - // Configure options - .SetConfig(configinfo) - .Option(UpdateOption.DownloadTimeOut, 120) - .Option(UpdateOption.Patch, true) - .Option(UpdateOption.BackUp, true) - // Launch the update - .LaunchAsync(); - } - catch (Exception ex) - { - Console.WriteLine($"Update failed: {ex.Message}"); - } - } +#### Get Specific Extension - private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"Downloading {version.Version}: {e.ProgressPercentage:F2}% at {e.Speed}"); - // Update UI progress bar here - } +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); - private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var version = e.Version as VersionInfo; - if (e.IsComplated) - { - Console.WriteLine($"Version {version.Version} downloaded successfully!"); - } - else - { - Console.WriteLine($"Failed to download version {version.Version}"); - } - } +if (extension != null) +{ + Console.WriteLine($"Extension Name: {extension.DisplayName}"); + Console.WriteLine($"Version: {extension.Version}"); + Console.WriteLine($"Publisher: {extension.Publisher}"); + Console.WriteLine($"Description: {extension.Description}"); +} +else +{ + Console.WriteLine("Extension not found"); +} +``` - private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - if (e.IsAllDownloadCompleted) - { - Console.WriteLine("All updates downloaded successfully!"); - } - else - { - Console.WriteLine($"Update failed. {e.FailedVersions.Count} versions failed."); - } - } +#### Filter Extensions by Platform - private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"Error downloading {version.Version}: {e.Exception.Message}"); - } +```c# +var windowsExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Windows); - private void OnException(object sender, ExceptionEventArgs e) - { - Console.WriteLine($"Update exception: {e.Exception.Message}"); - // Log exception to file or monitoring system - } +Console.WriteLine($"Windows extensions: {windowsExtensions.Count}"); - private async Task PerformPreUpdateChecks() - { - // Check system requirements - Console.WriteLine("Performing pre-update checks..."); - - // Verify network connectivity - if (!await CheckNetworkConnection()) - { - Console.WriteLine("No network connection available!"); - return false; - } - - // Check disk space - if (!HasSufficientDiskSpace(500 * 1024 * 1024)) - { - Console.WriteLine("Insufficient disk space!"); - return false; - } - - Console.WriteLine("Pre-update checks passed!"); - return true; - } +var linuxExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Linux); - private async Task AskUserForUpdatePermission() - { - Console.WriteLine("New update available. Update now? (y/n)"); - var response = Console.ReadLine(); - return response?.ToLower() == "y"; - } +Console.WriteLine($"Linux extensions: {linuxExtensions.Count}"); +``` + + + +### Version Compatibility + +GeneralUpdate.Extension automatically checks version compatibility to ensure extensions match the host application version. + +#### Compatibility Rules + +An extension is considered compatible if it meets the following conditions: +1. Extension's MinHostVersion ≤ Host version +2. Extension's MaxHostVersion ≥ Host version +3. Both conditions must be satisfied simultaneously + +#### Example - private async Task CheckNetworkConnection() +Assuming host version is 1.5.0: + +| Extension MinHostVersion | Extension MaxHostVersion | Result | +| ------------------------ | ------------------------ | --------------- | +| 1.0.0 | 2.0.0 | ✓ Compatible | +| 1.6.0 | 2.0.0 | ✗ Incompatible | +| 1.0.0 | 1.4.0 | ✗ Incompatible | +| 1.5.0 | 1.5.0 | ✓ Compatible | + +#### Checking Compatibility + +```c# +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null) +{ + bool isCompatible = host.IsExtensionCompatible(extension); + + if (isCompatible) { - // Implement network check - return true; + Console.WriteLine("✓ Extension is compatible with current version"); } - - private bool HasSufficientDiskSpace(long requiredBytes) + else { - // Implement disk space check - return true; + Console.WriteLine("✗ Extension is not compatible with current version"); + Console.WriteLine($" Required host version: {extension.MinHostVersion} - {extension.MaxHostVersion}"); + Console.WriteLine($" Current host version: {options.HostVersion}"); } } ``` -### Best Practices +### Platform Support -1. **Always handle exceptions**: Use `AddListenerException()` to catch and log all update errors. +GeneralUpdate.Extension supports multi-platform extensions, allowing different extension versions for different operating systems. -2. **Validate environment before updates**: Use `AddCustomOption()` to check system requirements, dependencies, and available disk space. +#### Platform Flags -3. **Provide user feedback**: Subscribe to download progress events to show real-time updates to users. +```c# +// Single platform +TargetPlatform.Windows // Windows only +TargetPlatform.Linux // Linux only +TargetPlatform.MacOS // macOS only + +// Multiple platforms (using bitwise flag combination) +TargetPlatform.Windows | TargetPlatform.Linux // Windows and Linux +TargetPlatform.All // All platforms +``` -4. **Protect user data**: Use blacklist features to exclude user configuration files and data from updates. +#### Automatic Platform Detection -5. **Implement retry logic**: In error handlers, implement intelligent retry mechanisms for transient failures. +The system automatically detects the current running platform and filters compatible extensions: -6. **Test custom operations**: Thoroughly test all custom operations to ensure they don't block legitimate updates. +```c# +// Automatically filter by platform when querying +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows // Only return extensions that support Windows +}; -7. **Use appropriate timeouts**: Set reasonable timeout values based on expected file sizes and network conditions. +var result = await host.QueryExtensionsAsync(query); +``` -8. **Log everything**: Maintain detailed logs of the update process for troubleshooting. +#### Platform-Specific Extensions +```c# +// Example: Set extension metadata for different platforms +var extension = new ExtensionMetadata +{ + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // Only supports Windows and Linux + SupportedPlatforms = TargetPlatform.Windows | TargetPlatform.Linux +}; +``` + + + +### Dependency Resolution + +GeneralUpdate.Extension automatically handles dependencies between extensions. +#### Dependency Features -### Common Use Cases +1. **Automatic transitive dependency resolution**: If A depends on B, and B depends on C, the system automatically identifies and installs C. +2. **Circular dependency detection**: Automatically detects and prevents circular dependencies. +3. **Correct installation order**: Installs in dependency order (dependencies installed first). +4. **Missing dependency check**: Checks if all dependencies are available before installation. -#### Use Case 1: Environment Validation +#### Defining Dependencies -Before updating, verify that all prerequisites are met: +Use comma-separated GUID list in extension metadata: ```c# -private async Task ValidateEnvironment() +var extension = new ExtensionMetadata { - // Check .NET runtime version - if (!IsRuntimeVersionSupported()) - return false; - - // Verify required services are running - if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) - return false; - - // Check file permissions - if (!HasWritePermission(installPath)) - return false; - - return true; -} + Id = "550e8400-e29b-41d4-a716-446655440001", + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // Depends on two other extensions + Dependencies = "550e8400-e29b-41d4-a716-446655440002,550e8400-e29b-41d4-a716-446655440003" +}; +``` + +#### Automatic Dependency Handling + +```c# +// UpdateExtensionAsync automatically handles dependencies: +// 1. Identify all dependencies +// 2. Check if already installed +// 3. Download and install missing dependencies +// 4. Install in correct order + +bool success = await host.UpdateExtensionAsync(extensionId); +// System will automatically install all required dependent extensions ``` -#### Use Case 2: Custom Update Notification -Show custom notifications to users during the update process: + +### Rollback Mechanism + +InstallExtensionAsync supports automatic rollback functionality, restoring to the previous state when installation fails. + +#### How It Works + +1. **Create backup**: Before installation, the system backs up the existing extension (if it exists). +2. **Attempt installation**: Execute extension installation operation. +3. **On success**: Delete backup files. +4. **On failure**: Automatically restore from backup, undoing all changes. + +#### Using Rollback Functionality ```c# -private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +bool success = await host.InstallExtensionAsync( + extensionPath: "./downloads/extension.zip", + rollbackOnFailure: true // Enable automatic rollback +); + +if (!success) { - // Show system tray notification - ShowNotification($"Updating: {e.ProgressPercentage:F0}%"); - - // Update application title - UpdateWindowTitle($"Downloading update... {e.ProgressPercentage:F0}%"); + Console.WriteLine("Installation failed, but automatically rolled back to previous version"); + Console.WriteLine("Your extension directory remains unchanged"); } ``` -#### Use Case 3: Conditional Updates +#### Best Practices + +- **Always enable rollback** in production (`rollbackOnFailure: true`) +- Ensure sufficient disk space for backups +- Monitor backup directory (default: `/.backup`) + + + +### Auto-Update Settings + +GeneralUpdate.Extension supports auto-update functionality for extensions. + +#### Enable Auto-Update for Individual Extension + +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + +// Enable auto-update +host.SetAutoUpdate(extensionId, true); + +// Disable auto-update +host.SetAutoUpdate(extensionId, false); + +// Check status +bool autoUpdateEnabled = host.IsAutoUpdateEnabled(extensionId); +Console.WriteLine($"Auto-update: {(autoUpdateEnabled ? "Enabled" : "Disabled")}"); +``` + +#### Global Auto-Update -Apply updates based on specific conditions: +```c# +// Enable auto-update for all extensions +host.SetGlobalAutoUpdate(true); + +// Disable global auto-update +host.SetGlobalAutoUpdate(false); +``` + + + +### Download Queue Management + +The download queue manager handles concurrent downloads, providing status tracking and cancellation capabilities. + +#### Queue Features + +- Default concurrent download limit: 3 simultaneous downloads +- Automatic status tracking +- Support for canceling ongoing downloads +- Supports resumable downloads (HTTP Range requests) + +#### Download Status + +Download tasks can be in one of the following states: + +```c# +public enum ExtensionUpdateStatus +{ + Queued, // Added to queue, waiting for download + Updating, // Downloading/updating + UpdateSuccessful, // Download/update successful + UpdateFailed // Download/update failed +} +``` + +#### Monitoring Downloads ```c# -private async Task ShouldApplyUpdate() +host.ExtensionUpdateStatusChanged += (sender, e) => { - // Check if it's business hours - var now = DateTime.Now; - if (now.Hour >= 9 && now.Hour <= 17) + Console.WriteLine($"Extension: {e.ExtensionName ?? e.ExtensionId}"); + Console.WriteLine($"Status: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) { - // Defer update during business hours - return false; + Console.WriteLine($"Progress: {e.Progress}%"); } - // Check if critical operations are running - if (IsCriticalOperationInProgress()) + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - return false; + Console.WriteLine($"Error: {e.ErrorMessage}"); } - - return true; -} +}; ``` -### Troubleshooting +### Server API Requirements + +GeneralUpdate.Extension requires the server to provide the following API endpoints: + +#### Query Extensions + +``` +POST {ServerUrl}/extensions +Content-Type: application/json +Authorization: Bearer {BearerToken} + +Request Body: ExtensionQueryDTO +Response: HttpResponseDTO> +``` + +#### Download Extension + +``` +GET {ServerUrl}/extensions/{id} +Authorization: Bearer {BearerToken} + +Response: File stream (supports HTTP Range requests for resumable downloads) +``` + -#### Problem: Custom operation blocks the update indefinitely -**Solution**: Ensure your custom operations have timeouts and proper error handling: +### Complete Usage Example + +Here is a complete example demonstrating the main operations of extension management: ```c# -private async Task CustomOperationWithTimeout() +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; +using GeneralUpdate.Extension.Common.Enums; +using System; +using System.Threading.Tasks; + +public class ExtensionManagerExample { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - try + public static async Task Main() { - await YourAsyncOperation(cts.Token); - return true; + // 1. Initialize extension host + var options = new ExtensionHostOptions + { + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" + }; + + var host = new GeneralExtensionHost(options); + + // 2. Subscribe to update events + host.ExtensionUpdateStatusChanged += (sender, e) => + { + Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {e.ExtensionName}"); + Console.WriteLine($" Status: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) + { + Console.WriteLine($" Progress: {e.Progress}%"); + } + + if (e.Status == ExtensionUpdateStatus.UpdateFailed) + { + Console.WriteLine($" Error: {e.ErrorMessage}"); + } + }; + + // 3. Query remote extensions + Console.WriteLine("=== Querying Remote Extensions ===\n"); + var query = new ExtensionQueryDTO + { + Platform = TargetPlatform.Windows, + HostVersion = options.HostVersion, + Status = true, + PageNumber = 1, + PageSize = 10 + }; + + var queryResult = await host.QueryExtensionsAsync(query); + if (queryResult.Success && queryResult.Data != null) + { + Console.WriteLine($"Found {queryResult.Data.TotalCount} extensions:\n"); + + foreach (var ext in queryResult.Data.Items) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" Publisher: {ext.Publisher}"); + Console.WriteLine($" Compatible: {ext.IsCompatible}"); + Console.WriteLine($" Platform: {ext.SupportedPlatforms}"); + Console.WriteLine(); + } + } + + // 4. Update specific extension + Console.WriteLine("\n=== Updating Extension ===\n"); + string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + + bool updateSuccess = await host.UpdateExtensionAsync(extensionId); + if (updateSuccess) + { + Console.WriteLine("✓ Extension updated successfully!"); + } + else + { + Console.WriteLine("✗ Extension update failed"); + } + + // 5. List installed extensions + Console.WriteLine("\n=== Installed Extensions ===\n"); + var installed = host.ExtensionCatalog.GetInstalledExtensions(); + Console.WriteLine($"Total {installed.Count} installed extensions:\n"); + + foreach (var ext in installed) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" Status: {(ext.Status == true ? "Enabled" : "Disabled")}"); + Console.WriteLine($" Platform: {ext.SupportedPlatforms}"); + + // Check compatibility + bool compatible = host.IsExtensionCompatible(ext); + Console.WriteLine($" Compatible: {(compatible ? "Yes" : "No")}"); + Console.WriteLine(); + } + + // 6. Configure auto-update + Console.WriteLine("\n=== Configuring Auto-Update ===\n"); + host.SetGlobalAutoUpdate(true); + Console.WriteLine("✓ Global auto-update enabled"); + + // Disable auto-update for specific extension + host.SetAutoUpdate(extensionId, false); + Console.WriteLine($"✓ Auto-update disabled for extension {extensionId}"); } - catch (OperationCanceledException) +} +``` + + + +### Best Practices + +1. **Always subscribe to events**: Subscribe to `ExtensionUpdateStatusChanged` event before performing any operations to monitor progress and errors. + +2. **Use rollback functionality**: Always enable `rollbackOnFailure: true` when installing extensions in production. + +3. **Check compatibility**: Use `IsExtensionCompatible()` to check extension compatibility before installation. + +4. **Reasonable pagination**: Use appropriate `PageSize` (recommended 10-50) when querying extensions to avoid loading too much data at once. + +5. **Secure token storage**: Bearer Token should be stored securely, not hardcoded in code. + +6. **Monitor disk space**: Ensure sufficient disk space for downloads and backups. + +7. **Verify extension metadata**: Validate extension metadata integrity and hash values before installation. + +8. **Handle network errors**: Implement retry logic to handle temporary network failures. + +9. **Logging**: Log all extension operations and errors for troubleshooting. + +10. **Regular cleanup**: Regularly clean up download cache and backup directories to free disk space. + + + +### Troubleshooting + +#### Issue: Extension Download Failed + +**Possible Causes**: +- Network connection issues +- Server unavailable +- Bearer Token invalid or expired + +**Solutions**: +```c# +// Check error information in event arguments +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - Console.WriteLine("Operation timed out"); - return false; + Console.WriteLine($"Download failed: {e.ErrorMessage}"); + // Log detailed information for debugging } +}; + +// Implement retry logic +int maxRetries = 3; +for (int i = 0; i < maxRetries; i++) +{ + bool success = await host.UpdateExtensionAsync(extensionId); + if (success) break; + + await Task.Delay(TimeSpan.FromSeconds(5)); // Wait before retrying } ``` -#### Problem: Events not firing - -**Solution**: Ensure you register event listeners before calling `LaunchAsync()`: +#### Issue: Extension Shows as Incompatible +**Solutions**: ```c# -// Correct order -await new GeneralClientBootstrap() - .AddListenerException(OnException) // Register first - .SetConfig(configinfo) - .LaunchAsync(); // Launch last +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null && !host.IsExtensionCompatible(extension)) +{ + Console.WriteLine("Extension version requirements:"); + Console.WriteLine($" Minimum host version: {extension.MinHostVersion}"); + Console.WriteLine($" Maximum host version: {extension.MaxHostVersion}"); + Console.WriteLine($" Current host version: {options.HostVersion}"); + + // Check if compatible version is available + var query = new ExtensionQueryDTO + { + Name = extension.Name, + HostVersion = options.HostVersion + }; + var result = await host.QueryExtensionsAsync(query); + // Check for compatible version +} ``` -#### Problem: Skip option not working +#### Issue: Installation Failed but Did Not Rollback + +**Solutions**: +Ensure rollback option is enabled: +```c# +bool success = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // Must be set to true +); +``` -**Solution**: Verify the server hasn't set the update as mandatory. The skip option only works for non-mandatory updates. +Check backup directory permissions: +```c# +string backupDir = Path.Combine(options.ExtensionsDirectory, ".backup"); +if (!Directory.Exists(backupDir)) +{ + Directory.CreateDirectory(backupDir); +} +// Ensure write permissions +``` -### Applicable To +### Applies To -| Product | Version | -| -------------- | ---------------- | -| .NET | 5, 6, 7, 8, 9, 10| -| .NET Framework | 4.6.1 | -| .NET Standard | 2.0 | -| .NET Core | 2.0 | +| Product | Versions | +| ------------------ | ------------------------------------- | +| .NET Standard | 2.0 | +| .NET Framework | 4.6.1+ | +| .NET Core | 2.0+ | +| .NET | 5, 6, 7, 8, 9, 10 | +| Mono | 5.4+ | +| Xamarin.iOS | 10.14+ | +| Xamarin.Android | 8.0+ | ### See Also - [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - Main client update component - [GeneralUpdate.Core](./GeneralUpdate.Core.md) - Core update logic +- [GitHub Source Code](https://github.com/GeneralLibrary/GeneralUpdate/tree/master/src/c%23/GeneralUpdate.Extension) - Complete source code and more examples - [Quick Start Guide](../quickstart/Quik%20start.md) - Getting started with GeneralUpdate diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md index be533c8..ad1e7ee 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/doc/GeneralUpdate.Extension.md @@ -6,518 +6,873 @@ sidebar_position: 12 命名空间:GeneralUpdate.Extension -程序集:GeneralUpdate.ClientCore.dll、GeneralUpdate.Core.dll +程序集:GeneralUpdate.Extension.dll +**NuGet 包**:GeneralUpdate.Extension -GeneralUpdate.Extension 提供了一个全面的扩展框架,允许开发人员自定义和扩展更新过程。它使您能够注入自定义逻辑、处理事件,并根据特定应用程序需求定制更新行为。对于需要对更新工作流进行细粒度控制的开发人员来说,此组件至关重要。 + +GeneralUpdate.Extension 是一个受 VS Code 启发的 .NET 应用程序扩展管理系统。它提供完整的插件/扩展管理功能,包括扩展下载、安装、更新、版本兼容性检查、平台支持、依赖关系解析和回滚机制。 ```c# -// 扩展方法通过 GeneralClientBootstrap 和 GeneralUpdateBootstrap 提供 -public class GeneralClientBootstrap : AbstractBootstrap +public class GeneralExtensionHost : IExtensionHost +{ + public IExtensionCatalog ExtensionCatalog { get; } + public event EventHandler? ExtensionUpdateStatusChanged; +} ``` -### 示例 +### 快速开始 -Extension 框架提供多种扩展功能的方法 [[查看示例]](https://github.com/GeneralLibrary/GeneralUpdate-Samples/blob/main/src/Client/Program.cs)。 +最基本的使用示例,初始化扩展主机并查询远程扩展 [[查看完整示例]](https://github.com/GeneralLibrary/GeneralUpdate/blob/master/src/c%23/GeneralUpdate.Extension/Examples/ExtensionExample.cs)。 ```c# -var configinfo = new Configinfo +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; + +// 1. 初始化扩展主机 +var options = new ExtensionHostOptions { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - AppName = "YourApp.exe", - ClientVersion = "1.0.0.0", - // ... 其他配置 + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", // 可选:用于身份验证 + HostVersion = "1.0.0", // 您的应用程序版本 + ExtensionsDirectory = "./extensions" // 扩展安装目录 }; -await new GeneralClientBootstrap() - // 添加自定义事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(CheckEnvironment) - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); -``` +var host = new GeneralExtensionHost(options); +// 2. 订阅更新事件 +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + Console.WriteLine($"扩展: {e.ExtensionName}"); + Console.WriteLine($"状态: {e.Status}"); + Console.WriteLine($"进度: {e.Progress}%"); +}; +// 3. 查询可用扩展 +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows, + HostVersion = "1.0.0", + PageNumber = 1, + PageSize = 20 +}; -### 核心扩展能力 +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + foreach (var ext in result.Data.Items) + { + Console.WriteLine($"{ext.DisplayName} v{ext.Version}"); + Console.WriteLine($"兼容: {ext.IsCompatible}"); + } +} + +// 4. 更新扩展 +bool success = await host.UpdateExtensionAsync("extension-guid"); +``` -GeneralUpdate.Extension 提供以下扩展功能。 -#### 事件监听器 -| 方法 | 说明 | -| -------------------------------------- | ------------------------------------------------------------ | -| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | -| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | -| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | -| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | -| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | +### 核心功能 -#### 自定义操作 +GeneralUpdate.Extension 提供以下核心功能。 -| 方法 | 说明 | -| --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | -| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | -| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | +#### 扩展主机 (GeneralExtensionHost) -#### 配置选项 +主要的扩展管理容器,提供所有扩展操作的入口点。 -| 方法 | 说明 | -| ------------- | ------------------------------------------------------------ | -| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | -| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | -| GetOption() | 检索当前更新配置设置以供检查或修改。 | -| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | +| 方法 | 说明 | +| --------------------------- | ------------------------------------------------------------ | +| QueryExtensionsAsync() | 从远程服务器查询可用扩展。支持按名称、平台、版本、状态等条件过滤。 | +| UpdateExtensionAsync() | 更新指定扩展。自动处理下载、兼容性检查、平台验证和安装。 | +| DownloadExtensionAsync() | 下载扩展包到指定路径。支持断点续传(HTTP Range 请求)。 | +| InstallExtensionAsync() | 安装扩展包。支持自动回滚功能,安装失败时恢复到之前状态。 | +| IsExtensionCompatible() | 检查扩展与当前主机版本是否兼容。 | +| SetAutoUpdate() | 为指定扩展启用/禁用自动更新。 | +| SetGlobalAutoUpdate() | 启用/禁用所有扩展的全局自动更新。 | +| IsAutoUpdateEnabled() | 检查扩展是否启用自动更新。 | +#### 扩展目录 (ExtensionCatalog) +管理本地已安装扩展的目录,使用 JSON 文件持久化存储。 -### 事件参数 +| 方法 | 说明 | +| --------------------------------- | ------------------------------------------------------------ | +| LoadInstalledExtensions() | 从 catalog.json 加载已安装的扩展列表。 | +| GetInstalledExtensions() | 获取所有已安装扩展的列表。 | +| GetInstalledExtensionById() | 根据扩展 ID(GUID)获取特定扩展。 | +| GetInstalledExtensionsByPlatform()| 获取支持指定平台的扩展列表。 | +| AddOrUpdateExtension() | 添加新扩展或更新现有扩展的元数据。 | +| RemoveExtension() | 从目录中删除扩展记录。 | +| SaveCatalog() | 将目录保存到 JSON 文件。 | -#### MultiDownloadStatisticsEventArgs +#### 下载队列 (DownloadQueueManager) -提供详细的下载进度信息。 +管理扩展下载队列,支持并发下载和状态跟踪。 -| 属性 | 类型 | 说明 | -| ------------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 当前正在下载的版本 | -| Speed | string | 下载速度(例如 "2.5 MB/s") | -| Remaining | string | 预计剩余时间(例如 "00:02:30") | -| BytesReceived | long | 到目前为止已下载的字节数 | -| TotalBytesToReceive| long | 下载的总大小(字节) | -| ProgressPercentage | double | 下载进度百分比(0-100) | +| 方法 | 说明 | +| ------------------------ | ------------------------------------------------------------ | +| EnqueueDownload() | 将下载任务添加到队列。任务将自动排队并处理。 | +| GetDownloadStatus() | 获取指定下载任务的当前状态。 | +| CancelDownload() | 取消正在进行的下载任务。 | -#### MultiDownloadCompletedEventArgs +下载队列特性: +- 默认并发下载数:3 个 +- 自动状态跟踪(排队、下载中、已完成、失败) +- 支持断点续传 +- 事件通知下载状态变化 -指示单个更新包的完成状态。 -| 属性 | 类型 | 说明 | -| ------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 已完成下载的版本信息 | -| IsComplated | bool | 下载是否成功完成 | -#### MultiAllDownloadCompletedEventArgs +### 扩展元数据 -所有下载任务的摘要。 +每个扩展必须包含以下元数据信息: -| 属性 | 类型 | 说明 | -| ---------------------- | --------------- | ------------------------------------------ | -| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| -| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | +| 属性 | 类型 | 说明 | +| ------------------ | --------------- | ---------------------------------------------------- | +| Id | string | 扩展唯一标识符(GUID 格式) | +| Name | string | 扩展名称(唯一,小写,无空格) | +| DisplayName | string | 人类可读的显示名称 | +| Version | string | 语义化版本号(例如 "1.2.3") | +| Publisher | string | 发布者标识符 | +| Description | string | 扩展描述 | +| SupportedPlatforms | TargetPlatform | 支持的平台标志(Windows/Linux/MacOS/All) | +| MinHostVersion | string | 最低兼容的主机版本 | +| MaxHostVersion | string | 最高兼容的主机版本 | +| Dependencies | string | 依赖的扩展 ID 列表(逗号分隔) | +| Format | string | 文件格式(.dll、.zip 等) | +| Categories | string | 扩展分类(逗号分隔) | +| IsPreRelease | bool | 是否为预发布版本 | +| License | string | 许可证标识符(例如 "MIT"、"Apache-2.0") | +| FileSize | long | 文件大小(字节) | +| Hash | string | 文件哈希值(SHA256) | +| DownloadUrl | string | 下载 URL | -#### MultiDownloadErrorEventArgs -下载失败的错误信息。 -| 属性 | 类型 | 说明 | -| --------- | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 遇到错误的版本 | -| Exception | Exception | 下载期间发生的异常 | +### 初始化和配置 -#### ExceptionEventArgs +#### 基本初始化 -全局异常信息。 +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" +}; -| 属性 | 类型 | 说明 | -| --------- | --------- | ---------------------------------------------------- | -| Exception | Exception | 更新过程中发生的异常 | +var host = new GeneralExtensionHost(options); +``` +#### 带身份验证的初始化 +```c# +var options = new ExtensionHostOptions +{ + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions", + CatalogPath = "./extensions/catalog.json" // 可选:自定义目录文件路径 +}; -### 自定义操作示例 +var host = new GeneralExtensionHost(options); +``` -以下是使用自定义操作的完整示例: +#### 订阅事件 ```c# -private async Task CheckEnvironment() +host.ExtensionUpdateStatusChanged += (sender, e) => { - try + switch (e.Status) { - // 检查是否安装了所需的依赖项 - if (!IsDependencyInstalled("SomeLibrary")) - { - Console.WriteLine("缺少必需的依赖项!"); - return false; // 取消更新 - } - - // 验证磁盘空间 - var requiredSpace = 500 * 1024 * 1024; // 500 MB - if (GetAvailableDiskSpace() < requiredSpace) - { - Console.WriteLine("磁盘空间不足!"); - return false; // 取消更新 - } - - // 创建关键文件的备份 - await BackupUserData(); - - return true; // 继续更新 + case ExtensionUpdateStatus.Queued: + Console.WriteLine($"{e.ExtensionName} 已加入队列"); + break; + case ExtensionUpdateStatus.Updating: + Console.WriteLine($"{e.ExtensionName} 更新中... {e.Progress}%"); + break; + case ExtensionUpdateStatus.UpdateSuccessful: + Console.WriteLine($"{e.ExtensionName} 更新成功!"); + break; + case ExtensionUpdateStatus.UpdateFailed: + Console.WriteLine($"{e.ExtensionName} 更新失败: {e.ErrorMessage}"); + break; } - catch (Exception ex) +}; +``` + + + +### 查询远程扩展 + +#### 基本查询 + +```c# +var query = new ExtensionQueryDTO +{ + PageNumber = 1, + PageSize = 20 +}; + +var result = await host.QueryExtensionsAsync(query); +if (result.Success && result.Data != null) +{ + Console.WriteLine($"找到 {result.Data.TotalCount} 个扩展"); + foreach (var ext in result.Data.Items) { - Console.WriteLine($"环境检查失败:{ex.Message}"); - return false; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" 发布者: {ext.Publisher}"); + Console.WriteLine($" 兼容: {ext.IsCompatible}"); } } +``` -// 注册自定义操作 -await new GeneralClientBootstrap() - .AddCustomOption(CheckEnvironment) - .SetConfig(configinfo) - .LaunchAsync(); +#### 高级查询(带过滤条件) + +```c# +var query = new ExtensionQueryDTO +{ + Name = "my-extension", // 按名称搜索 + Platform = TargetPlatform.Windows, // 只查询 Windows 扩展 + HostVersion = "1.0.0", // 检查与此版本的兼容性 + Status = true, // 只查询已启用的扩展 + BeginDate = DateTime.Now.AddMonths(-1), // 最近一个月的扩展 + EndDate = DateTime.Now, + PageNumber = 1, + PageSize = 50 +}; + +var result = await host.QueryExtensionsAsync(query); ``` -### 跳过选项示例 +### 安装和更新扩展 + +#### 自动更新扩展 -允许用户跳过非强制更新: +最简单的方式,一次调用完成所有操作: ```c# -private async Task ShowSkipDialog() +// UpdateExtensionAsync 自动执行以下步骤: +// 1. 从服务器查询扩展信息 +// 2. 检查版本兼容性 +// 3. 检查平台支持 +// 4. 下载扩展包 +// 5. 安装扩展 +// 6. 更新本地目录 + +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +bool success = await host.UpdateExtensionAsync(extensionId); + +if (success) { - // 向用户显示自定义对话框 - var result = await ShowUpdateDialog( - "有新的更新可用!", - "您想现在更新吗?", - new[] { "立即更新", "跳过" } - ); - - return result == "立即更新"; + Console.WriteLine("扩展更新成功!"); +} +else +{ + Console.WriteLine("扩展更新失败,请检查事件获取详细信息"); } - -// 注册跳过选项处理程序 -await new GeneralClientBootstrap() - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); ``` +#### 手动下载和安装 +分步骤控制安装过程: -### 黑名单配置 +```c# +// 步骤 1:下载扩展 +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +string savePath = "./downloads/my-extension.zip"; -从更新中排除特定文件或格式: +bool downloaded = await host.DownloadExtensionAsync(extensionId, savePath); -```c# -var configinfo = new Configinfo +if (downloaded) { - // ... 其他配置 - BlackFiles = new List<string> - { - "userconfig.json", - "license.dat", - "custom.db" - }, - BlackFormats = new List<string> - { - ".log", - ".temp", - ".cache" - }, - BlackDirectories = new List<string> + // 步骤 2:安装扩展(带回滚功能) + bool installed = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // 失败时自动回滚 + ); + + if (installed) { - "UserData", - "Plugins\\Custom" + Console.WriteLine("扩展安装成功!"); } -}; - -// 或使用 SetBlacklist 方法 -await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List<string> { "config.ini" }) - .SetConfig(configinfo) - .LaunchAsync(); + else + { + Console.WriteLine("安装失败,已自动回滚"); + } +} ``` -### 完整扩展示例 +### 管理已安装扩展 -以下是使用多个扩展功能的综合示例: +#### 列出所有扩展 ```c# -using GeneralUpdate.ClientCore; -using GeneralUpdate.Common.Shared.Object; +var extensions = host.ExtensionCatalog.GetInstalledExtensions(); -public class UpdateManager +Console.WriteLine($"已安装 {extensions.Count} 个扩展:"); +foreach (var ext in extensions) { - public async Task StartUpdateWithExtensions() - { - var configinfo = new Configinfo - { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", - AppName = "MyApp.exe", - MainAppName = "MyApp.exe", - ClientVersion = "1.0.0.0", - ProductId = "your-product-id", - AppSecretKey = "your-secret-key", - InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List<string> { "userdata.db" } - }; + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" 状态: {(ext.Status == true ? "启用" : "禁用")}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); +} +``` - try - { - await new GeneralClientBootstrap() - // 注册事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) - .AddListenerMultiDownloadError(OnDownloadError) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(PerformPreUpdateChecks) - .SetCustomSkipOption(AskUserForUpdatePermission) - // 配置选项 - .SetConfig(configinfo) - .Option(UpdateOption.DownloadTimeOut, 120) - .Option(UpdateOption.Patch, true) - .Option(UpdateOption.BackUp, true) - // 启动更新 - .LaunchAsync(); - } - catch (Exception ex) - { - Console.WriteLine($"更新失败:{ex.Message}"); - } - } +#### 获取特定扩展 - private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); - // 在此更新 UI 进度条 - } +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); - private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var version = e.Version as VersionInfo; - if (e.IsComplated) - { - Console.WriteLine($"版本 {version.Version} 下载成功!"); - } - else - { - Console.WriteLine($"版本 {version.Version} 下载失败!"); - } - } +if (extension != null) +{ + Console.WriteLine($"扩展名称: {extension.DisplayName}"); + Console.WriteLine($"版本: {extension.Version}"); + Console.WriteLine($"发布者: {extension.Publisher}"); + Console.WriteLine($"描述: {extension.Description}"); +} +else +{ + Console.WriteLine("未找到扩展"); +} +``` - private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - if (e.IsAllDownloadCompleted) - { - Console.WriteLine("所有更新下载成功!"); - } - else - { - Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); - } - } +#### 按平台筛选扩展 - private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); - } +```c# +var windowsExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Windows); - private void OnException(object sender, ExceptionEventArgs e) - { - Console.WriteLine($"更新异常:{e.Exception.Message}"); - // 将异常记录到文件或监控系统 - } +Console.WriteLine($"Windows 扩展: {windowsExtensions.Count} 个"); - private async Task PerformPreUpdateChecks() - { - // 检查系统要求 - Console.WriteLine("正在执行更新前检查..."); - - // 验证网络连接 - if (!await CheckNetworkConnection()) - { - Console.WriteLine("没有可用的网络连接!"); - return false; - } - - // 检查磁盘空间 - if (!HasSufficientDiskSpace(500 * 1024 * 1024)) - { - Console.WriteLine("磁盘空间不足!"); - return false; - } - - Console.WriteLine("更新前检查通过!"); - return true; - } +var linuxExtensions = host.ExtensionCatalog + .GetInstalledExtensionsByPlatform(TargetPlatform.Linux); - private async Task AskUserForUpdatePermission() - { - Console.WriteLine("有新更新可用。立即更新?(y/n)"); - var response = Console.ReadLine(); - return response?.ToLower() == "y"; - } +Console.WriteLine($"Linux 扩展: {linuxExtensions.Count} 个"); +``` + + + +### 版本兼容性 + +GeneralUpdate.Extension 自动检查版本兼容性,确保扩展与主机应用程序版本匹配。 + +#### 兼容性规则 + +扩展必须满足以下条件才被视为兼容: +1. 扩展的 MinHostVersion ≤ 主机版本 +2. 扩展的 MaxHostVersion ≥ 主机版本 +3. 两个条件必须同时满足 + +#### 示例 - private async Task CheckNetworkConnection() +假设主机版本为 1.5.0: + +| 扩展 MinHostVersion | 扩展 MaxHostVersion | 结果 | +| ------------------- | ------------------- | -------- | +| 1.0.0 | 2.0.0 | ✓ 兼容 | +| 1.6.0 | 2.0.0 | ✗ 不兼容 | +| 1.0.0 | 1.4.0 | ✗ 不兼容 | +| 1.5.0 | 1.5.0 | ✓ 兼容 | + +#### 检查兼容性 + +```c# +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null) +{ + bool isCompatible = host.IsExtensionCompatible(extension); + + if (isCompatible) { - // 实现网络检查 - return true; + Console.WriteLine("✓ 扩展与当前版本兼容"); } - - private bool HasSufficientDiskSpace(long requiredBytes) + else { - // 实现磁盘空间检查 - return true; + Console.WriteLine("✗ 扩展与当前版本不兼容"); + Console.WriteLine($" 需要主机版本: {extension.MinHostVersion} - {extension.MaxHostVersion}"); + Console.WriteLine($" 当前主机版本: {options.HostVersion}"); } } ``` -### 最佳实践 +### 平台支持 -1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 +GeneralUpdate.Extension 支持多平台扩展,可以为不同操作系统提供不同的扩展版本。 -2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 +#### 平台标志 -3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 +```c# +// 单个平台 +TargetPlatform.Windows // 仅 Windows +TargetPlatform.Linux // 仅 Linux +TargetPlatform.MacOS // 仅 macOS + +// 多平台(使用位标志组合) +TargetPlatform.Windows | TargetPlatform.Linux // Windows 和 Linux +TargetPlatform.All // 所有平台 +``` -4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 +#### 自动平台检测 -5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 +系统自动检测当前运行平台,并筛选兼容的扩展: -6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 +```c# +// 查询时自动按平台过滤 +var query = new ExtensionQueryDTO +{ + Platform = TargetPlatform.Windows // 只返回支持 Windows 的扩展 +}; -7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 +var result = await host.QueryExtensionsAsync(query); +``` -8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 +#### 平台特定扩展 +```c# +// 示例:为不同平台设置扩展元数据 +var extension = new ExtensionMetadata +{ + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // 仅支持 Windows 和 Linux + SupportedPlatforms = TargetPlatform.Windows | TargetPlatform.Linux +}; +``` + + + +### 依赖关系解析 + +GeneralUpdate.Extension 自动处理扩展之间的依赖关系。 +#### 依赖关系特性 -### 常见用例 +1. **自动解析传递依赖**:如果 A 依赖 B,B 依赖 C,系统会自动识别并安装 C。 +2. **循环依赖检测**:自动检测并防止循环依赖。 +3. **正确的安装顺序**:按依赖顺序安装(先安装被依赖项)。 +4. **缺失依赖检查**:安装前检查所有依赖是否可用。 -#### 用例 1:环境验证 +#### 定义依赖关系 -更新前,验证是否满足所有先决条件: +在扩展元数据中使用逗号分隔的 GUID 列表: ```c# -private async Task ValidateEnvironment() +var extension = new ExtensionMetadata { - // 检查 .NET 运行时版本 - if (!IsRuntimeVersionSupported()) - return false; - - // 验证所需服务是否正在运行 - if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) - return false; - - // 检查文件权限 - if (!HasWritePermission(installPath)) - return false; - - return true; -} + Id = "550e8400-e29b-41d4-a716-446655440001", + Name = "my-extension", + DisplayName = "My Extension", + Version = "1.0.0", + // 依赖两个其他扩展 + Dependencies = "550e8400-e29b-41d4-a716-446655440002,550e8400-e29b-41d4-a716-446655440003" +}; +``` + +#### 依赖自动处理 + +```c# +// UpdateExtensionAsync 自动处理依赖: +// 1. 识别所有依赖项 +// 2. 检查是否已安装 +// 3. 下载并安装缺失的依赖 +// 4. 按正确顺序安装 + +bool success = await host.UpdateExtensionAsync(extensionId); +// 系统会自动安装所有必需的依赖扩展 ``` -#### 用例 2:自定义更新通知 -在更新过程中向用户显示自定义通知: + +### 回滚机制 + +InstallExtensionAsync 支持自动回滚功能,在安装失败时恢复到之前的状态。 + +#### 工作原理 + +1. **创建备份**:安装前,系统备份现有扩展(如果存在)。 +2. **尝试安装**:执行扩展安装操作。 +3. **成功时**:删除备份文件。 +4. **失败时**:自动从备份恢复,撤销所有更改。 + +#### 使用回滚功能 ```c# -private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) +bool success = await host.InstallExtensionAsync( + extensionPath: "./downloads/extension.zip", + rollbackOnFailure: true // 启用自动回滚 +); + +if (!success) { - // 显示系统托盘通知 - ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); - - // 更新应用程序标题 - UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); + Console.WriteLine("安装失败,但已自动回滚到之前的版本"); + Console.WriteLine("您的扩展目录保持不变"); } ``` -#### 用例 3:条件更新 +#### 最佳实践 + +- 生产环境中**始终启用回滚**(`rollbackOnFailure: true`) +- 确保有足够的磁盘空间用于备份 +- 监控备份目录(默认为 `/.backup`) + + + +### 自动更新设置 + +GeneralUpdate.Extension 支持扩展的自动更新功能。 + +#### 为单个扩展启用自动更新 + +```c# +string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + +// 启用自动更新 +host.SetAutoUpdate(extensionId, true); + +// 禁用自动更新 +host.SetAutoUpdate(extensionId, false); + +// 检查状态 +bool autoUpdateEnabled = host.IsAutoUpdateEnabled(extensionId); +Console.WriteLine($"自动更新: {(autoUpdateEnabled ? "启用" : "禁用")}"); +``` + +#### 全局自动更新 -根据特定条件应用更新: +```c# +// 为所有扩展启用自动更新 +host.SetGlobalAutoUpdate(true); + +// 禁用全局自动更新 +host.SetGlobalAutoUpdate(false); +``` + + + +### 下载队列管理 + +下载队列管理器处理并发下载,提供状态跟踪和取消功能。 + +#### 队列特性 + +- 默认并发下载限制:3 个同时下载 +- 自动状态跟踪 +- 支持取消正在进行的下载 +- 支持断点续传(HTTP Range 请求) + +#### 下载状态 + +下载任务可能处于以下状态之一: + +```c# +public enum ExtensionUpdateStatus +{ + Queued, // 已加入队列,等待下载 + Updating, // 正在下载/更新 + UpdateSuccessful, // 下载/更新成功 + UpdateFailed // 下载/更新失败 +} +``` + +#### 监控下载 ```c# -private async Task ShouldApplyUpdate() +host.ExtensionUpdateStatusChanged += (sender, e) => { - // 检查是否在营业时间内 - var now = DateTime.Now; - if (now.Hour >= 9 && now.Hour <= 17) + Console.WriteLine($"扩展: {e.ExtensionName ?? e.ExtensionId}"); + Console.WriteLine($"状态: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) { - // 在营业时间推迟更新 - return false; + Console.WriteLine($"进度: {e.Progress}%"); } - // 检查是否有关键操作正在运行 - if (IsCriticalOperationInProgress()) + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - return false; + Console.WriteLine($"错误: {e.ErrorMessage}"); } - - return true; -} +}; ``` -### 故障排除 +### 服务器 API 要求 + +GeneralUpdate.Extension 需要服务器提供以下 API 端点: + +#### 查询扩展 + +``` +POST {ServerUrl}/extensions +Content-Type: application/json +Authorization: Bearer {BearerToken} + +请求体: ExtensionQueryDTO +响应: HttpResponseDTO> +``` + +#### 下载扩展 + +``` +GET {ServerUrl}/extensions/{id} +Authorization: Bearer {BearerToken} + +响应: 文件流(支持 HTTP Range 请求以实现断点续传) +``` + -#### 问题:自定义操作无限期地阻止更新 -**解决方案**:确保您的自定义操作具有超时和适当的错误处理: +### 完整使用示例 + +以下是一个完整的示例,展示了扩展管理的主要操作: ```c# -private async Task CustomOperationWithTimeout() +using GeneralUpdate.Extension; +using GeneralUpdate.Extension.Core; +using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Extension.Common.DTOs; +using GeneralUpdate.Extension.Common.Enums; +using System; +using System.Threading.Tasks; + +public class ExtensionManagerExample { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - try + public static async Task Main() { - await YourAsyncOperation(cts.Token); - return true; + // 1. 初始化扩展主机 + var options = new ExtensionHostOptions + { + ServerUrl = "https://extensions.example.com/api", + BearerToken = "your-bearer-token", + HostVersion = "1.0.0", + ExtensionsDirectory = "./extensions" + }; + + var host = new GeneralExtensionHost(options); + + // 2. 订阅更新事件 + host.ExtensionUpdateStatusChanged += (sender, e) => + { + Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {e.ExtensionName}"); + Console.WriteLine($" 状态: {e.Status}"); + + if (e.Status == ExtensionUpdateStatus.Updating) + { + Console.WriteLine($" 进度: {e.Progress}%"); + } + + if (e.Status == ExtensionUpdateStatus.UpdateFailed) + { + Console.WriteLine($" 错误: {e.ErrorMessage}"); + } + }; + + // 3. 查询远程扩展 + Console.WriteLine("=== 查询远程扩展 ===\n"); + var query = new ExtensionQueryDTO + { + Platform = TargetPlatform.Windows, + HostVersion = options.HostVersion, + Status = true, + PageNumber = 1, + PageSize = 10 + }; + + var queryResult = await host.QueryExtensionsAsync(query); + if (queryResult.Success && queryResult.Data != null) + { + Console.WriteLine($"找到 {queryResult.Data.TotalCount} 个扩展:\n"); + + foreach (var ext in queryResult.Data.Items) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" ID: {ext.Id}"); + Console.WriteLine($" 发布者: {ext.Publisher}"); + Console.WriteLine($" 兼容: {ext.IsCompatible}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); + Console.WriteLine(); + } + } + + // 4. 更新特定扩展 + Console.WriteLine("\n=== 更新扩展 ===\n"); + string extensionId = "550e8400-e29b-41d4-a716-446655440000"; + + bool updateSuccess = await host.UpdateExtensionAsync(extensionId); + if (updateSuccess) + { + Console.WriteLine("✓ 扩展更新成功!"); + } + else + { + Console.WriteLine("✗ 扩展更新失败"); + } + + // 5. 列出已安装的扩展 + Console.WriteLine("\n=== 已安装的扩展 ===\n"); + var installed = host.ExtensionCatalog.GetInstalledExtensions(); + Console.WriteLine($"总共 {installed.Count} 个已安装扩展:\n"); + + foreach (var ext in installed) + { + Console.WriteLine($"• {ext.DisplayName} v{ext.Version}"); + Console.WriteLine($" 状态: {(ext.Status == true ? "启用" : "禁用")}"); + Console.WriteLine($" 平台: {ext.SupportedPlatforms}"); + + // 检查兼容性 + bool compatible = host.IsExtensionCompatible(ext); + Console.WriteLine($" 兼容: {(compatible ? "是" : "否")}"); + Console.WriteLine(); + } + + // 6. 配置自动更新 + Console.WriteLine("\n=== 配置自动更新 ===\n"); + host.SetGlobalAutoUpdate(true); + Console.WriteLine("✓ 已启用全局自动更新"); + + // 为特定扩展禁用自动更新 + host.SetAutoUpdate(extensionId, false); + Console.WriteLine($"✓ 已为扩展 {extensionId} 禁用自动更新"); } - catch (OperationCanceledException) +} +``` + + + +### 最佳实践 + +1. **始终订阅事件**:在执行任何操作前订阅 `ExtensionUpdateStatusChanged` 事件,以监控进度和错误。 + +2. **使用回滚功能**:生产环境中安装扩展时始终启用 `rollbackOnFailure: true`。 + +3. **检查兼容性**:安装前使用 `IsExtensionCompatible()` 检查扩展兼容性。 + +4. **合理分页**:查询扩展时使用适当的 `PageSize`(建议 10-50),避免一次加载过多数据。 + +5. **安全存储令牌**:Bearer Token 应安全存储,不要硬编码在代码中。 + +6. **监控磁盘空间**:确保有足够的磁盘空间用于下载和备份。 + +7. **验证扩展元数据**:安装前验证扩展的元数据完整性和哈希值。 + +8. **处理网络错误**:实现重试逻辑处理网络临时故障。 + +9. **日志记录**:记录所有扩展操作和错误,便于故障排查。 + +10. **定期清理**:定期清理下载缓存和备份目录,释放磁盘空间。 + + + +### 故障排除 + +#### 问题:扩展下载失败 + +**可能原因**: +- 网络连接问题 +- 服务器不可用 +- Bearer Token 无效或过期 + +**解决方案**: +```c# +// 检查事件参数中的错误信息 +host.ExtensionUpdateStatusChanged += (sender, e) => +{ + if (e.Status == ExtensionUpdateStatus.UpdateFailed) { - Console.WriteLine("操作超时"); - return false; + Console.WriteLine($"下载失败: {e.ErrorMessage}"); + // 记录详细信息用于调试 } +}; + +// 实现重试逻辑 +int maxRetries = 3; +for (int i = 0; i < maxRetries; i++) +{ + bool success = await host.UpdateExtensionAsync(extensionId); + if (success) break; + + await Task.Delay(TimeSpan.FromSeconds(5)); // 等待后重试 } ``` -#### 问题:事件未触发 - -**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: +#### 问题:扩展显示不兼容 +**解决方案**: ```c# -// 正确的顺序 -await new GeneralClientBootstrap() - .AddListenerException(OnException) // 先注册 - .SetConfig(configinfo) - .LaunchAsync(); // 最后启动 +var extension = host.ExtensionCatalog.GetInstalledExtensionById(extensionId); +if (extension != null && !host.IsExtensionCompatible(extension)) +{ + Console.WriteLine("扩展版本要求:"); + Console.WriteLine($" 最低主机版本: {extension.MinHostVersion}"); + Console.WriteLine($" 最高主机版本: {extension.MaxHostVersion}"); + Console.WriteLine($" 当前主机版本: {options.HostVersion}"); + + // 检查是否有兼容的版本可用 + var query = new ExtensionQueryDTO + { + Name = extension.Name, + HostVersion = options.HostVersion + }; + var result = await host.QueryExtensionsAsync(query); + // 检查是否有兼容版本 +} ``` -#### 问题:跳过选项不起作用 +#### 问题:安装失败但未回滚 + +**解决方案**: +确保启用了回滚选项: +```c# +bool success = await host.InstallExtensionAsync( + extensionPath: savePath, + rollbackOnFailure: true // 必须设置为 true +); +``` -**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 +检查备份目录权限: +```c# +string backupDir = Path.Combine(options.ExtensionsDirectory, ".backup"); +if (!Directory.Exists(backupDir)) +{ + Directory.CreateDirectory(backupDir); +} +// 确保有写入权限 +``` ### 适用于 -| 产品 | 版本 | -| -------------- | ---------------- | -| .NET | 5, 6, 7, 8, 9, 10| -| .NET Framework | 4.6.1 | -| .NET Standard | 2.0 | -| .NET Core | 2.0 | +| 产品 | 版本 | +| -------------- | ------------------------------------- | +| .NET Standard | 2.0 | +| .NET Framework | 4.6.1+ | +| .NET Core | 2.0+ | +| .NET | 5, 6, 7, 8, 9, 10 | +| Mono | 5.4+ | +| Xamarin.iOS | 10.14+ | +| Xamarin.Android| 8.0+ | ### 另请参阅 -- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 +- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主客户端更新组件 - [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 +- [GitHub 源代码](https://github.com/GeneralLibrary/GeneralUpdate/tree/master/src/c%23/GeneralUpdate.Extension) - 完整源代码和更多示例 - [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门 From c243a551b0db6cfcd6d394b182d0ece192e73490 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:21:26 +0000 Subject: [PATCH 5/5] Remove backup file Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --- .../doc/GeneralUpdate.Extension.md.backup | 528 ------------------ 1 file changed, 528 deletions(-) delete mode 100644 website/docs/doc/GeneralUpdate.Extension.md.backup diff --git a/website/docs/doc/GeneralUpdate.Extension.md.backup b/website/docs/doc/GeneralUpdate.Extension.md.backup deleted file mode 100644 index d45f148..0000000 --- a/website/docs/doc/GeneralUpdate.Extension.md.backup +++ /dev/null @@ -1,528 +0,0 @@ ---- -sidebar_position: 12 ---- - -### 定义 - -命名空间:GeneralUpdate.Extension - -程序集:GeneralUpdate.Extension.dll - -**NuGet 包**:GeneralUpdate.Extension - - - -GeneralUpdate.Extension 是一个受 VS Code 启发的 .NET 应用程序扩展管理系统。它提供完整的插件/扩展管理功能,包括扩展下载、安装、更新、版本兼容性检查、平台支持、依赖关系解析和回滚机制。 - -```c# -public class GeneralExtensionHost : IExtensionHost -{ - public IExtensionCatalog ExtensionCatalog { get; } - public event EventHandler? ExtensionUpdateStatusChanged; -} -``` - - - -### 快速开始 - -最基本的使用示例,初始化扩展主机并查询远程扩展 [[查看完整示例]](https://github.com/GeneralLibrary/GeneralUpdate/blob/master/src/c%23/GeneralUpdate.Extension/Examples/ExtensionExample.cs)。 - -```c# -var configinfo = new Configinfo -{ - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - AppName = "YourApp.exe", - ClientVersion = "1.0.0.0", - // ... 其他配置 -}; - -await new GeneralClientBootstrap() - // 添加自定义事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(CheckEnvironment) - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); -``` - - - -### 核心扩展能力 - -GeneralUpdate.Extension 提供以下扩展功能。 - -#### 事件监听器 - -| 方法 | 说明 | -| -------------------------------------- | ------------------------------------------------------------ | -| AddListenerMultiDownloadStatistics() | 订阅下载进度通知,包括速度、剩余时间、已接收字节数和进度百分比。非常适合向用户显示实时下载信息。 | -| AddListenerMultiDownloadCompleted() | 单个更新包下载完成时触发的事件(无论成功或失败)。允许您处理每个版本的下载完成情况。 | -| AddListenerMultiAllDownloadCompleted() | 所有更新包下载完成时的通知。这是更新安装开始前的最后检查点。 | -| AddListenerMultiDownloadError() | 监听每个版本的下载错误。提供异常详情以帮助诊断网络或服务器问题。 | -| AddListenerException() | 全局异常处理程序,捕获整个更新过程中的任何错误。对于错误日志记录和用户通知至关重要。 | - -#### 自定义操作 - -| 方法 | 说明 | -| --------------------- | ------------------------------------------------------------ | -| AddCustomOption() | 向更新工作流注入自定义异步操作。在更新开始前执行。非常适合环境检查、更新前备份或任何准备任务。返回 Task<bool>,返回 false 将取消更新。 | -| SetCustomSkipOption() | 允许用户决定是否跳过非强制更新。提供一种显示自定义 UI 以获取用户同意的方法。仅在服务器未强制更新时有效。 | -| SetBlacklist() | 定义永远不应更新的文件或文件格式。对于保留用户数据、配置文件或第三方依赖项很有用。 | - -#### 配置选项 - -| 方法 | 说明 | -| ------------- | ------------------------------------------------------------ | -| Option() | 配置更新行为,例如超时时间、编码格式、补丁启用和备份设置。提供对更新过程的细粒度控制。 | -| Config() | 设置核心更新参数,包括服务器 URL、版本信息、身份验证密钥和安装路径。 | -| GetOption() | 检索当前更新配置设置以供检查或修改。 | -| SetConfig() | 应用完整的 Configinfo 对象来配置更新客户端。 | - - - -### 事件参数 - -#### MultiDownloadStatisticsEventArgs - -提供详细的下载进度信息。 - -| 属性 | 类型 | 说明 | -| ------------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 当前正在下载的版本 | -| Speed | string | 下载速度(例如 "2.5 MB/s") | -| Remaining | string | 预计剩余时间(例如 "00:02:30") | -| BytesReceived | long | 到目前为止已下载的字节数 | -| TotalBytesToReceive| long | 下载的总大小(字节) | -| ProgressPercentage | double | 下载进度百分比(0-100) | - -#### MultiDownloadCompletedEventArgs - -指示单个更新包的完成状态。 - -| 属性 | 类型 | 说明 | -| ------------ | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 已完成下载的版本信息 | -| IsComplated | bool | 下载是否成功完成 | - -#### MultiAllDownloadCompletedEventArgs - -所有下载任务的摘要。 - -| 属性 | 类型 | 说明 | -| ---------------------- | --------------- | ------------------------------------------ | -| IsAllDownloadCompleted | bool | 是否所有下载都成功完成| -| FailedVersions | List<VersionInfo>| 下载失败的版本列表 | - -#### MultiDownloadErrorEventArgs - -下载失败的错误信息。 - -| 属性 | 类型 | 说明 | -| --------- | ----------- | ---------------------------------------------------- | -| Version | VersionInfo | 遇到错误的版本 | -| Exception | Exception | 下载期间发生的异常 | - -#### ExceptionEventArgs - -全局异常信息。 - -| 属性 | 类型 | 说明 | -| --------- | --------- | ---------------------------------------------------- | -| Exception | Exception | 更新过程中发生的异常 | - - - -### 自定义操作示例 - -以下是使用自定义操作的完整示例: - -```c# -private async Task CheckEnvironment() -{ - try - { - // 检查是否安装了所需的依赖项 - if (!IsDependencyInstalled("SomeLibrary")) - { - Console.WriteLine("缺少必需的依赖项!"); - return false; // 取消更新 - } - - // 验证磁盘空间 - var requiredSpace = 500 * 1024 * 1024; // 500 MB - if (GetAvailableDiskSpace() < requiredSpace) - { - Console.WriteLine("磁盘空间不足!"); - return false; // 取消更新 - } - - // 创建关键文件的备份 - await BackupUserData(); - - return true; // 继续更新 - } - catch (Exception ex) - { - Console.WriteLine($"环境检查失败:{ex.Message}"); - return false; - } -} - -// 注册自定义操作 -await new GeneralClientBootstrap() - .AddCustomOption(CheckEnvironment) - .SetConfig(configinfo) - .LaunchAsync(); -``` - - - -### 跳过选项示例 - -允许用户跳过非强制更新: - -```c# -private async Task ShowSkipDialog() -{ - // 向用户显示自定义对话框 - var result = await ShowUpdateDialog( - "有新的更新可用!", - "您想现在更新吗?", - new[] { "立即更新", "跳过" } - ); - - return result == "立即更新"; -} - -// 注册跳过选项处理程序 -await new GeneralClientBootstrap() - .SetCustomSkipOption(ShowSkipDialog) - .SetConfig(configinfo) - .LaunchAsync(); -``` - - - -### 黑名单配置 - -从更新中排除特定文件或格式: - -```c# -var configinfo = new Configinfo -{ - // ... 其他配置 - BlackFiles = new List<string> - { - "userconfig.json", - "license.dat", - "custom.db" - }, - BlackFormats = new List<string> - { - ".log", - ".temp", - ".cache" - }, - BlackDirectories = new List<string> - { - "UserData", - "Plugins\\Custom" - } -}; - -// 或使用 SetBlacklist 方法 -await new GeneralClientBootstrap() - .SetBlacklist(blackFiles: new List<string> { "config.ini" }) - .SetConfig(configinfo) - .LaunchAsync(); -``` - - - -### 完整扩展示例 - -以下是使用多个扩展功能的综合示例: - -```c# -using GeneralUpdate.ClientCore; -using GeneralUpdate.Common.Shared.Object; - -public class UpdateManager -{ - public async Task StartUpdateWithExtensions() - { - var configinfo = new Configinfo - { - UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", - ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", - AppName = "MyApp.exe", - MainAppName = "MyApp.exe", - ClientVersion = "1.0.0.0", - ProductId = "your-product-id", - AppSecretKey = "your-secret-key", - InstallPath = AppDomain.CurrentDomain.BaseDirectory, - BlackFiles = new List<string> { "userdata.db" } - }; - - try - { - await new GeneralClientBootstrap() - // 注册事件监听器 - .AddListenerMultiDownloadStatistics(OnDownloadProgress) - .AddListenerMultiDownloadCompleted(OnDownloadCompleted) - .AddListenerMultiAllDownloadCompleted(OnAllDownloadsCompleted) - .AddListenerMultiDownloadError(OnDownloadError) - .AddListenerException(OnException) - // 添加自定义操作 - .AddCustomOption(PerformPreUpdateChecks) - .SetCustomSkipOption(AskUserForUpdatePermission) - // 配置选项 - .SetConfig(configinfo) - .Option(UpdateOption.DownloadTimeOut, 120) - .Option(UpdateOption.Patch, true) - .Option(UpdateOption.BackUp, true) - // 启动更新 - .LaunchAsync(); - } - catch (Exception ex) - { - Console.WriteLine($"更新失败:{ex.Message}"); - } - } - - private void OnDownloadProgress(object sender, MultiDownloadStatisticsEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"正在下载 {version.Version}:{e.ProgressPercentage:F2}%,速度 {e.Speed}"); - // 在此更新 UI 进度条 - } - - private void OnDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var version = e.Version as VersionInfo; - if (e.IsComplated) - { - Console.WriteLine($"版本 {version.Version} 下载成功!"); - } - else - { - Console.WriteLine($"版本 {version.Version} 下载失败!"); - } - } - - private void OnAllDownloadsCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - if (e.IsAllDownloadCompleted) - { - Console.WriteLine("所有更新下载成功!"); - } - else - { - Console.WriteLine($"更新失败。{e.FailedVersions.Count} 个版本失败。"); - } - } - - private void OnDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var version = e.Version as VersionInfo; - Console.WriteLine($"下载 {version.Version} 时出错:{e.Exception.Message}"); - } - - private void OnException(object sender, ExceptionEventArgs e) - { - Console.WriteLine($"更新异常:{e.Exception.Message}"); - // 将异常记录到文件或监控系统 - } - - private async Task PerformPreUpdateChecks() - { - // 检查系统要求 - Console.WriteLine("正在执行更新前检查..."); - - // 验证网络连接 - if (!await CheckNetworkConnection()) - { - Console.WriteLine("没有可用的网络连接!"); - return false; - } - - // 检查磁盘空间 - if (!HasSufficientDiskSpace(500 * 1024 * 1024)) - { - Console.WriteLine("磁盘空间不足!"); - return false; - } - - Console.WriteLine("更新前检查通过!"); - return true; - } - - private async Task AskUserForUpdatePermission() - { - Console.WriteLine("有新更新可用。立即更新?(y/n)"); - var response = Console.ReadLine(); - return response?.ToLower() == "y"; - } - - private async Task CheckNetworkConnection() - { - // 实现网络检查 - return true; - } - - private bool HasSufficientDiskSpace(long requiredBytes) - { - // 实现磁盘空间检查 - return true; - } -} -``` - - - -### 最佳实践 - -1. **始终处理异常**:使用 `AddListenerException()` 捕获并记录所有更新错误。 - -2. **更新前验证环境**:使用 `AddCustomOption()` 检查系统要求、依赖项和可用磁盘空间。 - -3. **提供用户反馈**:订阅下载进度事件以向用户显示实时更新。 - -4. **保护用户数据**:使用黑名单功能从更新中排除用户配置文件和数据。 - -5. **实现重试逻辑**:在错误处理程序中,为临时故障实现智能重试机制。 - -6. **测试自定义操作**:彻底测试所有自定义操作,以确保它们不会阻止合法更新。 - -7. **使用适当的超时**:根据预期文件大小和网络条件设置合理的超时值。 - -8. **记录所有内容**:维护更新过程的详细日志以便故障排除。 - - - -### 常见用例 - -#### 用例 1:环境验证 - -更新前,验证是否满足所有先决条件: - -```c# -private async Task ValidateEnvironment() -{ - // 检查 .NET 运行时版本 - if (!IsRuntimeVersionSupported()) - return false; - - // 验证所需服务是否正在运行 - if (!AreServicesRunning(new[] { "ServiceA", "ServiceB" })) - return false; - - // 检查文件权限 - if (!HasWritePermission(installPath)) - return false; - - return true; -} -``` - -#### 用例 2:自定义更新通知 - -在更新过程中向用户显示自定义通知: - -```c# -private void NotifyUser(object sender, MultiDownloadStatisticsEventArgs e) -{ - // 显示系统托盘通知 - ShowNotification($"正在更新:{e.ProgressPercentage:F0}%"); - - // 更新应用程序标题 - UpdateWindowTitle($"正在下载更新... {e.ProgressPercentage:F0}%"); -} -``` - -#### 用例 3:条件更新 - -根据特定条件应用更新: - -```c# -private async Task ShouldApplyUpdate() -{ - // 检查是否在营业时间内 - var now = DateTime.Now; - if (now.Hour >= 9 && now.Hour <= 17) - { - // 在营业时间推迟更新 - return false; - } - - // 检查是否有关键操作正在运行 - if (IsCriticalOperationInProgress()) - { - return false; - } - - return true; -} -``` - - - -### 故障排除 - -#### 问题:自定义操作无限期地阻止更新 - -**解决方案**:确保您的自定义操作具有超时和适当的错误处理: - -```c# -private async Task CustomOperationWithTimeout() -{ - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - try - { - await YourAsyncOperation(cts.Token); - return true; - } - catch (OperationCanceledException) - { - Console.WriteLine("操作超时"); - return false; - } -} -``` - -#### 问题:事件未触发 - -**解决方案**:确保在调用 `LaunchAsync()` 之前注册事件监听器: - -```c# -// 正确的顺序 -await new GeneralClientBootstrap() - .AddListenerException(OnException) // 先注册 - .SetConfig(configinfo) - .LaunchAsync(); // 最后启动 -``` - -#### 问题:跳过选项不起作用 - -**解决方案**:验证服务器是否未将更新设置为强制更新。跳过选项仅适用于非强制更新。 - - - -### 适用于 - -| 产品 | 版本 | -| -------------- | ---------------- | -| .NET | 5, 6, 7, 8, 9, 10| -| .NET Framework | 4.6.1 | -| .NET Standard | 2.0 | -| .NET Core | 2.0 | - -### 另请参阅 - -- [GeneralUpdate.ClientCore](./GeneralUpdate.ClientCore.md) - 主要客户端更新组件 -- [GeneralUpdate.Core](./GeneralUpdate.Core.md) - 核心更新逻辑 -- [快速入门指南](../quickstart/Quik%20start.md) - GeneralUpdate 入门