You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Electron/React应用崩溃重启后显示模板窗口问题排查

问题描述

我使用React和Electron开发了一款应用,目前遇到如下问题:应用启动时偶尔会因IPC通信失败触发未捕获异常。我按照文档在Electron入口文件中添加了异常处理器,但重启后并未加载我的应用,而是显示Electron模板窗口。

异常处理器代码:

process.on("uncaughtException", function (err) {
  //log the message and stack trace
  log.warn("uncaught Exception: ", err);
  //relaunch the app
  app.relaunch({ args: [] });
  app.exit(0);
  handleLaunch(tray, window, store, __dirname);
});

handleLaunch函数代码:

// Library Imports
const { app, BrowserWindow, dialog } = require("electron");
const { autoUpdater } = require("electron-updater");
const log = require("electron-log");
//Functions
const { createIpcBusBridge } = require("../ipc/createIpcBusBridge");
const { createWindow } = require("../window/createWindow");
const { createTray } = require("../tray/createTray");

const handleLaunch = (tray, window, store, directoryName) => {
  const isDev = !app.isPackaged;
  if (isDev === true) {
    const reactDevToolsId = "fmkadmapgofadopljbjfkapdkoienihi";
    const {
      default: installExtension,
      REDUX_DEVTOOLS,
    } = require("electron-devtools-installer");
    installExtension([REDUX_DEVTOOLS, reactDevToolsId])
      .then((name) => console.log(`Added Extension:  ${name}`))
      .catch((err) => console.log("An error occurred: ", err));
  }
  log.info("CREATING WINDOW...........");
  window = createWindow(directoryName, process, store, isDev);
  if (tray !== undefined) {
    tray.destroy();
  }

  tray = createTray(window, directoryName, process);
  createIpcBusBridge();

  app.on("activate", function () {
    /* 
      On macOS it's common to re-create a window in the app when the
      dock icon is clicked and there are no other windows open.
    */
    if (BrowserWindow.getAllWindows().length === 0)
      createWindow(window, tray, directoryName, process, store);
  });

  if (isDev === false) {
    autoUpdater.on("update-available", (_event, releaseNotes, releaseName) => {
      const dialogOpts = {
        type: "info",
        buttons: ["Ok"],
        title: "Application Update",
        message: process.platform === "win32" ? releaseNotes : releaseName,
        detail: "A new version is being downloaded.",
      };
      dialog.showMessageBox(dialogOpts, (response) => {});
    });

    autoUpdater.on("update-downloaded", (_event, releaseNotes, releaseName) => {
      const dialogOpts = {
        type: "info",
        buttons: ["Restart", "Later"],
        title: "Application Update",
        message: process.platform === "win32" ? releaseNotes : releaseName,
        detail:
          "A new version has been downloaded. Restart the application to apply the updates.",
      };
      dialog.showMessageBox(dialogOpts).then((returnValue) => {
        if (returnValue.response === 0) autoUpdater.quitAndInstall();
      });
    });
  }
};

module.exports = { handleLaunch };

createWindow函数代码:

