Electron IPC 在应用程序后台发送消息吗?

huangapple go评论55阅读模式
英文:

Does Electron IPC send messaged while app is in background?

问题

我正在为我们的Angular-WebApp创建Windows的Electron桌面版本。我通过Windows Hello实现了生物识别身份验证,它运行良好。实际上,当通过进程间通信(IPC)触发事件告诉我页面已准备好登录时,我使用nodeRT打开Hello对话框。页面是通过window.loadURL(url)加载的,因此没有本地Web内容。

一段时间后,在应用程序中什么都不做的情况下,用户会被自动注销。之后,“登录准备好”事件应该在登录页面上触发。现在有两种可能性:

  1. 应用程序仍然在前台并具有焦点。然后Windows Hello窗口会像预期的那样打开并具有焦点。事件如预期地触发。
  2. 如果应用程序在后台且没有焦点,Windows Hello窗口将不会打开。事件不会触发。

这是否是预期的行为?是否可能在应用程序在后台时没有IPC?我该如何处理这种情况?也许我可以再次将应用程序设置为焦点,但我认为这可能会让用户感到不适。有什么建议吗?

我写了日志,显示在应用程序在后台时未触发预期的事件。

英文:

I am working on an Electron Desktop version for Windows for our Angular-WebApp.
I implemented biometric authentication via Windows Hello which works nicely. Actually I open the Hello dialog (with nodeRT) when an event gets triggered via Inter-Process Communication (IPC), which tells me, that the page is ready for a login. The page is loaded with window.loadURL(url), so there is no local webcontent.

After a while, without doing anything in the app, the users get logged out automatically. After this, the event "login ready" should get triggered on the login page. Now there are two possibilies:

  1. The app is still in foreground and has focus. Then the Windows Hello window will open like expected and has focus. The event was triggered as expected.
  2. If the app is in background, without focus, the Windows Hello window will not open. The event was not triggered.

Is this the expected behavior? Is it possible that there is no IPC when the app is in background?
How could I handle this? Maybe I could focus the app again. But I guess this could be disturbing.
Any ideas?

I wrote logs which showed that the expected event was not triggered while the app is in background.

答案1

得分: 0

以下是您要翻译的内容:

I have tested what happens when the main window is minimised and an event calls for the creation and display of a second (child) window.

As expected, the second window will be created and shown.

Therefore, Electron IPC messages can be sent and received by both the main process and render process(es) when the window(s) are minimised. IE: When the application is running in the background.

In the below minimum reproducible example, you can set the timeout delay between when the main window is minimised and the second (login.html) window is created and shown.

main.js (main process)

// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;

// Import required Node modules
const nodePath = require('path');

// Prevent garbage collection
let mainWindow;
let loginWindow;

function createMainWindow() {
    const mainWindow = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    mainWindow.loadFile(nodePath.join(__dirname, 'index.html'))
        .then(() => { mainWindow.show(); });

    return mainWindow;
}

function createLoginWindow(parentWindow) {
    const loginWindow = new electronBrowserWindow({
        x: 200,
        y: 150,
        width: 400,
        height: 300,
        parent: parentWindow,
        modal: true,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    loginWindow.loadFile(nodePath.join(__dirname, 'login.html'))
        .then(() => { loginWindow.show(); });

    return loginWindow;
}

electronApp.on('ready', () => {
    mainWindow = createMainWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createMainWindow();
    }
});

// ---

electronIpcMain.on('minimiseMainWindow', () => {
    mainWindow.minimize();
})

electronIpcMain.on('openLoginWindow', (event, delay) => {
    setTimeout(() => {
        loginWindow = createLoginWindow(mainWindow);
    }, delay * 1000)
})

preload.js (main process)

As you can see below, I have requested the main window get minimised first just to make sure IPC continues to work when a second IPC message is sent.

// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods
    'ipcRenderer', {
        // From render to main
        delayOpenLoginWindow: (delay) => {
            ipcRenderer.send('minimiseMainWindow');
            ipcRenderer.send('openLoginWindow', delay);
        }
    }
);

And how it works...

