electron-vue项目修改注册表在系统右键菜单中添加功能

一、使用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是参数
将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 构建打包总结

Electron 渲染进程之间的通信

 

 

 

 

   

posted @ 2020-05-20 23:20  菲比月  阅读(1360)  评论(0编辑  收藏  举报