一、使用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 渲染進程之間的通信