White Screen issue with Electron/Node app - node.js

I've recently created an app with a Java backend and a React frontend, and I was using Electron and some Node features to bundle the app and create a desktop app for it. Basically, I decided to use Java for some Java-specific libraries I needed. The app will run on port 8080, with the React/JS stuff being served from the static folder, and the Electron wrapper will use some Node libraries to start up the Java process and then after a few seconds connect to localhost:8080.
This works like a charm about half the time, and the other half instead shows me a white screen with no errors! I have debugged this countless times and the only way to fix it is to force reload the Chromium page which sometimes works, and other times doesn't. Obviously this isn't acceptable for users to do with my app. The problem is, I have run out of ideas as to what could be causing this issue.
Here is my main.js for the electron app
const {app, BrowserWindow} = require('electron')
function createWindow () {
try {
var jarPath = './app.jar';
var kill = require('tree-kill');
var child = require('child_process').spawn('java', ['-jar', jarPath, '']);
let win = new BrowserWindow({width: 1000, height: 730});
setTimeout(function() {
win.loadURL('http://localhost:8080/index.html');
}, 2000);
console.log("PID: " + child.pid);
win.on('closed', function () {
kill(child.pid);
mainWindow = null
}
)
} catch(e) {
console.log(e);
}
}
app.on('ready', createWindow)

Related

Electron Global Shortcut alternative without reserving the key

How can i capture keypress in Electron without globalShortcut and without losing the key functionality. For example i want to capture Tab press but without losing it's ability to indent for example in visual studio code.
I use Electron 13.0 because if i use higher some required modules don't work.
I tried iohook but throws iohook.node module not found. I think it doens't have support for Electron 13 yet.
Anyone ideea how can i do accomplish this? Thank you !
Electron can be a bit of a headache when it comes to communicate between the window and the main process, and for good reason: Security.
However, this problem has two solutions:
Not recommended: plain ipcRenderer required with { nodeIntegration: true } and window.electron in index.html, that can cause a lot of trouble, don't do that, you give access to the user to all nodejs functions, like fs, child_process, ...
Recomended: preload. Preload makes the bridge between the process and the window allowing you to pick what you want to share with the window, in this case, ipcRenderer without the whole electron access.
Read more about Electron secuity here
First, create a preload.js to pass scope isolated ipcRenderer.send function to the window
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
const exposedAPI = {
sendMessage: (message) => {
ipcRenderer.send('my-event', string);
}
};
contextBridge.exposeInMainWorld("electron", exposedAPI);
More about contextBridge here
In the main electron script
// main.js
const { ipcRenderer } = require('electron');
...
const window = new BrowserWindow({
...
preload: 'my/preload/path/preload.js', // Here preload is loaded when the window is created
})
...
ipcRenderer.on('my-event', (string) => {
// do struff with string
});
Great full example here
Finally, the window where you want to capture the event from without changing the behaviour
// index.html or your-script.js
document.addEventListener('keydown', (evt) => { // keyup, keydown or keypress
window.electron.exposedAPI.sendMessage('key was pressed');
});

Electron application exits immediately after starting

