fix(main): 修复 config.json 并发读写导致的 EBUSY 主进程崩溃弹窗

- 主进程 6 个独立 Store 实例合并为共享单例(window-size/lyric/server/deviceInfo/window 复用 config.ts 实例)
- initializeConfig 幂等化,修复 set-store-value IPC 监听重复注册导致的双倍写入
- 窗口/歌词窗 move、resize 保存增加 500ms 防抖,拖动时不再每秒几十次写盘,close 时冲刷落盘
- store 读写 IPC handler 与窗口状态读写加 try-catch,撞锁丢弃单次写入而非崩溃
- 全局 uncaughtException 兜底:文件锁类错误(EBUSY/EPERM 等)仅记日志,其余异常保留报错弹窗

Closes #714
This commit is contained in:
alger
2026-07-05 14:06:01 +08:00
parent f39100e9eb
commit 9f5473e14d
7 changed files with 154 additions and 49 deletions
+20 -1
View File
@@ -1,7 +1,26 @@
import { electronApp, optimizer } from '@electron-toolkit/utils';
import { app, ipcMain, nativeImage, protocol, session } from 'electron';
import { app, dialog, ipcMain, nativeImage, protocol, session } from 'electron';
import { join } from 'path';
// 全局兜底(#714):Windows 上 config.json 等文件可能被杀毒/云同步软件短暂锁定,
// electron-store 读写撞锁会抛 EBUSY 等未捕获异常,Electron 默认弹出致命错误框。
// 对带 path 的文件系统锁类错误仅记录日志;其余异常保留报错弹窗以免掩盖真 bug。
const FILE_LOCK_ERROR_CODES = new Set(['EBUSY', 'EPERM', 'EACCES', 'EAGAIN', 'EMFILE', 'ENFILE']);
process.on('uncaughtException', (error: NodeJS.ErrnoException) => {
if (error?.code && FILE_LOCK_ERROR_CODES.has(error.code) && typeof error.path === 'string') {
console.error('[main] 文件被占用/锁定,已忽略本次读写:', error.message);
return;
}
console.error('[main] 未捕获异常:', error);
dialog.showErrorBox(
'A JavaScript error occurred in the main process',
error?.stack || String(error)
);
});
process.on('unhandledRejection', (reason) => {
console.error('[main] 未处理的 Promise 拒绝:', reason);
});
// 必须在 app.whenReady() 之前注册自定义协议为特权协议,
// 否则 http(s) 页面(dev server、生产环境的 file://)无法把 local:// 当成
// 安全/可 fetch/可流式的资源加载,会触发 CORS 拦截或 net::ERR_UNKNOWN_URL_SCHEME