Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9350

ElectronアプリをURLから起動する

$
0
0
Electronアプリをブラウザ上のURLから開き、URLに含まれた情報をレンダラープロセスで利用できるようにする実装方法をまとめます。(mac, windows用) 特にwindows用の実装の情報が少なく、苦戦したので、同じような状況の方の参考になりましたら幸いです。 環境 electron v16(v12でも動作しました) electron-builder v22 サンプルリポジトリ 完成イメージ custom-url:// のようなURLからアプリを起動し、画面にそのURLを表示することができる アプリが既に起動している場合、していない場合も同じように動作する 実装ステップ 1. URLからアプリを起動する URLの情報を受け取ることは考えず、とりあえずURLをクリックすることでアプリが起動するようにします。 1-1. electron-builder用の設定を追加 起動したいcustom schemeを決めて、 package.json もしくは electron-builder.yml に以下のように追記します。 今回の例では custom-url:// から始まるURLから起動できるようにします。 package.json { ... "build": { ... "protocols": { "name": "Custom URL Sample", "schemes": [ "custom-url" ] } } } 1-2. OSのレジストリにプロトコルを登録するコードを追加(windowsのみ) windowsでは、上記の設定だけではアプリが起動されないため、 app.setAsDefaultProtocolClient でプロトコルを登録する必要があります。 main.js const { app } = require('electron'); const CUSTOM_SCHEME = 'custom-url'; ... // 初回起動時のみ必要だが、既に登録された状態で呼び出しても問題ない app.setAsDefaultProtocolClient(CUSTOM_SCHEME); ※ 初回起動時にはURLから開くことができなくても大丈夫な場合を想定しています。 2. メインプロセスでURLを取得する 起動時のURLを、まずメインプロセスで取得できるようにします。この実装はmacとwindowsで大きく異なります。 mac macでは、 app の "open-url" というイベントで簡単にURLを受け取ることができます。アプリが既に起動していても、していなくても同じように動作します。 main.js const { app } = require('electron'); ... app.on('open-url', (_event, url) => { // ここで第二引数にurlが渡ってくるため、次項でそれをレンダラープロセスに渡す }); windows A. アプリが起動していなかった場合 URLクリックによりアプリが起動した場合は、 process.argv の配列の中にURLが含まれます。 main.js const url = process.argv.find((arg) => arg.startsWith(`${CUSTOM_SCHEME}://`)); B. アプリが起動していた場合 windowsでは、アプリが起動している状態でURLをクリックすると、2つ目のアプリが重複して開こうとします。 そこで、 app.requestSingleInstanceLock() を呼び出すことで、 app が2つ目のアプリかどうかを確認し、かつその時のURLを1つ目のアプリでlistenしておくようにします。 main.js const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { // gotTheLockがfalseの場合、appは2つ目のアプリのインスタンスなので、即座に終了する app.quit(); } else { // 2つ目のアプリがrequestSingleInstanceLockを呼び出した時に、1つ目のアプリで発火される app.on('second-instance', async (_event, commandLineArgs) => { // 第二引数には、2つ目のアプリ起動時のprocess.argvと同じものが含まれる const url = commandLineArgs.find((arg) => arg.startsWith(`${CUSTOM_SCHEME}://`)); }); } A, Bをまとめると以下のようになります。 main.js const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { app.on('second-instance', async (_event, commandLineArgs) => { const url = commandLineArgs.find((arg) => arg.startsWith(`${CUSTOM_SCHEME}://`)); // 次項でレンダラープロセスに渡す }); const url = process.argv.find((arg) => arg.startsWith(`${CUSTOM_SCHEME}://`)); // 次項でレンダラープロセスに渡す } 3. レンダラープロセスにURLを渡す あとはURLをレンダラープロセスに渡すだけです。 1つ注意点として、URLからアプリを起動した場合は、レンダラープロセスで受け取る準備ができていないため、それを待つ必要があります。 レンダラープロセス index.js const { ipcRenderer } = require('electron'); window.onload = () => { ipcRenderer.on('url-opened', (_event, url) => { document.getElementById('url').textContent = url; }); ipcRenderer.send('window-ready', true); }; ※ 説明の簡略化のためレンダラープロセスで直接 ipcRenderer を利用していますが、本来はpreloadの利用が推奨されているようです。(参考: ElectronでcontextBridgeによる安全なIPC通信) メインプロセス 簡略化のため、macの場合のみを例示します。 main.js const { app, BrowserWindow, ipcMain } = require('electron'); let mainWindow; let windowReady = false; const getMainWindowWhenReady = async () => { if (!windowReady) { await new Promise((resolve) => ipcMain.once('window-ready', resolve)); } return mainWindow; }; app.on('open-url', async (_event, url) => { const mainWindow = await getMainWindowWhenReady(); mainWindow.send('url-opened', url); }); app.on('ready', () => { mainWindow = new BrowserWindow({ ... }); ipcMain.once('window-ready', () => { windowReady = true; }); }); ※ ウィンドウの準備を待つ実装については、もっと良い方法があるかもしれません。 また、windowsの場合は、2つ目のウィンドウが一瞬表示されてしまうことを防ぐなど、やや工夫が必要でしたので、詳細は サンプルリポジトリ の方もご覧ください。 参考

Viewing all articles
Browse latest Browse all 9350

Trending Articles