未定义的角度 11 电子工控机通信'send'



我做了第一次尝试,想使用角度渲染器进程中的电子api。我遵循中的说明

创建角电子应用的说明

所以在我的main.js文件中,我添加了:

const {app, BrowserWindow, ipcMain} = require('electron')

我还添加了

function openModal(){
const { BrowserWindow } = require('electron');
let modal = new BrowserWindow({ parent: mainWindow, modal: true, show: false })
modal.loadURL('https://www.sitepoint.com')
modal.once('ready-to-show', () => {
modal.show()
})
}
ipcMain.on('openModal', (event, arg) => {
openModal()
})

在我的app.component.ts文件中,添加了导入import { IpcRenderer } from 'electron';

我添加了以下构造函数

private ipc: IpcRenderer
constructor(){
if ((<any>window).require) {
try {
this.ipc = (<any>window).require('electron').ipcRenderer;
} catch (e) {
throw e;
}
} else {
console.warn('App not running inside Electron!');
}
}

由于我的CLI并不完全清楚icp将是我在这行中添加的IpcRenderer类型

private ipc: IpcRenderer | any;

具有功能

openModal(){
console.log("Open a modal");
this.ipc.send("openModal");
}

它应该能够向";主";过程但如果我调用函数,我会得到错误

TypeError:无法读取未定义的属性"send">

我做错了什么?

我也遇到过同样的问题,但发现了一个长时间的破解。

原因

看看这一行:

(<any>window).require

此行在全局窗口对象中搜索require()方法,这是一个NodeJS函数,因此如果您使用Electron版本>5,因为BrowserWindow类的webPreferences -> nodeIntegration属性默认设置为false。

解决方案

有两种解决方案:

  1. 设置nodeIntegration: truecontextIsolation: false应用程序将按预期运行此黑客攻击存在安全问题。如本答复所述:

Electron应用程序很棒,因为我们可以使用node,但这种能力是一把双刃剑。如果我们不小心,我们会通过我们的应用程序允许某人访问node,而使用node,一个糟糕的参与者可能会损坏您的机器或删除您的操作系统文件(我想还有其他事情(。

  1. 使用contextBridge和preload.js:这是从上面的答案和这个问题的其他答案中得出的

我将使用Angular提供一个解决方法。

在main.js中创建mainWindow时,在webPreferences中进行以下修改:

webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, "preload.js")
}

main.js所在的同一目录中,创建一个preload.js文件并在其中添加以下代码(您可以省略receive()声明,因为在本用例中不需要它(。

const {
contextBridge,
ipcRenderer
} = require("electron");
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
"api", {
send: (channel, data) => {
// whitelist channels
let validChannels = ["openModal"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender` 
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
}
);

附言:添加此代码的原因已经在参考答案中提到,所以我不会在这里重复

现在,在运行时,电子将在全局window对象中注入window.api.send()window.api.receive()方法。如果您尝试在角度组件中直接访问它们,linter将出错,因为它们没有在标准窗口对象上定义。为了解决这个问题,我们将创建一个Angular服务并在我们的应用程序中注入。此服务将引用在运行时创建的浏览器的窗口对象,并添加了以上两个函数。

创建一个WindowRefService并添加以下代码:

窗口引用服务.ts

import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class WindowRefService {
getWindow(): any {
return window;
}
constructor() { }
get nativeWindow(): Window{
return this.getWindow();
}
}

不要忘记在AppModule中注册服务:

...
import { WindowRefService } from './window-ref.service';
...
@NgModule{
...
providers: [WindowRefService]
}

之后将其注入您的组件:

import { WindowRefService } from './window-ref.service';
...

private _window:any;
constructor(windowRef: WindowRefService){
this._window = windowRef.nativeWindow;
}
...
//openModal function
openModal(){
console.log("Open a modal");
this._window.api.send("openModal", /* data to be sent, if any*/);
}

删除构造函数中的private ipc: IpcRenderer | any;和其他现有代码,因为这不是必需的。

最新更新