/**
 *
 * Main --> Render
 * -------------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Preload: apiName: (listener) => { ipcRenderer.on('channel', listener); }
 * Render:  window.ipcRenderer.apiName((event, data) => { methodName(data); });
 *
 * Main --> Render (Once)
 * ----------------------
 * Main:    window.webContents.send('channel', data); // Data is optional.
 * Preload: apiName: (listener) => { ipcRenderer.once('channel', listener); }
 * Render:  window.ipcRenderer.apiName((event, data) => { methodName(data); });
 *
 * Render --> Main
 * -------------------
 * Render:  window.ipcRenderer.apiName(data); // Data is optional.
 * Preload: apiName: (data) => { ipcRenderer.send('channel', data) }
 * Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); });
 *
 * Render --> Main (Value) --> Render
 * ----------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) => { methodName(result); });
 * Preload: apiName: (data) => { return ipcRenderer.invoke('channel', data); }
 * Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
 *
 * Render --> Main (Promise) --> Render
 * ------------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) => { methodName(result); });
 * Preload: apiName: (data) => { return ipcRenderer.invoke('channel', data); }
 * Main:    electronIpcMain.handle('channel', async (event, data) => {
 *              return await myPromise(data).then((result) => { return result; })
 *          });
 *
 * Main:    function myPromise(data) { return new Promise((resolve, reject) => { ... }; )};
 *
 */

index.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Main Window</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <h1>Main Window</h1>

        <p>
            <span>Open the login window <input type="number" id="delay" min="5" max="30" step="5" value="5"> seconds after clicking the below Minimise Window button.</span>
        </p>

        <hr>

        <input type="button" id="button" value="Minimise Window">
    </body>

    <script>
        document.getElementById('button').addEventListener('click', () => {
            window.ipcRenderer.delayOpenLoginWindow(document.getElementById('delay').valueAsNumber);
        })
    </script>
</html>

login.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login Window</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <h1>Login window</h1>
    </body>
</html>
英文:

I have tested what happens when the main window is minimised and an event calls for the creation and display of a second (child) window.

As expected, the second window will be created and shown.

Therefore, Electron IPC messages can be sent and received by both the main process and render process(es) when the window(s) are minimised. IE: When the application is running in the background.

In the below minimum reproducible example, you can set the timeout delay between when the main window is minimised and the second (login.html) window is created and shown.


main.js (main process)

// Import required electron modules
const electronApp = require(&#39;electron&#39;).app;
const electronBrowserWindow = require(&#39;electron&#39;).BrowserWindow;
const electronIpcMain = require(&#39;electron&#39;).ipcMain;

// Import required Node modules
const nodePath = require(&#39;path&#39;);

// Prevent garbage collection
let mainWindow;
let loginWindow;

function createMainWindow() {
    const mainWindow = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, &#39;preload.js&#39;)
        }
    });

    mainWindow.loadFile(nodePath.join(__dirname, &#39;index.html&#39;))
        .then(() =&gt; { mainWindow.show(); });

    return mainWindow;
}

function createLoginWindow(parentWindow) {
    const loginWindow = new electronBrowserWindow({
        x: 200,
        y: 150,
        width: 400,
        height: 300,
        parent: parentWindow,
        modal: true,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, &#39;preload.js&#39;)
        }
    });

    loginWindow.loadFile(nodePath.join(__dirname, &#39;login.html&#39;))
        .then(() =&gt; { loginWindow.show(); });

    return loginWindow;
}

electronApp.on(&#39;ready&#39;, () =&gt; {
    mainWindow = createMainWindow();
});

electronApp.on(&#39;window-all-closed&#39;, () =&gt; {
    if (process.platform !== &#39;darwin&#39;) {
        electronApp.quit();
    }
});

electronApp.on(&#39;activate&#39;, () =&gt; {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createMainWindow();
    }
});

// ---

electronIpcMain.on(&#39;minimiseMainWindow&#39;, () =&gt; {
    mainWindow.minimize();
})

electronIpcMain.on(&#39;openLoginWindow&#39;, (event, delay) =&gt; {
    setTimeout(() =&gt; {
        loginWindow = createLoginWindow(mainWindow);
    }, delay * 1000)
})

preload.js (main process)

As you can see below, I have requested the main window get minimised first just to make sure IPC continues to work when a second IPC message is sent.