// Library Imports
const { BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const url = require("url");
const log = require("electron-log");
// Functions
const { handleResolutionPref } = require("./handleResolutionPref");
const { handlePositionPref } = require("./handlePositionPref");
const { maximizeWindow } = require("./maximizeWindow");
const { minimizeWindow } = require("./minimizeWindow");
const { closeWindow } = require("./closeWindow");

const createWindow = (directoryName, process, store, isDev) => {
  try {
    const [screenWidth, screenHeight] = handleResolutionPref(store);
    const [screenXCoordinate, screenYCoordinate, isScreenPositionCustom] = 
      handlePositionPref(store);

    let window = null;
    const browserWindowOptions = {
      width: screenWidth,
      height: screenHeight,
      frame: false,
      fullscreenable: true,
      resizable: true,
      transparent: false,
      webPreferences: {
        preload: path.join(directoryName, "preload.bundle.js"),
        contextIsolation: false,
        nodeIntegration: true,
        sandbox: false,
        webSecurity: false,
      },
      icon: path.join(directoryName, "icon.ico"),
    };

    // Create the browser window.
    if (isScreenPositionCustom === true) {
      browserWindowOptions.x = screenXCoordinate;
      browserWindowOptions.y = screenYCoordinate;

      window = new BrowserWindow(browserWindowOptions);
    } else {
      window = new BrowserWindow(browserWindowOptions);
    }

    window.on("closed", () => (window = null));

    if (isDev === true) {
      window.loadURL("http://localhost:3000");
    } else {
      window.loadURL(
        url.format({
          pathname: path.join(directoryName, "index.html"),
          protocol: "file:",
          slashes: true,
        })
      );
    }

    // Handle window toggling for custom titlebar
    ipcMain.on("windowMinimize", () => minimizeWindow(window));
    ipcMain.on("windowMaximize", () => maximizeWindow(window));
    ipcMain.on("windowClose", () => closeWindow(window));

    // Open the DevTools.
    // window.webContents.openDevTools()

    return window;
  } catch (error) {
    log.info("CREATE WINDOW ERROR INSIDE FUNCTION: ", error);
  }
};

module.exports = { createWindow };
错误原因分析
  1. app.exit(0)终止进程后后续代码无法执行
    异常处理器中调用app.exit(0)会立即终止当前进程,后面的handleLaunch(tray, window, store, __dirname)完全不会运行。而app.relaunch()只是调度新进程启动,要等当前进程退出后才会执行,新进程会从头执行入口文件的启动流程,不会在当前进程里重新调用handleLaunch。

  2. activate事件中createWindow参数顺序错误
    在handleLaunch的app.on("activate")回调里,调用createWindow时传参顺序是createWindow(window, tray, directoryName, process, store),但createWindow的定义是(directoryName, process, store, isDev),参数顺序完全错位,导致createWindow内逻辑出错(比如store参数拿到的是isDev的值),窗口初始化失败后就会显示Electron默认模板窗口。

  3. IPC事件重复绑定引发异常
    每次调用createWindow都会重新绑定ipcMain.on("windowMinimize")等事件,多次绑定同一事件会导致触发时执行多个处理函数,可能引发IPC相关异常,尤其是重启或多次创建窗口时。

  4. createWindow错误处理不完整
    createWindow的try/catch捕获错误后仅打印日志,返回undefined,后续tray = createTray(window, ...)会因window为undefined出错,导致应用初始化失败。

修复方案

1. 修正异常处理器逻辑

移除app.exit(0)后的handleLaunch调用,同时传递原进程参数给app.relaunch,确保新进程保留启动参数:

process.on("uncaughtException", function (err) {
  log.warn("uncaught Exception: ", err);
  // 传递原进程的参数(排除第一个执行路径)
  app.relaunch({ args: process.argv.slice(1) }); 
  app.exit(0);
});

2. 修正activate事件的createWindow传参

按照createWindow定义的参数顺序传递,保证参数正确:

app.on("activate", function () {
  if (BrowserWindow.getAllWindows().length === 0)
    createWindow(directoryName, process, store, isDev);
});

3. 避免IPC事件重复绑定

将IPC事件绑定移到入口文件(如main.js)中,只初始化一次,通过获取当前聚焦窗口来处理操作,避免绑定到特定窗口实例:

// 入口文件中全局绑定一次IPC事件
const { ipcMain, BrowserWindow } = require('electron');
const { minimizeWindow, maximizeWindow, closeWindow } = require('./window/...');

ipcMain.on("windowMinimize", () => {
  const activeWindow = BrowserWindow.getFocusedWindow();
  if (activeWindow) minimizeWindow(activeWindow);
});

ipcMain.on("windowMaximize", () => {
  const activeWindow = BrowserWindow.getFocusedWindow();
  if (activeWindow) maximizeWindow(activeWindow);
});

ipcMain.on("windowClose", () => {
  const activeWindow = BrowserWindow.getFocusedWindow();
  if (activeWindow) closeWindow(activeWindow);
});

然后删除createWindow内的IPC绑定代码。

4. 完善createWindow错误处理

捕获错误后抛出异常,让上层处理,避免返回undefined导致后续流程出错:

const createWindow = (directoryName, process, store, isDev) => {
  try {
    // ... 原有逻辑
    return window;
  } catch (error) {
    log.error("CREATE WINDOW ERROR INSIDE FUNCTION: ", error);
    // 抛出错误,让调用者处理
    throw error;
  }
};

内容的提问来源于stack exchange,提问作者Frank Edwards

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 11:02:09