I've created multiple windows from Electron's main process and need to pass messages between them. The only way I know to send messages from rendererA to rendererB is by bouncing it to the main process. Is there any way to directly send a message from rendererA to renderB?
-
As I know not with the IPC capabilities of Electron. – Alexander Leithner Nov 21 '17 at 16:41
3 Answers
In a way or another, the main process has to get involved, but communicating between the renderer processes of two windows can be achieved in some kind of straightforward way:
In the main process, define the window references as properties of the global object;
In each renderer process, access the reference of the window you want to send a message to by using remote.getGlobal (), then use the send () method;
Use ipcRenderer.on () the usual way to receive the message in each renderer process.
Here is a quick example of an Electron app which does just that:
main.js:
const { app, BrowserWindow } = require ('electron');
global.window1 = null;
global.window2 = null;
function onAppReady ()
{
window1 = new BrowserWindow ({ width: 600, height: 500 });
window1.loadURL (`file://${__dirname}/index1.html`);
window1.webContents.openDevTools ();
window1.on ('closed', () => { window1 = null; });
//
window2 = new BrowserWindow ({ width: 500, height: 600 });
window2.loadURL (`file://${__dirname}/index2.html`);
window2.webContents.openDevTools ();
window2.on ('closed', () => { window2 = null; });
}
app.on ('ready', onAppReady);
app.on ('window-all-closed', () => { app.quit (); });
index1.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Window 1</title>
</head>
<body>
<h1>Window 1</h1>
<button type="button" class="send-message">Send Message to Window 2</button>
<script>
const { remote, ipcRenderer } = require ('electron');
//
let button = document.querySelector ('.send-message');
button.addEventListener ('click', () =>
{
let window2 = remote.getGlobal ('window2');
if (window2) window2.webContents.send ('message', "Message from Window 1");
});
//
ipcRenderer.on ('message', (event, message) => { console.log (message); });
</script>
</body>
</html>
index2.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Window 2</title>
</head>
<body>
<h1>Window 2</h1>
<button type="button" class="send-message">Send Message to Window 1</button>
<script>
const { remote, ipcRenderer } = require ('electron');
//
let button = document.querySelector ('.send-message');
button.addEventListener ('click', () =>
{
let window1 = remote.getGlobal ('window1');
if (window1) window1.webContents.send ('message', "Message from Window 2");
});
//
ipcRenderer.on ('message', (event, message) => { console.log (message); });
</script>
</body>
</html>

- 5,285
- 1
- 22
- 36
-
1I just tested your "direct message flow" code in my electron app with a mainWindow and a hidden worker renderer process window, where the worker sends a lot of messages to the main window. It work's great but it's way slower than before (~5 times). Before I did send all messages to the main process, which dispatched them to the main renderer window. I guess it has to do with `let window2 = remote.getGlobal ('window2');` – Mike Lieser Dec 04 '18 at 09:02
Basicly, in electron, communications between process has three forms:
- main -> renderer:
webContents.fromId(id).send()
on sender side,ipcRenderer.on
on receiver side - renderer -> main:
ipcRenderer.send()
on sender side,ipcMain.on
on receiver side - renderer -> renderer:
ipcRenderer.sendTo()
on sender side,ipcRenderer.on
on receiver side
So in renderer to renderer scenario, sender must know the destination's webContents.id, then call it to/from via ipcRenderer.sendTo()
I've made a electron ipc framework, electron-ipcfy, which unifies ipc calls in all three scenario above.
import { ipcfy } from "electron-ipcfy";
interface TestService {
greet(name: string);
}
const testService = ipcfy<TestService>('test');
if (process.type == 'browser') {
// Attach implementation
testService.__attachImpl(
new class implements TestService {
greet(name: string) {
console.log(`Hello, ${name}!`);
}
});
}
// Then you can call it in any process
testService.greet('world');

- 496
- 8
- 9
It depends from the logic of your communication system.
For example, if you always have to send data from BrowserWindow2 to BrowserWindow4, you can declare the ipcMain in the BrowserWindow4 and ipcRenderer in the BrowserWindow2.
In case you have to send from all the BrowserWindows to all the others, I advice you to use Main process and dispatch message to the BrowserWindows (using the relatives ID)
In your message receiver:
ipcMain.on('asynchronous-message', (event, arg) => {
//manage data
}
And in your message sender:
ipcRenderer.send('asynchronous-message', message)

- 696
- 8
- 25
-
What do you mean by declare ipcMain in BrowserWindow4? Can you be more specific please? – hawk Nov 22 '17 at 14:36
-