编译后Electron应用存于网络共享文件夹无法启动问题
问题
我开发了一款管理NeDB数据库的Electron应用,在VS Code中启动运行完全正常。但执行npm run package打包后,将输出文件夹移至网络共享路径,运行.exe文件时窗口短暂空白后无提示闪退。
- 已确认文件夹权限无问题,该.exe在本地及其他PC均可正常运行。
- 闪退之后文件夹无法删除,提示文件被占用但任务管理器无对应进程。
- 测试通过Electron Forge新建的项目打包后放共享文件夹也出现相同问题。
- 通过终端启动时,输出显示GPU进程启动失败(error_code=18),最终因GPU进程不可用退出。
解决方案
针对网络共享路径下GPU进程启动失败的问题,可尝试以下几种修复方式:
1. 禁用GPU硬件加速
在Electron主进程初始化前添加禁用GPU的参数,绕过GPU进程启动问题:
// 在main.js最顶部添加 app.commandLine.appendSwitch('disable-gpu'); app.commandLine.appendSwitch('disable-software-rasterizer');
或者在创建BrowserWindow时添加webPreferences配置:
win = new BrowserWindow({ // 其他配置... webPreferences: { // 其他配置... disableHardwareAcceleration: true } });
2. 修改asar打包配置
网络共享路径可能对asar包的读取存在兼容性问题,可尝试禁用asar或指定部分文件不打包进asar:
在forge.config.js的packagerConfig中修改:
packagerConfig: { // 其他配置... asar: false, // 直接禁用asar // 或者保留asar但解压native模块 // asar: { // unpack: "**/*.node" // } }
3. 调整应用临时文件路径
Electron默认会在系统临时目录创建缓存,网络路径下可能存在权限异常,手动指定临时目录到本地路径:
// 在main.js的app.whenReady()之前添加 app.setPath('userData', path.join(process.env.LOCALAPPDATA, 'gestione-marcature-laser')); app.setPath('temp', path.join(process.env.TEMP, 'gestione-marcature-laser'));
4. 检查共享路径权限配置
确保网络共享路径支持文件锁定和执行权限,部分SMB共享配置可能限制了可执行文件的运行环境,可尝试开启"允许网络用户更改我的文件"权限,或使用SMBv3协议连接共享。
相关配置文件
package.json
{"name": "gestione-marcature-laser","version": "2.0.0","description": "Gestore Marcature Laser","main": "index.js","scripts": {"start": "electron-forge start","package": "electron-forge package","make": "electron-forge make"},"author": "Nemanja G.","license": "ISC","dependencies": {"bcryptjs": "^2.4.3","electron-prompt": "^1.7.0","electron-squirrel-startup": "^1.0.0","jquery": "^3.5.1","moment": "^2.29.1","nedb-async": "^0.1.6","select2": "^4.0.13","xlsx": "^0.16.9"},"devDependencies": {"@electron-forge/cli": "^6.2.1","@electron-forge/maker-deb": "^6.2.1","@electron-forge/maker-rpm": "^6.2.1","@electron-forge/maker-squirrel": "^6.2.1","@electron-forge/maker-zip": "^6.2.1","@electron-forge/plugin-auto-unpack-natives": "^6.2.1","electron": "^25.2.0"}}
forge.config.js
const path = require('path') module.exports = { packagerConfig: { icon: './assets/logo', asar: { //unpack: "**/node_modules/pdf-to-printer/**/*" }, out: "gestione-marcature" }, rebuildConfig: {}, makers: [ { name: '@electron-forge/maker-squirrel', config: { iconUrl: path.resolve(__dirname, './assets/logo.ico'), setupIcon: path.resolve(__dirname, './assets/logo.ico'), }, }, { name: '@electron-forge/maker-zip', platforms: ['darwin'], }, { name: '@electron-forge/maker-deb', config: { icon: './Styles/static/icon.png', }, }, { name: '@electron-forge/maker-rpm', config: {}, }, ], plugins: [ { name: '@electron-forge/plugin-auto-unpack-natives', config: {}, }, ] };
main.js
const { app, BrowserWindow, dialog } = require('electron'); const path = require('path'); const url = require('url'); const { ipcMain } = require('electron'); const bcrypt = require('bcryptjs'); const prompt = require('electron-prompt'); /* SEZIONE PASSWORD, attualmente quella di sap. Usare codice sotto per generare hash */ /* const password = "PASS" const saltRounds = 10 bcrypt.genSalt(saltRounds, function (err, salt) { if (err) { throw err } else { bcrypt.hash(password, salt, function (err, hash) { if (err) { throw err } else { console.log(hash); dialog.showErrorBox('Errore', hash); //$2a$10$FEBywZh8u9M0Cec/0mWep.1kXrwKeiWDba6tdKvDfEBjyePJnDT7K } }) } }) */ //hash = "$2b$10$6OT.h/W3EnHAxiRC15OfR.BdEseDilja8m5Z2LPtEeed3pNYNsJwO"; hash = "$2b$10$OMZlnwGs8av1Kwhbx5ya5uUU2HMCdU.Gyn//s0.J2/mSklzoBEjny"; let win = null; let win2 = null; function createWindow() { // Create the browser window. win = new BrowserWindow({ width: 1000, height: 720, webPreferences: { nodeIntegration: true, preload: path.join(__dirname, 'preload.js') }, icon: path.join(__dirname, './assets/logo.png'), show: false }); // maximize it win.maximize(); // and load the index.html of the app. win.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true })); win.once('ready-to-show', () => { win.show() }); win.on("close", (evt) => { //Cancel the current event to prevent window from closing //options object for dialog.showMessageBox(...) let options = { type: "question", buttons: ["&Sì", "&No", "&Cancella"], defaultId: 2, title: "Attenzione!", message: "Confermi la chiusura del programma Gestione Database Marcature?", detail: "Potrai riaprirlo dall'icona nel desktop.", icon: path.join(__dirname, './assets/logo.png'), cancelId: 2, noLink: true, normalizeAccessKeys: true }; let res = dialog.showMessageBoxSync(win, options); if (res === 0) { //Yes button pressed } else if (res === 1) { //No button pressed evt.preventDefault(); } else if (res === 2) { //Cancel button pressed evt.preventDefault(); } }); // Open the DevTools. win.removeMenu(); //win.webContents.openDevTools(); } ipcMain.on('openPage', async (_, arg) => { if (!win2) { let pwd = await prompt({ title: 'Attenzione!', label: 'Inserire la password:', type: 'input', height: 200, inputAttrs: { type: 'password', required: true }, icon: path.join(__dirname, './assets/logo.png'), menuBarVisible: false, buttonLabels: 'ok', customStylesheet: path.join(__dirname, 'styles', 'prompt-styles.css') }); bcrypt.compare(pwd, hash).then(res => { if (res) { win2 = new BrowserWindow({ parent: win, webPreferences: { nodeIntegration: true, preload: path.join(__dirname, 'preload.js') }, frame: false, width: 1000, height: 720, show: false }); win2.maximize(); win2.loadURL(url.format({ pathname: path.join(__dirname, arg.page), protocol: 'file:', slashes: true })); win2.once('ready-to-show', () => { win2.show() }); win2.on('close', _ => { win2 = null }); //win2.webContents.openDevTools(); } else { dialog.showErrorBox('Errore', 'Password non corretta!'); } }); } }); ipcMain.on('closePage', _ => { win2.close(); }); ipcMain.on('showMessageBox', (_, arg) => { dialog.showMessageBox({ title: arg.title, message: arg.message }) }); const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { app.on('second-instance', (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. if (win) { if (win.isMinimized()) { win.restore(); } win.focus(); } }) // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(createWindow); // Quit when all windows are closed. app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit(); } }); }
内容的提问来源于stack exchange,提问作者Nemanja Gavranovic
相关产品推荐
相关产品推荐

