I am a beginner of Electron and I wanna build an Electron App which can get the basic hardware information of a windows PC.
The text below lists what I want to get:
BIOS(Manufacturer , BIOS Version, release Date)
CPU
Memory,
Storage
Network
etc..
I have tried to use the 3rd party module(systeminformation), but the results are not what I wanted.
Does anybody know how to achieve this?
Below is some starter code to gather the desired information, primarily your BIOS information (as indicated in your comment).
This will only work on a Microsoft Windows machine (tested on Windows 10).
// Import the necessary Electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
// Import the necessary Node modules
let nodeExec = require('child_process').exec;
const nodePath = require('path');
// Prevent garbage collection
let window;
function createWindow() {
window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
function getBios() {
return new Promise((resolve, reject) => {
nodeExec("reg query HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\BIOS", (error, stdout, stderr) => {
if (error) { reject(error.message); }
if (stderr) { reject(stderr); }
resolve(stdout);
})
})
}
getBios()
.then((bios) => {
bios = JSON.stringify(bios);
bios = bios.replace(`HKEY_LOCAL_MACHINE\\\\HARDWARE\\\\DESCRIPTION\\\\System\\\\BIOS`, '');
bios = bios.replace(/^"\\r\\n\\r\\n|\\r\\n\\r\\n"/g, "");
let array = bios.split('\\r\\n');
let temp = [];
for (let line of array) {
temp.push(line.trim());
}
bios = [];
for (let line of temp) {
let item = line.split(' ');
let data = (item[2].startsWith('0x')) ? parseInt(item[2], 16) : item[2];
bios.push({
'name': item[0],
'type': item[1],
'data': data
})
}
console.log(bios);
})
Even though the above code could be tidied up and optimised, it will give you the general idea about how it is acquired and manipulated.
If the registry key (HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS) changes between Window versions (EG: XP, 7, 8, 10, 11, etc) then you will need to detect which version of Windows the user is using first, then use the correct registry key corresponding to that version of Windows.
Related
My electron main.js code:
function createWindow () {
const { screen } = require('electron');
const primaryDisplay = screen.getPrimaryDisplay();
const { width, height } = primaryDisplay.workAreaSize;
let mainWindow = new BrowserWindow({
width,
height,
webPreferences: {
webSecurity: false,
},
});
return mainWindow;
}
function createServerProcess () {
serverProcess = fork(__dirname + '/index.js');
}
app.whenReady().then(async () => {
createServerProcess();
let mainWindow = createWindow();
let workspaceId = await localStorage.getItem('workspaceId');
if (!workspaceId) {
workspaceId = 'abc';
}
let url = `http://${workspaceId}.localhost:8443`;
setTimeout(() => {
mainWindow.loadURL(url);
}, 3000);
});
I am using electron-forge to build my electron app, using electron-forge start to start local app works fine. But when starting the app generated by electron-forge make command, it shows below error:
I have already set webSecurity to false, for turning off cross origin restriction. Just don't know why while local app works fine but generated app is not working. I am confused. I am new to Electron.
I have an electron application that loads a web page on the internet.
one of the sites main features is the ability to capture screen, it uses the
navigator.mediaDevices.getDisplayMedia({video: true});
but obviously, the electron will through the Permission denied because there will be no 'selecting window to capture' popped up to grant any permission to it.
I already check out some articles and saw desktopCapture
the problem is, this is happening and running through the web page javascript not my application's code so I don't know how to affect it.
so what should I do to make capturing the screen works in this situation?
You can override navigator.mediaDevices.getDisplayMedia to call Electron's desktopCapturer API like shown below. This implementation assumes you have contextIsolation enabled which is the default behaviour in Electron >= 12
// preload.js
const { desktopCapturer, contextBridge } = require("electron");
const { readFileSync } = require("fs");
const { join } = require("path");
// inject renderer.js into the web page
window.addEventListener("DOMContentLoaded", () => {
const rendererScript = document.createElement("script");
rendererScript.text = readFileSync(join(__dirname, "renderer.js"), "utf8");
document.body.appendChild(rendererScript);
});
contextBridge.exposeInMainWorld("myCustomGetDisplayMedia", async () => {
const sources = await desktopCapturer.getSources({
types: ["window", "screen"],
});
// you should create some kind of UI to prompt the user
// to select the correct source like Google Chrome does
const selectedSource = sources[0]; // this is just for testing purposes
return selectedSource;
});
// renderer.js
navigator.mediaDevices.getDisplayMedia = async () => {
const selectedSource = await globalThis.myCustomGetDisplayMedia();
// create MediaStream
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: selectedSource.id,
minWidth: 1280,
maxWidth: 1280,
minHeight: 720,
maxHeight: 720,
},
},
});
return stream;
};
Now when this API is called, a stream will be returned to the caller as expected
navigator.mediaDevices.getDisplayMedia({video: true});
I have created a GitHub repo that has a working implementation of this solution
I have built an Electron app and I'm trying to build it out. It works perfectly when I run it in the dev environment with "npm start", but once I build it out, it fails. I've tried this with electron forge and electron packager. Essentially, my app lives in the task tray and scans a folder every second. If the folder is not empty, it shows the window. While the window is shown, the loop stops. Once the main.js file receives a command that the user actions were completed, it hides the window and goes back to the tray to resume scanning. Here is my main.js file:
const { app, BrowserWindow, Tray, Menu } = require('electron')
var ipcMain = require('electron').ipcMain;
const Store = require('electron-store');
const store = new Store();
const fs = require('fs');
shouldScan = true;
// global window declaration function
var win;
async function createWindow () {
win = new BrowserWindow({
width: 500,
height: 250,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
}
})
win.setMenuBarVisibility(false)
win.loadFile('index.html')
tray = new Tray('spongebob.ico')
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show App', click: function () {
win.show()
}
},
{
label: 'Quit', click: function () {
app.isQuiting = true
app.quit()
}
}
])
tray.setToolTip('This is my application.')
tray.setContextMenu(contextMenu)
win.on('minimize', function (event) {
event.preventDefault()
shouldScan = true
scanning()
win.hide()
})
win.on('show', function (event) {
event.preventDefault()
shouldScan = false
})
await sleep(1000)
win.minimize()
}
// start the application
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
//allow delays
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
//check the designated folder and stop the loop while showing the app window from the tray
async function scanning(){
while(shouldScan){
console.log('scanning')
if(store.get('default_path') != null){
files = fs.readdirSync(store.get('default_path'));
if(files.length > 0){
fs.rename(store.get('default_path') + "/" + files[0], store.get('default_path') + "/encounter", err => {
if (err) {
console.error(err)
return
}
})
console.log('should have shown')
win.show()
shouldScan = false
}
}
await sleep(1000)
}
}
//start the scanning funciton again when the signal is received
ipcMain.on('processInput', function(event, status) {
win.hide()
shouldScan = true
scanning()
});
The error I'm experiencing is that the window never goes to tray. Its even supposed to minimize 1 second after launching but it doesn't do that either. Actually, none of the scripts in the main file run other than creating the window. If the window is minimized and its target folder is not empty, it does not re-show the window. The dev tools within the window don't show any errors. I don't know how to have a command prompt running while the packaged .exe is running either, though. Does anyone have some advice?
for anyone in the future, it seems that electron does not like just local file paths. When I was creating the new Tray('spongebob.ico') that wasn't good. doing this seemed to fix the error:
new Tray(path.join(__dirname, 'asset','img', 'spongebob.png'));
obviously I had to create the correct path and file type.
I'm working on a JHipster application that I'm trying to get functioning in Electron. I have Golden Layout for window/pane management and cross-pane communication. I am having several problems with the combination of technologies, including:
I can't pop out more than one pane at the same time into their own Electron windows. I instead get an Uncaught Error: Can't create config, layout not yet initialised error in the console.
Two thirds of the panes don't display anything when popped out into Electron windows, and I'm not sure what the reason is. Any ideas or suggestions for this? One example of content is a leaflet map, another is a "PowerPoint preview" that is really just divs that mock the appearance of slides.
I haven't made it this far yet, but I assume that I will have trouble communicating between popped-out Electron windows when I get more than one open. Right now, the panes communicate between each other using Golden Layout's glEventHub emissions. I have an avenue to explore when I cross that bridge, namely Electron ipcRenderer.
Some borrowed code is here (most of it I can't share because it's company confidential):
electron.js:
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({width: 900, height: 680});
mainWindow.loadURL(isDev ? 'http://localhost:9000' : `file://${path.join(__dirname, '../build/index.html')}`);
if (isDev) {
// Open the DevTools.
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => mainWindow = null);
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
goldenLayoutComponent.tsx, a patch for Golden Layout:
import React from "react";
import ReactDOM from "react-dom";
// import "./goldenLayout-dependencies";
import GoldenLayout from "golden-layout";
import "golden-layout/src/css/goldenlayout-base.css";
import "golden-layout/src/css/goldenlayout-dark-theme.css";
import $ from "jquery";
interface IGoldenLayoutProps {
htmlAttrs: {},
config: any,
registerComponents: Function
}
interface IGoldenLayoutState {
renderPanels: Set<any>
}
interface IContainerRef {
current: any
}
export class GoldenLayoutComponent extends React.Component <IGoldenLayoutProps, IGoldenLayoutState> {
goldenLayoutInstance = undefined;
state = {
renderPanels: new Set<any>()
};
containerRef: IContainerRef = React.createRef();
render() {
const panels = Array.from(this.state.renderPanels || []);
return (
<div ref={this.containerRef as any} {...this.props.htmlAttrs}>
{panels.map((panel, index) => {
return ReactDOM.createPortal(
panel._getReactComponent(),
panel._container.getElement()[0]
);
})}
</div>
);
}
componentRender(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.add(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDestroy(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.delete(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDidMount() {
this.goldenLayoutInstance = new GoldenLayout(
this.props.config || {},
this.containerRef.current
);
if (this.props.registerComponents instanceof Function)
this.props.registerComponents(this.goldenLayoutInstance);
this.goldenLayoutInstance.reactContainer = this;
this.goldenLayoutInstance.init();
}
}
// Patching internal GoldenLayout.__lm.utils.ReactComponentHandler:
const ReactComponentHandler = GoldenLayout["__lm"].utils.ReactComponentHandler;
class ReactComponentHandlerPatched extends ReactComponentHandler {
_container: any;
_reactClass: any;
_render() {
const reactContainer = this._container.layoutManager.reactContainer; // Instance of GoldenLayoutComponent class
if (reactContainer && reactContainer.componentRender)
reactContainer.componentRender(this);
}
_destroy() {
// ReactDOM.unmountComponentAtNode( this._container.getElement()[ 0 ] );
this._container.off("open", this._render, this);
this._container.off("destroy", this._destroy, this);
}
_getReactComponent() {
// the following method is absolute copy of the original, provided to prevent depenency on window.React
const defaultProps = {
glEventHub: this._container.layoutManager.eventHub,
glContainer: this._container
};
const props = $.extend(defaultProps, this._container._config.props);
return React.createElement(this._reactClass, props);
}
}
GoldenLayout["__lm"].utils.ReactComponentHandler = ReactComponentHandlerPatched;
Any help or insight into these issues would be appreciated. Thanks in advance!
If you are still looking for a solutions, 1 and 2 I have solved, if you want to see my solution you could see in this repository.
But it was basically this:
1: The window that popups has a different path than the main window, so I just had to put a try catch in my requires, and you have to set
nativeWindowOpen = true
when creating the Browser window.
2: Solves it's self after 1 I think
Is it possible to create a tcp client with electron?
Or can we access the chrome socket api with does that?
https://developer.chrome.com/apps/sockets_tcp
You can use the Node net API in Electron to implement a TCP client.
Try this sample code (don't forget to change IP address) with a little socket server as SocketTest java application for example (HERE).
At the connection, you should see a "World!" string on server side. Try to send this message from server:
{
"nom":"Xplorer",
"prenom":"Yann"
}
And you should see Hello Yann! in your electron console.
'use strict';
const electron = require('electron');
const path = require('path');
const url = require('url');
const net = require('net');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
let mainWindow;
var socketClient
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600,backgroundColor:'#FFFFFF', frame:false})
// and load the index.html of the app.
mainWindow.loadURL(url.format({
pathname: path.join(__dirname+'/html/', 'main.html'),
protocol: 'file:',
slashes: true
}))
// 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
})
/* Instance socket on create window */
console.log('Try to connect');
socketClient = net.connect({host:'192.16.122.3', port:9042}, () => {
// 'connect' listener
console.log('connected to server!');
socketClient.write('world!\r\n');
});
socketClient.on('data', (data) => {
console.log(data.toString());
var person = JSON.parse(data);
console.log('Hello '+person.prenom+"!");
});
socketClient.on('end', () => {
console.log('disconnected from server');
});
//mainWindow.openDevTools();
}
app.on('before-quit',function(){
socketClient.end();
})
see you.
You can use the Node net API in Electron to implement a TCP client.
It's easy to just test if the chrome API is present. If not I use the node API. So that I have the same code base for my Chrome App and my Electron App.
The 2 APIs are slightly different so I post here how to do it.
let client = null; // node socket
let socketId; // chrome API socket id
function toBuffer(ab) {
return new Buffer(new Uint8Array(ab));
}
function toArrayBuffer(buf) {
return new Uint8Array(buf).buffer;
}
function initConnToServer (ip, port) {
return new Promise((resolve, reject) => {
if(typeof chrome !== 'undefined') {
chrome.sockets.tcp.create({}, r => {
socketId = r.socketId;
chrome.sockets.tcp.connect(r.socketId, ip, port, code => resolve(code));
});
} else {
client = new net.Socket(); // return a Node socket
client.connect(port, ip);
client.on('connect', () => resolve());
}
});
};
function sendToServer_simple (data) {
return new Promise((resolve, reject) => {
if(typeof chrome !== 'undefined') {
chrome.sockets.tcp.send(socketId, data, r => {});
chrome.sockets.tcp.onReceive.addListener(receiveInfo => resolve(receiveInfo.data));
} else {
client.write(toBuffer(data));
client.on('data', data => resolve(toArrayBuffer(data)));
}
});
};