基于 WPF + WebView2 + smh-web-uikit + smh-js-sdk 的 Windows 桌面 SMH 云盘客户端。
功能对齐 demo/electron 版,但核心宿主是 C# 原生进程:
OpenFileDialog)CreateWebFileSystemFileHandle 把 本地绝对路径 直接转成浏览器原生 File 对象smh-js-sdk 基于该 File 做分片上传 / 断点续传 / 暂停恢复 / 取消smh-web-uikit 的 SpaceDrive 组件承载demo/c-sharp-pc/ ├── SmhDrive.csproj # WPF 项目(.NET 8) ├── App.xaml(.cs) # 应用入口 ├── MainWindow.xaml(.cs) # 主窗口(承载 WebView2,注入 C# 桥对象) ├── FileProvider.cs # C# → JS 桥:RequestFile / OpenFilePicker ├── publish.ps1 # 一键打包:先构建前端,再打 self-contained exe ├── wwwroot/ # WebView2 加载的前端产物(由 webui 构建) │ ├── index.html │ └── assets/ └── webui/ # 前端源码(Vite + React + uikit) ├── index.html ├── vite.config.js ├── package.json └── src/ ├── main.jsx ├── App.jsx ├── TransferList.jsx ├── app.css ├── webview2-bridge.js └── upload-manager.js
用户点「选择本地文件上传」 ↓ JS openFilePicker() ← webview2-bridge.js ↓ window.chrome.webview.hostObjects.fileProvider.OpenFilePicker(id) ↓ C# FileProvider.OpenFilePicker → Win32 OpenFileDialog ↓ C# PostWebMessageAsJson({ type:'PICKER_RESPONSE', path:'C:\\...' }) ↓ JS getFileHandlerByPath(path) ↓ C# FileProvider.RequestFile → _webView2.Environment.CreateWebFileSystemFileHandle(path) ↓ C# PostWebMessageAsJson({ type:'FILE_RESPONSE' }, [handle]) ↓ JS handle.getFile() → 原生 File ↓ UploadManager.add(file) → smh-js-sdk client.createUploadTask → 云端
# 1. 安装依赖(首次) cd ..\..\uikit ; npm install ; npm run build:lib cd ..\demo\c-sharp-pc\webui ; npm install # 2a. 前端开发模式(热更新),浏览器访问 http://localhost:5189 cd webui npm run dev # 2b. 或者生产构建前端(输出到 ../wwwroot/) npm run build # 3. 运行 WPF 宿主(会加载 https://app.local/index.html) cd .. dotnet run --project .\SmhDrive.csproj
开发模式下如果想让 WebView2 直接指向
http://localhost:5189/,可在MainWindow.xaml.cs里临时把WebView.Source = new Uri("http://localhost:5189/")。
# 默认:win-x64 自包含单文件(先构建 webui → wwwroot,再打 exe) .\publish.ps1 # ARM64 .\publish.ps1 -Rid win-arm64 # 框架依赖版(体积小,但目标机需要 .NET 8 Desktop Runtime) .\publish.ps1 -NoSelfContained # 如果 wwwroot 已经是最新产物,跳过前端构建 .\publish.ps1 -SkipWeb
产物:publish\win-x64\SmhDrive.exe + publish\win-x64\wwwroot\。
wwwroot/**会被.csproj自动拷贝到发布目录,保持 exe 与前端资源相对关系即可。
| 模式 | 体积 | 目标机需要 |
|---|---|---|
| 自包含(默认) | 约 75 MB | WebView2 Runtime(Win11 自带) |
-NoSelfContained | 约 5 MB | .NET 8 Desktop Runtime + WebView2 Runtime |
首次启动后需要在连接页填写:
https://smhxxx.api.tencentsmh.cn)spacexxxx)前端使用 smh-js-sdk 的 token.createToken 直接签发 space_admin 级 accessToken,
不依赖 demo/server(Electron 版依赖它,因为 Electron 主进程是 Node 环境)。
凭证会保存在 WebView2 用户数据目录的 localStorage 中,下次启动自动回填。
| 能力 | Electron 版 | C# 版 |
|---|---|---|
| 宿主语言 | Node.js | .NET 8 / WPF |
| 本地文件 → File | f.path + fs.createReadStream(node-sdk) | WebView2 CreateWebFileSystemFileHandle(js-sdk) |
| 上传 SDK | smh-node-sdk | smh-js-sdk(浏览器版) |
| 断点续传 | checkpoint 持久化到 userData JSON | File 对象只驻内存,重启后任务标记为"已失效"需重新选择文件 |
| Token 来源 | Electron 主进程调 demo server → node-sdk | 浏览器端 js-sdk 直接创建 |
| 原生文件选择框 | Electron dialog.showOpenDialog | C# Microsoft.Win32.OpenFileDialog |
示例未做路径白名单。若要产品化建议在 FileProvider.RequestFile 里校验路径(仅允许
用户主动通过 OpenFilePicker 选的路径 / 白名单目录),防止前端被 XSS 后扫描整盘。