I had an Electron 5 environment running on Windows 7.
Electron 5 was installed in the local node_modules.
I was able to develop and run the application from VSCode.
I ran 'ncu' and opted to upgrade to Electron 7.
This upgraded the electron (from 5.0.0 to 7.1.3) and socket.io modules (from 2.1.0 to 2.3.0).
Now, when running 'electron .', my application starts but exits before the window is rendered.
This is because I set the BrowserWindow to show:false and expose it on the ready-to-show event, which is never sent.
I tried downgrading to Electron 5.0.12 but even after removing node_modules the behavior persisted.
I had a global instance of Electron 7 (7.0.0) but the application was using version 5 per a document.write(process.versions.electron) statement in my HTML.
Regardless, I upgraded the global Electron version to 7.1.3 to match.
I then copied the electron-quick-start file, modified with numerous debug statements. The file is:
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')
// 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
function createWindow () {
// Create the browser window.
console.log('READY:create...');
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
console.log('load index...');
mainWindow.loadFile('index-none.html')
// Open the DevTools.
console.log('load devtools...');
mainWindow.webContents.openDevTools()
console.log('set handler...');
// Emitted when the window is closed.
mainWindow.on('closed', function () {
console.log('MAINWIN:Closed...');
// 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
})
console.log('created');
}
// 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 () {
console.log('onWall-close...');
// 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()
})
app.on('activate', function () {
console.log('onActivate...');
// 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 (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.
console.log('setting timer');
setTimeout(()=>{
console.log('timing out...');
}, 15000)
console.log('setting timer2');
setTimeout(()=>{
console.log('timing2 out...');
}, 200)
console.log('setting timer5');
setTimeout(()=>{
console.log('timing5 out...');
}, 500)
console.log('setting timer7');
setTimeout(()=>{
console.log('timing7 out...');
}, 700)
setTimeout(()=>{
console.log('timing10 out...');
}, 1000)
setTimeout(()=>{
console.log('timing12 out...');
}, 1200)
setTimeout(()=>{
console.log('timing15 out...');
}, 1500)
setTimeout(()=>{
console.log('timing17 out...');
}, 1700)
The HTML file is:
<html>
<head><title>title</title></head>
<body>
Hello world.
</body>
</html>
When I run either "electron eqs.js" or ".\node_modules.bin\electron.cmd eqs.js" I get this output:
setting timer
setting timer2
setting timer5
setting timer7
READY:create...
load index...
load devtools...
set handler...
created
timing2 out...
timing5 out...
timing7 out...
timing10 out...
timing12 out...
timing15 out...
(Sometimes timing15 doesn't get printed).
In this case I see the BrowserWindow get created, but it remains blank. The developer tools do not open. Commenting out the line opening the developer tools changes nothing.
This behavior happens with both:
C:\electron\electron-quick-start>.\node_modules.bin\electron.cmd -v
v5.0.12
C:\electron\electron-quick-start>electron -v
v7.1.3
I don't understand why the process is exiting while timers are active, so something somewhere must be calling app.exit() or similar. Besides "What is going on?", my obvious question is: how do I fix this?
I don't have an actual answer to this problem, but it was fixed when I upgraded to Electron 7.1.9 as I commented in the bug report https://github.com/electron/electron/issues/21544#issuecomment-577242668
During the upgrade some process took an unusually long time to complete but when it did my application ran successfully. I was also able to downgrade to a previous version of Electron and my application still ran, even when using a version that was failing earlier.
EDIT:
This problem resurfaced when an attempt to upgrade to Electron 8.2.2 failed because the application was running in a debugger. Unfortunately, no amount of version up/down-grades (up to 8.2.3) was able to recover the environment. Ultimately, disabling the sandboxing from my package.json launch script allowed me to proceed. This is the new command structure:
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Electron: Main",
"protocol": "inspector",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"runtimeArgs": [
"--remote-debugging-port=9223",
"--no-sandbox", <<<<<-- PASS THIS COMMAND OPTION
"."
],
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
}
},
[...]
]
For anyone stumbling across this: I had a similar problem.
Deleting the local files on Windows under %appdata%/YOUR_APP_NAME fixed Electron failing to launch properly for me.

Nodejs/electron Override chromium browser timeout.

Been looking all over for this information, closest i can see is the
command line switches. i have a web page that i ported to electron and im trying to override the Browser inactivity timeout. for example running a report over 2 minutes the browser times out the connection. My thinking for a quick fix was to wrap in electron and extend the browser timeout.
https://peter.sh/experiments/chromium-command-line-switches/#timeout
https://github.com/electron/electron/blob/master/docs/api/chrome-command-line-switches.md
In Electron the main.js looks to have wrapped the modules in webpack
in all of the examples ive found
it looks like this
You can use app.commandLine.appendSwitch to append them in your app's main script before the ready event of the app module is emitted:
const { app } = require('electron')
app.commandLine.appendSwitch('remote-debugging-port', '8315')
app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1')
app.on('ready', () => {
// Your code here
})
however in my electron file my const app is actually
const path = __webpack_require__(14);
const electron = __webpack_require__(18);
const unusedFilename = __webpack_require__(20);
const pupa = __webpack_require__(23);
const extName = __webpack_require__(24);
const {app, shell} = electron;
i have tried
app.commandLine.appendSwitch("--disable-renderer-backgrounding");
app.commandLine.appendArgument('--disable-timeouts-for-profiling');
Doesnt seem to work, Any hints would be appreciated.

Electron - "Cannot read property 'on' of undefined"; tried reinstalling,

