一、使用reg文件测试
reg文件内容:
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\密存加密\command] @="\"D:\\application\\secretsave\\secretsave.exe\" \"%1\"\1" [HKEY_CLASSES_ROOT\*\shell\密存解密\command] @="\"D:\\application\\secretsave\\secretsave.exe\" \"%1\"\2"
HKEY_CLASSES_ROOT其实就是HKEY_LOCAL_MACHINE\SOFTWARE\Classes,包含了所有应用程序运行时必需的信息:
在文件和应用程序之间所有的扩展名和关联;
所有的驱动程序名称; 类的ID数字(所要存取项的名字用数字来代替);
用于应用程序和文件的图标;
在注册表中HKEY_CLASSES_ROOT是系统中控制所有数据文件的项。这个在Win95和Winnt中是相通的。HKEY_CLASSES_ROOT控制键包括了所有文件扩展和所有和执行文件相关的文件。它同样也决定了当一个文件被双击时起反应的相关应用程序。
右键菜单的开启
HKEY_CLASSES_ROOT\*\shell\密存加密\command意思是打开cmd
@=
"\"D:\\application\\secretsave\\secretsave.exe\" \"%1\"\1"
这句话是cmd里面的命令,应用路径,最后面的1是参数<br>将req文件放在安装好的应用根目录下,右键编辑注册表,修改成功
二、对参数处理
在主进程main/index.js里面
app.on('ready', function () { createWindow() global.sharedObject = {prop1: process.argv} })
在render的vue文件里接收
mounted () { let args = remote.getGlobal('sharedObject').prop1 console.log(args) let types = ['1','2'] if (args.length >= 3 && types.includes(args[2])) { args[1] = args[1].replace(/\\/g, '/') this.getArgFile(args) } }
右键点击文件唤起应用时args打印结果
["D:\application\secretsave\secretsave.exe", "D:\work\electron\AesTest.rar", "1"]
数组第二个值是文件路径,第三个值是注册表传递过来的参数
三、使用electron-builder里面的nsis对象,让应用在安装时写入注册表
1、package.json里面配置:
"nsis": { "oneClick": false, "perMachine": true, "allowElevation": true, "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "runAfterFinish": true, "shortcutName": "无忧密存", "installerIcon": "./static/icon.ico", "uninstallerIcon": "./static/icon.ico", "include": "installer.nsh" },
- include 指定要包含 nsis 的脚本,基于内置的nsis脚本进一步扩展,这个对于构建需求严格得安装过程相当有用
- script 指定自定义使用 nsis 的脚本,完全自己控制nsis 的打包,用于自定义安装程序,默认为build / installer.nsi
关于include
和 script
到底选择哪一个 ?
在对个性化安装过程需求并不复杂,只是需要修改一下安装位置,卸载提示等等的简单操作建议使用include
配置,如果你需要炫酷的安装过程,建议使用script
进行完全自定义。
我们只是添加两个按钮,使用include就好
2、编写installer.nsh文件,放在build文件夹下
!macro customInstall WriteRegStr HKCR "CenDC" "URL Protocol" "" WriteRegStr HKCR "CenDC" "" "URL:CenDC Protocol Handler" WriteRegStr HKCR "*\shell\密存加密\command" "" '"$INSTDIR\secretsave.exe" "%1" "1"' WriteRegStr HKCR "*\shell\密存解密\command" "" '"$INSTDIR\secretsave.exe" "%1" "2"'
!macroend !macro customUninstall DeleteRegKey HKCR "*\shell\密存加密" DeleteRegKey HKCR "*\shell\密存解密"
!macroend
简单解释脚本的含义,具体了解详情请看下方参考资料:
!macro 是定义宏
customInstall会在文件安装后自动调用(electron-builder实现)
WriteRegStr 是写注册表 如果原来有会覆盖。
$INSTDIR 是所选的文件安装路径
HKCR
即是注册表目录HKEY_CLASSES_ROOT
的缩写。在写value
的时候如果要写多个参数,可以用单引号包起来。attr-name
不写即为默认
customUnInstall在卸载阶段将之前写的注册表删除,以免用户卸载了应用之后菜单还在
四、问题修改
效果虽然达到了,但是每次右键都会新开一个应用,node服务端口就会占用
1、解决端口占用问题,实现单例应用的命令行调用:如果应用已经打开的情况下,不打开新窗口
Electron版本v4.x以上用的是app.
requestSingleInstanceLock,2.0用的是app.makeSingleInstance
const { app } = require('electron') let mainWindow = null
const gotTheLock = app.requestSingleInstanceLock() // 拿到单例锁
if (!gotTheLock) { // 如果一个应用二次打开,那么getTheLock为false
app.quit() // 立即退出二次打开的应用
} else { app.on('second-instance', (event, commandLine, workingDirectory) => { // 一个应用尝试打开第二个实例时触发
if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() } }) // Create myWindow, load the rest of the app, etc...
app.on('ready', () => { }) }
端口占用的问题解决了,但是数据无法传递,应用只是聚焦,并没有做任何处理
2、进程通信修改
global.sharedObject可以做到数据共享,却没有实质的通信功能;
ipcMain与
ipcRenderer需要渲染进程先发消息,于是选择了主进程用
webContents.send发送消息,渲染进程用ipcRenderer监听
修改后代码如下:
if (!gotTheLock) { app.quit() } else { app.on('second-instance', (event, commandLine, workingDirectory) => { // 当运行第二个实例时,将会聚焦到myWindow这个窗口
if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() global.sharedObject = {prop1: process.argv} mainWindow.webContents.send('getRightPath', process.argv) /* dialog.showMessageBox({ title: 'second', message: 'second:' + commandLine + ' workingDirectory' + workingDirectory }) */ } else { if (app.isReady()) createWindow() global.sharedObject = {prop1: process.argv} } }) // mac环境
app.on('open-url', (event, commandLine, workingDirectory) => { // 当运行第二个实例时,将会聚焦到myWindow这个窗口
if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() global.sharedObject = {prop1: process.argv} mainWindow.webContents.send('getRightPath', process.argv) } else { if (app.isReady()) createWindow() global.sharedObject = {prop1: process.argv} } }) // 创建 myWindow, 加载应用的其余部分, etc...
app.on('ready', () => { createWindow() global.sharedObject = {prop1: process.argv} }) }
渲染进程vue组件里面改为:
const ipc = require('electron').ipcRenderer methods: { dealArgs () { let args = remote.getGlobal('sharedObject').prop1 // console.log("666--:",args)
let types = ['1','2'] if (args.length >= 3 && types.includes(args[2])) { args[1] = args[1].replace(/\\/g, '/') this.getArgFile(args) } } }, mounted () { let _this = this
this.dealArgs() // 第一次进入时也处理
ipc.on('getRightPath', function (event, argv) { _this.dealArgs() }) }
参考资料:
Electron-vue开发实战7——命令行调用与系统级别右键菜单项的实现
electron-builder构建的安装包,安装时通过nsis脚本自动导入注册表
Electron 渲染进程之间的通信