About 30% of the time my electron app does not execute the fs.readdir callback after I've reloaded the window that contains that script. When I open the application with electron ., the issue never occurs, it only ever occurs after I've reloaded the window.
I've tried adding a setTimeout of 5 seconds before executing fs.readdir, however this didn't change anything. Additionally, after the first time fs.readdir is run, all proceeding fs.readdir callbacks are never executed unless done immediately afterwards or if the window has never been reloaded before.
Anyone know a why this occurs and a solution?
mainWindow.js:
const fs = require('fs')
// read all files in "images" directory
function readDirectory(){
fs.readdir('images', (e, files) => {
// On error, show and return error
if(e) return console.error(e);
console.log(files)
});
}
readDirectory()
main.js:
const electron = require('electron')
const path = require('path')
const {app, BrowserWindow, Menu} = electron
process.env.NODE_ENV = 'development'
let mainWindow
let mainMenu
function createMainWindow(){
// Create mainWindow
mainWindow = new BrowserWindow({
icon:'icon.png',
webPreferences:{
nodeIntegration:true,
fullscreen: true
}
})
// Load html file
mainWindow.loadFile('mainWindow.html')
// Main menu
mainMenu = Menu.buildFromTemplate(mainMenuTemplate)
// Set main menu
Menu.setApplicationMenu(mainMenu)
// Garbage handling
mainWindow.on('close',()=>{
mainWindow = null
mainMenu = null // idk if necessary
})
if (process.env.NODE_ENV !== 'production'){
mainWindow.webContents.openDevTools()
}
}
app.on('ready',e=>{
createMainWindow()
})
const mainMenuTemplate = [
{
role:'reload',
accelerator:'cmdorctrl+r',
}
]
Also, sometimes the content is read but then it seems like it disappears. Vid: https://imgur.com/a/qZjwSBi
If you're using the fs module set:
app.allowRendererProcessReuse = false;
https://github.com/electron/electron/issues/22119
Related
I created an electron app that has user configuration/settings (changing of theme, tracking and persisting window's current size and position). I saved the json files that contained all of these in user AppData (app.getPath('userData')) working well on development but after being packaged with electron-builder, the app no longer communicates the AppData. Tried to change the theme but the new colors could not be written in AppData, size and position not changing. Frustrated 😒😭...
Please what next should I do if I must use the AppData
const { app, BrowserWindow, ipcMain } = require("electron");
let isDev = require('electron-is-dev');
const path = require('path');
const fs = require('fs');
const { setColors } = require('./ipc');
const userDir = app.getPath('userData');
let isConfigDir = fs.existsSync(path.join(userDir, 'config'));
let isSizeDir = fs.existsSync(path.join(userDir, 'config/size.json'))
let isPosDir = fs.existsSync(path.join(userDir, 'config/pos.json'));
let isColorDir = fs.existsSync(path.join(userDir, 'config/colors.json'));
//create config dir if not already exists
if(!isConfigDir){
fs.mkdirSync(path.join(userDir, '/config'))
}
//check and create config files if not already exist
let pos;
if(!isPosDir){
let pos_ = {"x":636,"y":0};
fs.writeFileSync(path.join(userDir, 'config/pos.json'), JSON.stringify(pos_));
pos = pos_
}else{
pos = JSON.parse(fs.readFileSync(path.join(userDir, 'config/pos.json'), "utf8"))
}
let size
if(!isSizeDir){
let size_ = {"width":701,"height":768}
fs.writeFileSync(path.join(userDir, 'config/size.json'), JSON.stringify(size_));
size = size_;
}else{
size = JSON.parse(fs.readFileSync(path.join(userDir, 'config/size.json'), "utf8"))
}
ipcMain.handle("getColors", (event, args)=>{
let colors;
if(!isColorDir){
let colors_ = {"bg":"gold","text":"#000"}
fs.writeFileSync(path.join(userDir, 'config/colors.json'), JSON.stringify(colors_));
colors = colors_
return colors;
}else{
colors = JSON.parse(fs.readFileSync(path.join(userDir, 'config/colors.json'), "utf8"));
return colors;
}
})
let win;
function createWin(){
win = new BrowserWindow({
width:size.width,
height: size.height,
x: pos.x,
y: pos.y,
title: 'BMS',
webPreferences:{
preload: path.join(__dirname, "preload.js")
}
});
isDev ? win.loadURL('http://localhost:3000') : win.loadFile('views/build/index.html')
win.on("closed", ()=>win = null);
// set window size in file size.json when the system resized
win.on('resized', ()=>{
let size = {
width: win.getBounds().width,
height: win.getBounds().height,
}
fs.writeFileSync(path.join(userDir, 'config/size.json'), JSON.stringify(size))
})
// set window position in file size.json when the window moves
win.on('move', ()=>{
let pos = {
x: win.getBounds().x,
y: win.getBounds().y
}
fs.writeFileSync(path.join(userDir, 'config/pos.json'), JSON.stringify(pos))
})
}
app.on("ready", ()=>{
//create window
createWin();
setColors(userDir)
})
app.on("window-all-closed", ()=>{
if(process.platform !== 'darwin'){
app.quit()
}
})
app.on('active', ()=>{
if(win===null){
createWin()
}
})
Looking at your code, I suspect that your /config folder does not exists when running your application. So, all your variables isSizeDir, isPosDir, etc. are false and stays false even after creating the folder on line 17.
I would move the /config folder creation after line 11.
You should take a look at the electron-json-storage package. The package will handle where to put the persisent data depending on the user OS system, and the JSON parsing. You just need to call the package with whatever JS Object you want to store.
I am using this package in my differents electron projects, and it really makes it easy. I recommand using it instead of struggling with the 'fs' Node API.
Got it working now.
So basically.
app.getPath('userData') gives you path up into electron app dir so I have been writing into my electron app directory.
But using app.getPath('appData') get path unto electron app directory level and does not include it. hence won't be packages by electron-builder and it always be in the pc app dir.
const appDir = app.getPath('appData');
instead of
const appDir = app.getPath('userData');
I got this from Jay. chatted with him earlier. His youtube channel - ithinktechnologies
Thanks #Guillaume and #Dony for your time. Means a lot to me
I'm using electron 2.0.7 and I want to prevent multiple instances of the app by using app.makeSingleInstance.
It works but when i'm trying to run another instance of the app I get this error: "A Javascript error is occurred in the main process" as a pop up.
This is the code in main.ts:
function checkSingleInstance() {
// to make singleton instance
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
win.focus();
}
}
});
if (isSecondInstance) {
app.quit();
return;
}
}
checkSingleInstance();
This is the error:
Try replacing app.quit() with app.exit().
app.exit() does not emit events before quitting as opposed to app.quit() which does the proper cleanup.
It's hard to say exactly where the error is coming from and why, but this issue is documented here.
After completing the source code you posted, I can run it using Electron 2.0.7 just fine.
The error you're seeing probably stems from some other part of your code. Judging by the error message, check if you import a module by the name screen somewhere.
Here's your source code, completed to a MCVE:
const {app, BrowserWindow} = require('electron')
let win = null
console.log(`Node ${process.versions.node}, Chrome ${process.versions.chrome}, Electron ${process.versions.electron}`)
function checkSingleInstance() {
// to make singleton instance
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
win.focus();
}
}
});
if (isSecondInstance) {
console.log("Exiting because another instance is running")
app.quit();
return;
}
}
checkSingleInstance();
app.on('ready', () => {
win = new BrowserWindow({width: 200, height: 200});
win.on('closed', () => win = null);
});
Im having issues in bypassing proxy. I’m new to both JavaScript and Electron.
I'm either facing this issue (pic below) or a blank white screen will be loaded.
My pacfile.pac :
function findProxyForURL(url, host) {
// If the hostname matches, send direct.
if (dnsDomainIs(host, “http://000.00.0.0:0000”) ||
shExpMatch(host, “(*.domain.com)”))
return “DIRECT”;
}
my index.js:
const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')
var fs = require('fs');
var pac = require('pac-resolver');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
let ProxyFunc
let pacVariable
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
//proxy: "http://000.00.0.0:0000"
// /* src="https://domain.name.com/#/random/login" */
/*mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000"}, function () {
mainWindow.loadURL('https://domain.name.com/#/random/login');
});*/
app.commandLine.appendSwitch('proxy-bypass-list', '*.google.com;*domain.name.com;');
pacVariable = new pacfile();
pacVariable.findProxyForURL("https://domain.name.com/#/random/login", "*domain.name.com");
//ProxyFunc = new FindProxyForURL("https://domain.name.com/#/random/login","domain.name.com");
mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000",proxyBypassRules:"domain.name.com"}, function () {
mainWindow.loadURL(path.join('file://', __dirname, 'index.html'));
});
/*mainWindow.webContents.session.setProxy({proxyRules:"http://000.00.0.0:0000"}, function () {
mainWindow.loadURL('https://domain.name.com/#/random/login');
});*/
mainWindow.openDevTools({ mode: 'bottom' });
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// 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.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X 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()
}
})
app.on('activate', function () {
// On OS X 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 (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Replace pacVariable = new pacfile() with
var FindProxyForURL = pac(fs.readFileSync('pacfile.pac'));
FindProxyForURL('https://domain.name.com/#/random/login').then((res) => {
console.log(res);
});
refer the docs for API
FindProxyForURL is a promise, so you'll have to write the code that requires the response inside the .then.
noobie here..
In the course of making an electron app,
I have used electron-process package to make electron run in the background(even when all windows are closed). I noticed that, when the electron app was relaunched, the background process of the previous launch of the app remained, and this made background processes pile up with increase in the number of relaunches of the app (If the app was launched 10 times, I would have 10 background processes of the app).. Then I decided to terminate the background process of the previous launch, when I made the next launch.. I have made an attempt to do the same, which is shown below. I noticed that, the app was launched and almost instantly, the app was terminated(which I figured it out to be, the process termination part of the code being exectued later than the part which created the window and started the app. Hence terminating the current process only, instead of the process of the previous launch of the app).. Please help me in making this code run sequentially.. All comments, suggestions, explainations, advices are warmly welcomed. Thanks in advance..
'use strict';
const electron = require('electron');
const main = require('electron-process').main;
const app = electron.app; // Module to control application life.
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
const exec = require('child_process').exec;
const exec1 = require('child_process').exec;
const fs = require('fs');
let mainWindow = null;
let mainWindow1 = null;
app.on('ready', function() {
var pidrun;
var killtask;
var read;
if(process.platform == 'win32'){
exec('tasklist /v /fo csv | findstr /i "electron.exe"', (error, stdout, stderr) => {
if (error){
console.error(`exec error: ${error}`);
return;
}
pidrun = stdout[16]+stdout[17]+stdout[18]+stdout[19];
killtask = "Taskkill /FI \"PID eq "+pidrun+"\" /F";
exec(killtask, (error, stdout, stderr) => {
if (error) {
console.error('exec error: ${error}');
return;
}
if(stdout == "INFO: No tasks running with the specified criteria."){
return;
}
});
});
}
const backgroundhtml = 'file://' + __dirname + '/background.html';
const backgroundProcessHandler = main.createBackgroundProcess(backgroundhtml);
mainWindow = new BrowserWindow({width: 1280, height: 600});
backgroundProcessHandler.addWindow(mainWindow);
mainWindow.loadURL('file://'+__dirname+'/foreground2.html');
mainWindow.loadURL('file://' + __dirname + '/foreground.html');
mainWindow.webContents.openDevTools()
});
Sounds to me like you want only a single instance of your app running at any time, Electron provides app.makeSingleInstance specifically for that purpose.
for some reason my code is compiling with no error, however, my message "Helloworld" is not displaying properly in the console. however my test message is being displayed when i press the bound key combinations. below is my set of code, index.js and main.js
This is written for node/electron.
My main.js file:
//main.js
//requirements
const electron = require('electron');
const app = require('electron').app;
const BrowserWindow = require('electron').BrowserWindow;
const remote = require('electron').remote;
const ipc = require('electron').ipcMain;
const globalShortcut = require('electron').globalShortcut;
var mainWindow = null;
//create app, instantiate window
app.on('ready', function() {
mainWindow = new BrowserWindow({
frame: false,
height: 700,
resizable: false,
width: 368
});
mainWindow.loadURL(`file://${__dirname}/app/index.html`);
//this is the icp bind
globalShortcut.register('ctrl+shift+1', function(){
console.log("test")
mainWindow.webContents.send("testBindicp" ,"HelloWorld");
});
//this is the remote bind
globalShortcut.register('ctrl+shift+2', function(){
console.log("test")
mainWindow.webContents.send("testBindicp" ,"HelloWorld");
});
});
//close the app
ipc.on('close-main-window', function () {
app.quit();
});
below is my entire index.js:
//index.js
const globalShortcut = require('electron').globalShortcut;
const remote = require('electron').remote;
const ipc = require('electron').ipcRenderer;
//testing remote render from remote bind
remote.require('./main.js');
remote.on('testBindRemote', function(event){
console.log(event + " - test - from remote index.js");
});
//testing icpRenderer from icp bind
ipc.on('testBindicp', function (event) {
console.log(event + " - test - from icpRenderer on index.js")
});
//close the app
var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function() {
ipc.send('close-main-window');
});
the problem i'm having is when i press the keyboard binds, only the console logs from the main.js file are being send to the console. the close command is still working within the rendered window, but any other commands in the index.js window are not being bound to the proper main.js elements.
if i am doing something wrong, please let me know the proper way to implement these methodologies, as the remote and icp structures seem to be confusing to me.
Thank you.
You need to have another argument passed into the listening process ipc.on of index.js file, make it something like this:
ipc.on(<channel name>, function (event, arg) {
console.log(arg + " - test - from icpRenderer on index.js");
});
for more info, visit webContents API docs