Skip to content

Commit a529fb5

Browse files
committed
1.MSIX 打包后端进程启动修复 2.增加修复文档
1 parent b051d47 commit a529fb5

7 files changed

Lines changed: 537 additions & 133 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export default defineConfig({
129129
text: '软件优化',
130130
collapsed: false,
131131
items: [
132+
{ text: 'MSIX 后端启动修复', link: '/changelog/optimization/msix-backend-startup-fix' },
132133
{ text: '消除应用启动黑框', link: '/changelog/optimization/startup-black-screen-elimination' }
133134
]
134135
}

docs/changelog/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@
3434

3535
| 文档 | 说明 |
3636
|------|------|
37+
| [MSIX 打包后端进程启动修复](./optimization/msix-backend-startup-fix) | MSIX 后端启动修复 + 代码拆分重构 — 日期:2026-06-17 |
3738
| [消除应用启动黑框闪现](./optimization/startup-black-screen-elimination) | Tauri + PyInstaller 启动控制台黑框彻底消除 — 日期:2026-06-13 |
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# MSIX 打包后端进程启动修复
2+
3+
> 日期:2026-06-17
4+
> 标签:`MSIX` `后端启动` `进程管理` `代码重构`
5+
6+
## 问题描述
7+
8+
MSIX 格式打包安装后,应用启动时一直显示加载动画(转圈圈),无法访问后台服务。MSI 格式正常。
9+
10+
## 根因分析
11+
12+
### 原因 1:MSIX 环境检测失效
13+
14+
`is_msix_env()` 函数通过检查 `PACKAGE_FAMILY_NAME` / `PACKAGE_FULL_NAME` 环境变量来判断是否运行在 MSIX 环境中。
15+
16+
但此 MSIX 包的 `Package.appxmanifest``EntryPoint="Windows.FullTrustApplication"`(完全信任模式),**不会设置这两个环境变量**,导致检测永远返回 `false`,代码走了 Tauri shell 插件 sidecar 路径,而 MSIX 包内没有对应的资源结构,后端无法启动。
17+
18+
**修复**:改用 `current_exe()` 路径是否包含 `WindowsApps` 来判断。MSIX 安装的 exe 一定在 `C:\Program Files\WindowsApps\` 目录下。
19+
20+
### 原因 2:DETACHED_PROCESS 在 MSIX 下无效
21+
22+
MSIX 完全信任模式下,`DETACHED_PROCESS``0x00000008`)进程标志没有生效,子进程仍然附着在父进程的控制台上,出现控制台黑框,且关闭黑框会导致后端进程退出。
23+
24+
**修复**:改用 `CREATE_NO_WINDOW``0x08000000`),直接不分配控制台,进程在后台静默运行。
25+
26+
### 原因 3:代码耦合度高,MSIX 与 MSI 逻辑混杂
27+
28+
MSIX 和 MSI/NSIS 的后端启动逻辑完全不同(进程标志、端口分配、进程句柄管理),混在同一个文件中维护困难,修改 MSIX 逻辑容易影响 MSI。
29+
30+
**修复**:将代码拆分为三个文件:
31+
32+
| 文件 | 职责 |
33+
|------|------|
34+
| `backend/mod.rs` | 公共入口:环境检测、`#[tauri::command]`、按环境转发 |
35+
| `backend/msix.rs` | MSIX 独立逻辑:`Mutex<Option<Child>>`、固定端口 8991、`CREATE_NO_WINDOW` |
36+
| `backend/normal.rs` | 非 MSIX 逻辑:`ProcessHandle` 枚举、Tauri shell 插件 sidecar、动态端口 |
37+
38+
## 解决方案
39+
40+
### 1. MSIX 环境检测
41+
42+
```rust
43+
fn is_msix_env() -> bool {
44+
#[cfg(target_os = "windows")]
45+
{
46+
if let Ok(exe) = std::env::current_exe() {
47+
if let Some(path) = exe.to_str() {
48+
return path.contains("WindowsApps");
49+
}
50+
}
51+
false
52+
}
53+
#[cfg(not(target_os = "windows"))]
54+
{ false }
55+
}
56+
```
57+
58+
### 2. MSIX 进程标志
59+
60+
```rust
61+
fn new_detached_cmd(program: &str) -> Command {
62+
let mut cmd = Command::new(program);
63+
#[cfg(target_os = "windows")]
64+
cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
65+
cmd
66+
}
67+
```
68+
69+
### 3. 代码拆分
70+
71+
```
72+
src-tauri/src/backend/
73+
├── mod.rs # 公共入口 + #[tauri::command] + 统一转发
74+
├── msix.rs # MSIX 独立逻辑
75+
└── normal.rs # 非 MSIX 逻辑
76+
```
77+
78+
`mod.rs` 中的 `is_msix_env()` 在每次公开 API 调用时实时检测,根据结果转发到 `msix::``normal::` 对应函数,两者完全隔离,不共享全局状态。
79+
80+
## 涉及文件
81+
82+
| 文件 | 改动 |
83+
|------|------|
84+
| `src-tauri/src/backend.rs` | 已删除,拆分为 backend/ 子目录 |
85+
| `src-tauri/src/backend/mod.rs` | 新建:公共入口、环境检测、统一转发 |
86+
| `src-tauri/src/backend/msix.rs` | 新建:MSIX 独立逻辑 |
87+
| `src-tauri/src/backend/normal.rs` | 新建:非 MSIX 逻辑 |
88+
89+
## 效果验证
90+
91+
- ✅ MSIX 安装包:后端正常启动,无控制台黑框
92+
- ✅ MSI 安装包:后端正常启动,无控制台黑框(不受拆分影响)
93+
- ✅ 开发模式:后端正常启动
94+
- ✅ MSIX 关闭应用时后端进程正确清理

src-tauri/src/backend/mod.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
mod msix;
2+
mod normal;
3+
4+
use tauri::AppHandle;
5+
6+
/// 检测当前是否运行在 MSIX/AppX 环境中
7+
///
8+
/// MSIX 包使用 EntryPoint="Windows.FullTrustApplication"(完全信任模式),
9+
/// 不会设置 PACKAGE_FAMILY_NAME 等环境变量。只能通过 exe 路径是否在
10+
/// WindowsApps 目录下来判断。
11+
fn is_msix_env() -> bool {
12+
#[cfg(target_os = "windows")]
13+
{
14+
if let Ok(exe) = std::env::current_exe() {
15+
if let Some(path) = exe.to_str() {
16+
return path.contains("WindowsApps");
17+
}
18+
}
19+
false
20+
}
21+
#[cfg(not(target_os = "windows"))]
22+
{
23+
false
24+
}
25+
}
26+
27+
// ── Tauri commands(放在 mod.rs 中,让 #[tauri::command] 宏正确生成内部符号) ──
28+
29+
#[tauri::command]
30+
pub async fn is_backend_ready() -> Result<bool, String> {
31+
if is_msix_env() {
32+
Ok(msix::is_backend_ready().await)
33+
} else {
34+
normal::is_backend_ready().await
35+
}
36+
}
37+
38+
#[tauri::command]
39+
pub fn get_backend_url() -> Result<String, String> {
40+
if is_msix_env() {
41+
Ok("http://127.0.0.1:8991".to_string())
42+
} else {
43+
normal::get_backend_url()
44+
}
45+
}
46+
47+
// ── 统一入口:根据环境转发到对应模块 ──
48+
49+
pub fn spawn_backend(app: &AppHandle) -> String {
50+
if is_msix_env() {
51+
eprintln!("[EMS] detected MSIX environment, using msix backend module");
52+
msix::spawn_backend(app)
53+
} else {
54+
normal::spawn_backend(app)
55+
}
56+
}
57+
58+
pub async fn wait_backend_ready(url: &str) {
59+
if is_msix_env() {
60+
msix::wait_backend_ready().await
61+
} else {
62+
normal::wait_backend_ready(url).await
63+
}
64+
}
65+
66+
pub fn cleanup_managed_process() {
67+
if is_msix_env() {
68+
msix::cleanup()
69+
} else {
70+
normal::cleanup()
71+
}
72+
}
73+
74+
pub fn stop_backend() {
75+
if is_msix_env() {
76+
msix::stop()
77+
} else {
78+
normal::stop()
79+
}
80+
}

0 commit comments

Comments
 (0)