Electron - uncatch TypeError: 無法讀取未定義的屬性(讀作 'showOpenDialog')



我试图打开关于电子的对话框,但我得到了这个错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'showOpenDialog')

我已经访问了几个论坛和社区,看看我是否有解决问题的方法。但没有一个解决方案,也许是因为这个版本。

我目前使用的电子版本是16.0.5

这个答案对我帮助不大

https://stackoverflow.com/a/63756725/14271847

我不会留下所有的main.js,但我更改的部分是这个,enableRemoteModule:

const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule:true,
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js')
}
})

文件test.js

下面是我试图在电子上做的,遵循这个链接中的

const { dialog } = require('electron')
console.log(dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))

我用遥控器试过,但不起作用:

const { dialog } = require('electron')
console.log(dialog.remote.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))

有人能帮忙吗?

启用上下文隔离和禁用nodeIntegration是目前真正保护Electron应用程序安全的最佳做法。此外,由于我们现在有进程间通信,因此不鼓励使用remote

由于不知道您的preload.js脚本是什么样子,我在下面包含了一个简单的preload.js脚本,该脚本仅使用白名单通道名称列表和ipcRenderer方法的实现。也就是说,拥有一个不同外观的preload.js脚本应该不会对应该在您的main.jsindex.html文件,以使您的对话框正常工作。


使用invoke方法将允许我们向主进程发送一条消息以打开对话框接收一个回复,所有这些都是一体的。

preload.js(主流程(

// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'dialog:openMultiFileSelect' // Channel name
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);

下面显示了上述preload.js脚本的使用摘要。

/**
* Render --> Main
* ---------------
* Render:  window.ipcRender.send('channel', data); // Data is optional.
* Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main:    windowName.webContents.send('channel', data); // Data is optional.
* Render:  window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main:    electronIpcMain.handle('channel', async (event, data) => {
*              return await promiseName(data)
*                  .then(() => { return result; })
*          });
*/

main.js脚本中,使用handle方法,让我们在dialog:openMultiFileSelect通道上侦听消息。收到后,打开dialog.showOpenDialog()方法,.then等待结果。一旦收到结果(IE:选择文件并接受对话框(,就将结果(通过handle方法(返回到渲染过程。

main.js(主流程(

const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const 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();
}
});
// ---
electronIpcMain.handle('dialog:openMultiFileSelect', () => {
let options = {
properties: ['openFile', 'multiSelections']
};
return electronDialog.showOpenDialog(window, options)
.then((result) => {
// Bail early if user cancelled dialog
if (result.canceled) { return }
return result.filePaths;
})
})

最后,在index.html文件中,我们监听按钮上的click事件,然后通过dialog:openMultiFileSelect通道发送消息以打开对话框(在主进程中(。返回结果后,.then将显示所选文件。

注意我们如何检测undefined的结果并相应地进行管理。当用户取消对话框时,会发生这种情况。

index.html(渲染过程(

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<input type="button" id="button" value="Open File Dialog">
<ul id="paths"></ul>
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
window.ipcRender.invoke('dialog:openMultiFileSelect')
.then((paths) => {
if (paths === undefined) { return } // Dialog was cancelled
let result = '';
for (let path of paths) {
result += '<li>' + path + '</li>';
}
document.getElementById('paths').innerHTML = result;
})
})
</script>
</html>

相关内容

最新更新