6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

Electronアプリをブラウザ上のURLから開き、URLに含まれた情報をレンダラープロセスで利用できるようにする実装方法をまとめます。(mac, windows用)

特にwindows用の実装の情報が少なく、苦戦したので、同じような状況の方の参考になりましたら幸いです。

環境

  • electron v16(v12でも動作しました)
  • electron-builder v22

サンプルリポジトリ

完成イメージ

キャプチャ.gif

  • 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つ目のウィンドウが一瞬表示されてしまうことを防ぐなど、やや工夫が必要でしたので、詳細は サンプルリポジトリ の方もご覧ください。

参考

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?