// Import the necessary Electron modules
const contextBridge = require(&#39;electron&#39;).contextBridge;
const ipcRenderer = require(&#39;electron&#39;).ipcRenderer;

// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
    // Allowed &#39;ipcRenderer&#39; methods
    &#39;ipcRenderer&#39;, {
        // From render to main
        delayOpenLoginWindow: (delay) =&gt; {
            ipcRenderer.send(&#39;minimiseMainWindow&#39;);
            ipcRenderer.send(&#39;openLoginWindow&#39;, delay);
        }
    }
);

And how it works...

/**
 *
 * Main --&gt; Render
 * ---------------
 * Main:    window.webContents.send(&#39;channel&#39;, data); // Data is optional.
 * Preload: apiName: (listener) =&gt; { ipcRenderer.on(&#39;channel&#39;, listener); }
 * Render:  window.ipcRenderer.apiName((event, data) =&gt; { methodName(data); });
 *
 * Main --&gt; Render (Once)
 * ----------------------
 * Main:    window.webContents.send(&#39;channel&#39;, data); // Data is optional.
 * Preload: apiName: (listener) =&gt; { ipcRenderer.once(&#39;channel&#39;, listener); }
 * Render:  window.ipcRenderer.apiName((event, data) =&gt; { methodName(data); });
 *
 * Render --&gt; Main
 * ---------------
 * Render:  window.ipcRenderer.apiName(data); // Data is optional.
 * Preload: apiName: (data) =&gt; { ipcRenderer.send(&#39;channel&#39;, data) }
 * Main:    electronIpcMain.on(&#39;channel&#39;, (event, data) =&gt; { methodName(data); });
 *
 * Render --&gt; Main (Once)
 * ----------------------
 * Render:  window.ipcRenderer.apiName(data); // Data is optional.
 * Preload: apiName: (data) =&gt; { ipcRenderer.send(&#39;channel&#39;, data); }
 * Main:    electronIpcMain.once(&#39;channel&#39;, (event, data) =&gt; { methodName(data); });
 *
 * Render --&gt; Main (Value) --&gt; Render
 * ----------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) =&gt; { methodName(result); });
 * Preload: apiName: (data) =&gt; { return ipcRenderer.invoke(&#39;channel&#39;, data); }
 * Main:    electronIpcMain.handle(&#39;channel&#39;, (event, data) =&gt; { return someMethod(data); });
 *
 * Render --&gt; Main (Promise) --&gt; Render
 * ------------------------------------
 * Render:  window.ipcRenderer.apiName(data).then((result) =&gt; { methodName(result); });
 * Preload: apiName: (data) =&gt; { return ipcRenderer.invoke(&#39;channel&#39;, data); }
 * Main:    electronIpcMain.handle(&#39;channel&#39;, async (event, data) =&gt; {
 *              return await myPromise(data).then((result) =&gt; { return result; })
 *          });
 *
 * Main:    function myPromise(data) { return new Promise((resolve, reject) =&gt; { ... }; )};
 *
 */

index.html (render process)

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;title&gt;Main Window&lt;/title&gt;
        &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;script-src &#39;self&#39; &#39;unsafe-inline&#39;;&quot;/&gt;
    &lt;/head&gt;

    &lt;body&gt;
        &lt;h1&gt;Main Window&lt;/h1&gt;

        &lt;p&gt;
            &lt;span&gt;Open the login window &lt;input type=&quot;number&quot; id=&quot;delay&quot; min=&quot;5&quot; max=&quot;30&quot; step=&quot;5&quot; value=&quot;5&quot;&gt; seconds after clicking the below Minimise Window button.&lt;/span&gt;
        &lt;/p&gt;

        &lt;hr&gt;

        &lt;input type=&quot;button&quot; id=&quot;button&quot; value=&quot;Minimise Window&quot;&gt;
    &lt;/body&gt;

    &lt;script&gt;
        document.getElementById(&#39;button&#39;).addEventListener(&#39;click&#39;, () =&gt; {
            window.ipcRenderer.delayOpenLoginWindow(document.getElementById(&#39;delay&#39;).valueAsNumber);
        })
    &lt;/script&gt;
&lt;/html&gt;

login.html (render process)

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;title&gt;Login Window&lt;/title&gt;
        &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;script-src &#39;self&#39; &#39;unsafe-inline&#39;;&quot;/&gt;
    &lt;/head&gt;

    &lt;body&gt;
        &lt;h1&gt;Login window&lt;/h1&gt;
    &lt;/body&gt;
&lt;/html&gt;

huangapple
  • 本文由 发表于 2023年3月9日 19:29:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75683990.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定