electron 开发经验之谈系列-electron 自定义协议

本文最后更新于:2021年3月22日 早上

场景

有时候需要与其他程序进行交互时,自定义协议是一个不错的选择 – 它能在程序为启动时启动程序然后处理其它程序的动作,而这是其它解决方案,包括 HTTP 请求、共享数据库不能比的。其实日常生活中也有现成的例子,迅雷的自定义协议下载链接、BitTorrent 协议、百度网盘启动本地客户端等等。

使用

参考: 将当前可执行文件设置为协议的默认处理程序(注册表级别)

  1. 让程序保持单例启动
  2. 设置客户端支持的协议(在 Windows 中会写入到注册表)
  3. 处理命令行参数找到其中需要的 url 信息
  4. 监听 readysecond-instance 事件

让程序保持单例启动

参考: app.requestSingleInstanceLock()
注: 仅在单例模式下才能监听 second-instance 事件

1
2
3
4
5
6
// 请求单例锁,避免打开多个 electron 实例
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
return;
}

设置客户端支持的协议(在 Windows 中会写入到注册表)

参考: app.setAsDefaultProtocolClient(protocol[, path, args])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { app } from "electron";
import path = require("path");

/**
* 客户端默认支持的协议
*/
export class DefaultProtocolClient {
constructor(public readonly protocol: string) {}

/**
* 注册一个默认支持打开的协议
*/
register() {
// 开发模式下在 window 运行需要做兼容
if (
process.env.NODE_ENV === "development" &&
process.platform === "win32"
) {
// 设置 electron.exe 和 app 的路径
app.setAsDefaultProtocolClient(this.protocol, process.execPath, [
path.resolve(process.argv[1]),
]);
} else {
app.setAsDefaultProtocolClient(this.protocol);
}
}

/**
* 从命令行参数中找到 url
* @param argv
*/
findUrl(argv: string[]): string | undefined {
const regExp = new RegExp(`^${this.protocol}://`);
return argv.find((str) => regExp.test(str));
}
}

const defaultProtocolClient = new DefaultProtocolClient("custom-protocol");

await defaultProtocolClient.register();

处理命令行参数找到其中需要的 url 信息

添加函数 handleDefaultProtocol 从命令行参数中找到 url 然后处理它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 处理客户端支持的默认协议
* @param argv
*/
async function handleDefaultProtocol(argv: string[]) {
const url = defaultProtocolClient.findUrl(argv);
if (!url) {
return;
}
await dialog.showMessageBox({
type: "info",
message: "window protocol 自定义协议打开",
detail: ` 链接:${url}`,
});
}

监听 readysecond-instance 事件

参考: 事件: ‘second-instance’

1
2
3
4
5
6
7
app.addListener("second-instance", async (event, argv) => {
await handleDefaultProtocol(argv);
});
app.addListener("ready", async () => {
await createMainWindow();
await handleDefaultProtocol(process.argv);
});

外部调用

既然我们自定义协议的目的是让外部程序调用,那么如何使用外部调用就很重要了。

首先检查注册表中是否已经包含它了,操作 ctrl+s => 搜索注册表 => 进入注册表 => ctrl+f 查找 custom-protocol

注册表

浏览器打开

如上图所示,可以简单在浏览器中输入 custom-protocol://test 来启动程序。

自定义协议效果

nodejs 示例

在 nodejs 中使用 npm 包 open 可以轻易打开自定义默认链接。

1
2
3
import * as open from "open";

open("custom-protocol://test");

其实本质上就是拼接命令,然后执行系统命令打开 url,参考它的实现