I'm testing an electron app but I'm getting this pernicious error:
"TypeError: Cannot read property 'on' of undefined"
The research I've done pointed to either a botched module install, a syntax issue, or passing in an undefined variable to the app.on, and I suspect the issue may be Electron being pointed to incorrectly (now it's being pointed to the folder ending in electron\dist\electron.exe, which I've heard might not the right location), but I'm unsure.
Despite checking the require command, syntax, rechecking, uninstalling, and reinstalling node, I can't seem to make this darn error go away. What could be causing this?
const electron = require('electron');
const os = require('os');
const fs = require('fs');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
var Mousetrap = require('mousetrap');
const path = require('path');
const url = require('url');
const ipc = electron.ipcMain;
let mainWindow;
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
/* More code in this and sub functions*/
}))
}
})
const preferencesManager = require('./preferencesManager');
/******
Send data to database given constructor created in preferencesManager
******/
// First instantiate the class because we want to turn the class into an object to be able to use.
const store = new Store({ //create a new getting and setting logic
//We'll call our data file 'user-preferences'
configName: 'user-preferences',
defaults: {
//800 x 600 is the default size of our window
windowBounds: { width: 800, height: 600}
}
});
// When our app is ready, we'll create our BrowserWindow
app.on('ready',function(){
//Set up a listener for what I've done in keycapture (in the renderer process)
//???
ipc.on('invokeAction', function(event, args){
/* Do some action */
});
});
You are probably trying to run your application like a node app with:
$ node index.js
The electron file is a binary file, not a JavaScript file, when you require it an run with node there will be no object to call electron.app, so it parses for null and cannot have an property. As in the getting started documento of Electron.JS you must run the application like this:
Change your package.json script session adding start:
{
"scripts": {
"start": "electron ."
}
}
Now run:
$ npm start
The code you posted has an error, witch may be an edition error while coping and pasting but it should loose some parenthesis and curly brackets:
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
/* More code in this and sub functions*/
}))
}
The application should now run correctly. I tested you exact code, removing the libs I did not have and it loaded with no errors.

How to integrate and run existing ReactJS Application in Electron

For instance, I have an ReactJS application: https://iaya-664f3.firebaseapp.com/
You can see in the HTML source the bundle.js file.
I have tried to run this application as desktop application using Electron, which should launch this web application in chromium window but it is not working.
Following is my main React application file app.js sitting in root directory. However compiled files bundle.js and index.html are in ./public/directory.
./app.js
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
import routes from './routes';
import {Provider} from "react-redux";
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import rootReducer from './reducers/index';
const store = applyMiddleware(ReduxPromise)(createStore)(rootReducer);
ReactDOM.render( <Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider> ,
document.getElementById('react-app'));
./index.js
In this file I'm embedding my application to Electron to run in chromium.
var app = require("./app");
var BrowserWindow = require("browser-window");
// on electron has started up , booted up, everything loaded (Chromium ,goen, live)
app.on("ready", function(){
var mainWindow = new BrowserWindow({
width:800,
height:600
});
mainWindow.loadUrl("file://" + __dirname+ "/public/index.html");
});
But this give some error on import React from 'React' line in my ./app.js.
So I assume that, I should only give ./public/index.html file to load which includes the compiled bundle.js. But I wonder, how that will work as the line app.on("ready", function(){ expect an app.
Moreover I have also tried following way in ./index.js but it gives some other error.
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');
// 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;
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600});
// and load the index.html of the app.
/*mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'public/index.html'),
protocol: 'file:',
slashes: true
}));*/
mainWindow.loadURL("file://" + __dirname+ "/public/index.html");
// 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;
});
}
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();
}
});
Basically the thing is very simple. Electron acts just like a desktop chromium wrapper, that displays your any (any) type of web page inside desktop chromium.
So for example, we want to display http://www.google.com then you simply pass this URL to your loadURL() function.
Here is the working copy of code (asked in the question):
const electron = require('electron');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
app.on('ready', function(){
var mainWindow = new BrowserWindow({width: 800, height: 600});
mainWindow.loadURL("http://localhost:8080/"); // option1: (loading a local app running on a local server)
//mainWindow.loadURL("https://iaya-664f3.firebaseapp.com"); // option2: (loading external hosted app)
// loading developer tool for debugging
mainWindow.webContents.openDevTools();
});
I hope this will clarify the confusion for many people, who are new to Electron. So the final words are that Electron only loads existing and running web application. It does not compile, does not act as a server. It is just a box in which you can put anything and give it a desktop sort of look e.g. menues, desktop notification, local file system access, etc.

Resources