用户可借助该项目快速构建支持热更新的 Windows 工具集。它采用插件化架构,主程序通过 PluginManager 加载/卸载工具插件,利用 AssemblyLoadContext 实现隔离加载,UpdateService 支持后台检查更新与热切换。【此简介由AI生成】
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 29 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 |
MToolbox - Windows 工具箱
基于 C# + WPF + AssemblyLoadContext 插件化 架构的 Windows 工具箱,支持热更新。
架构概览
MToolbox.Shell (WPF 主程序) ── IToolPlugin 接口 ── 插件.dll
│ │
├── PluginManager ──── 加载/卸载/热切换(通过 IPluginDisplay 与 UI 解耦)
├── PluginLoadContext ──── 可回收 Assembly 隔离加载 + 临时文件自动清理
└── UpdateService ──── 后台检查更新 → HTTP 下载 → 热切换(支持 CancellationToken)
│
MToolbox.WebServer (ASP.NET, linux-x64) ─┘ 提供 manifest API + 文件下载
设计原则
- 依赖注入:使用
Microsoft.Extensions.DependencyInjection,所有服务(PluginManager、UpdateService、MainViewModel)由 DI 容器管理 - UI 解耦:
IPluginDisplay接口隔离插件管理器对 WPF UI 线程的依赖,便于单元测试 - 共享契约:
UpdateManifest、PluginInfo等 DTO 统一定义在MToolbox.Contracts中,Shell 和 WebServer 共享 - 单实例互斥体:使用命名
Mutex确保单实例运行,避免进程名匹配的误判 - 异步取消:
UpdateService.StartAutoCheckAsync支持CancellationToken,程序退出时优雅取消
项目结构
MToolbox/
├── MToolbox.sln # 解决方案
├── global.json # SDK 版本锁定(CI 一致性)
├── build.bat # 一键构建脚本(编译 + 打包 + 发布)
├── scripts/
│ └── setup-ci-environment.ps1 # CI 机器环境部署脚本
├── src/
│ ├── MToolbox.Contracts/ # 接口契约层(多目标:net10.0; net10.0-windows)
│ │ ├── IToolPlugin.cs # 插件必须实现的接口(仅 windows 目标编译)
│ │ ├── Contracts/
│ │ │ ├── PluginInfo.cs # 插件信息 DTO(Shell + WebServer 共享)
│ │ │ └── UpdateManifest.cs # 更新清单 DTO
│ │ └── Utilities/
│ │ └── FormatUtils.cs # 公共工具方法(FormatBytes 等)
│ ├── MToolbox.Shell/ # 主程序 (WPF)
│ │ ├── App.xaml(.cs) # 应用入口(DI 容器、Mutex 单实例、崩溃日志)
│ │ ├── MainWindow.xaml(.cs) # 主窗口(DI 构造注入、托盘 try/finally 清理)
│ │ ├── Resources/icon.ico # 应用图标
│ │ ├── Services/
│ │ │ ├── IPluginDisplay.cs # 插件 UI 操作接口(UI 线程隔离)
│ │ │ ├── PluginLoadContext.cs # 插件加载上下文(隔离 Assembly、临时文件清理)
│ │ │ ├── PluginManager.cs # 插件管理器(通过 IPluginDisplay 与 UI 交互)
│ │ │ └── UpdateService.cs # 更新服务(CancellationToken 支持)
│ │ └── ViewModels/
│ │ ├── MainViewModel.cs # 主视图模型(实现 IPluginDisplay)
│ │ ├── PluginItem.cs # 插件 UI 条目模型
│ │ └── RelayCommand.cs # 泛型 RelayCommand
│ ├── MToolbox.Tests/ # 单元测试 (xUnit)
│ │ └── UpdateServiceTests.cs # 版本比较、CollectUpdates 纯函数测试
│ ├── MToolbox.WebServer/ # 更新服务器 (ASP.NET)
│ │ ├── Program.cs # 入口:API 路由 + 静态文件服务
│ │ ├── appsettings.json # 配置
│ │ ├── Properties/launchSettings.json
│ │ ├── Pages/
│ │ │ ├── Index.cshtml # 插件发布管理页面
│ │ │ └── Index.cshtml.cs # 页面模型(使用共享 DTO)
│ │ └── README.md # WebServer 部署文档
│ └── Plugins/
│ ├── MToolbox.Plugin.Sample/ # 示例插件
│ ├── MToolbox.Plugin.QuickActions/ # 快捷操作插件
│ └── MToolbox.Plugin.SystemInfo/ # 系统信息插件(WMI)
└── build/ # 构建产物
├── Shell/ # 主程序 (win-x64, 单文件)
├── WebServer/ # 更新服务器 (linux-x64, 自包含)
├── Plugins/ # 插件 DLL
└── Server/ # 更新服务器静态文件
├── manifest.json # 插件版本清单
├── shell-version.json # 主程序版本
├── plugins/ # 插件下载
└── shell/ # 主程序下载
快速开始
前置条件
- .NET 10 SDK(>= 10.0.302,由
global.json锁定)
CI 环境部署(Windows Server 2025)
全新机器一键部署编译环境:
.\scripts\setup-ci-environment.ps1
脚本会通过 winget 自动安装 .NET 10 SDK 和 Git,并验证构建是否通过。详见脚本注释。
构建
# 一键构建(编译 + 打包 + 生成更新清单 + 交叉编译 WebServer)
.\build.bat
# 可通过环境变量指定更新服务器地址
set UPDATE_SERVER_BASE=http://your-server:5000
.\build.bat
构建过程(共 6 步):
| 步骤 | 说明 |
|---|---|
| [1/6] | 清理旧构建 |
| [2/6] | 编译全部项目 |
| [3/6] | 发布 Shell 主程序(win-x64 单文件,自包含) |
| [4/6] | 准备更新服务器文件(生成 manifest.json、shell-version.json) |
| [5/6] | 交叉编译 WebServer 为 linux-x64(单文件,自包含) |
| [6/6] | 打印生成内容预览 |
运行测试
dotnet test src/MToolbox.Tests/
运行客户端
.\build\Shell\MToolbox.exe
部署 WebServer 到 Linux
# 1. 上传 build\WebServer\ 到 Linux 服务器
# 2. 将 build\Server\ 放在 build\WebServer\ 的同级目录
# 3. 启动
chmod +x MToolbox.WebServer
MT_WEB_URL=http://0.0.0.0:5000 ./MToolbox.WebServer
WebServer 的环境变量:
| 变量 | 说明 | 默认值 |
|---|---|---|
MT_WEB_URL |
监听地址(最高优先级) | — |
ASPNETCORE_URLS |
ASP.NET Core 标准监听地址 | — |
MT_SERVER_ROOT |
插件文件根目录 | ../Server |
开发新插件
- 在
src/Plugins/下新建 WPF 类库项目 - 引用
MToolbox.Contracts项目 - 将输出目录设为
$(SolutionDir)build\Plugins\ - 实现
IToolPlugin接口:
public class MyTool : IToolPlugin
{
public string Id => "my.tool";
public string Name => "我的工具";
public string Version => "1.0.0";
public string Description => "功能描述";
public string Author => "作者";
public string? Icon => null;
public UserControl CreateView() => new MyToolView();
public void Initialize() { /* 初始化 */ }
public void Shutdown() { /* 清理 */ }
}
编译后 DLL 自动出现在 build\Plugins\,主程序运行时会自动发现并加载。
使用公共工具方法
MToolbox.Contracts.Utilities.FormatUtils 提供了常用的格式化方法:
using MToolbox.Contracts.Utilities;
var memStr = FormatUtils.FormatBytes(workingSet); // "1.5 GB"
热更新工作流
开发者 WebServer (Linux) 客户端 (Windows)
│ │ │
├─ 编译新版本插件 │ │
├─ build.bat ──────────────►│ 生成 manifest.json │
│ │ /plugins/*.dll │
│ │ │
│ │ ◄── GET /api/plugins/manifest ──┤ UpdateService 定时轮询
│ │ ── 返回版本清单 ─────────────►│ 发现新版本
│ │ │
│ │ ◄── GET /plugins/xxx.dll ─────┤ 下载新 DLL
│ │ ── 返回 DLL ────────────────►│
│ │ ├─ Shutdown() 旧插件
│ │ ├─ 卸载 AssemblyLoadContext + 清理临时文件
│ │ ├─ GC.Collect()
│ │ ├─ 加载新 DLL
│ │ └─ 刷新 UI(用户无感知)
更新服务器 API
UpdateService 连接 WebServer 获取更新,协议如下:
GET /api/plugins/manifest
{
"plugins": [
{
"id": "MToolbox.Plugin.Sample",
"version": "2.0.0",
"downloadUrl": "http://localhost:5000/plugins/MToolbox.Plugin.Sample.dll"
}
]
}
其他端点
| 方法 | 路由 | 说明 |
|---|---|---|
GET |
/api/shell/version |
主程序版本 |
GET |
/api/health |
健康检查 |
GET |
/plugins/{filename} |
下载插件 DLL |
GET |
/shell/{filename} |
下载主程序 |
GET |
/ |
插件发布管理页面 |
修改更新服务器地址
在 MToolbox.Shell/Services/UpdateService.cs 中修改 UpdateServerUrl 为实际地址即可。