已验证环境:Windows 10 + (Chrome, Edge, IE 10)

HTML

我们写一个最简单的网页用来测试

1
<a href="myproto:1">Click Me</a>

myproto是我们自定义的协议,1是需要传给本地应用程序的参数。

C++本地应用程序

用C++写一个最简单的应用程序,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <filesystem>
#include <fstream>
int main(int argc, char* argv[])
{
std::string filename = (std::filesystem::temp_directory_path() / "myproto.txt").u8string();
std::ofstream ofs;
ofs.open(filename);
for (int i = 0; i < argc; ++i) {
ofs << argv[i] << std::endl;
}
ofs.close();
return 0;
}

这个程序只有一个功能,把所有传入的参数写入到系统临时目录的myproto.txt文件中。

注册表

在注册表的HKEY_CLASSES_ROOT添加自定义协议的项。

1
2
3
4
5
$ reg add HKEY_CLASSES_ROOT\myproto /t REG_SZ /d "My Protocol" /f
$ reg add HKEY_CLASSES_ROOT\myproto /t REG_SZ /v "URL Protocol" /d "" /f
$ reg add HKEY_CLASSES_ROOT\myproto\shell /f
$ reg add HKEY_CLASSES_ROOT\myproto\shell\open /f
$ reg add HKEY_CLASSES_ROOT\myproto\shell\open\command /t REG_SZ /d "\"C:\Users\Administrator\source\repos\myproto\Debug\myproto.exe\" \"%%1\""

相应地,你可以通过下面的命令还删除注册表。

1
$ reg delete HKEY_CLASSES_ROOT\sxw-pdf-gen /f

如果只添加在当前用户下,把HKEY_CLASSES_ROOT替换成HKEY_CURRENT_USER\Software\Classes即可。

测试

在浏览器中点击网页中的Click Me, 会弹出如下的提示框。

点击”打开myproto.exe”,检查临时目录中的myproto.txt,看到已成功写入。

1
2
C:\Users\Administrator\source\repos\myproto\Debug\myproto.exe
myproto:1

网页端检测自定义协议

在某些场景中,我们可能并没有事先安装本地应用程序。需要网页端检测后提示用户安装。

我使用了custom-protocol-detection来进行检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<script src="protocolcheck.js"></script>
<script>
function foo(url) {
window.protocolCheck(url, function() { alert("protocol(myproto) not recognized") });
}
</script>
</head>
<body>
<a href="#" onclick="foo('myproto:1')">Click Me</a>
</body>
</html>

如果没检测到,会弹出如下的提示框。

经实际验证,此种检测方式有缺陷,比如在firefox上无法检测,在chrome上的hack方式也不能保证所有版本都能正常使用。

有StackOverflow网友说用特殊字体,在安装时安装一个特殊字体到系统,然后在前端进行字体检测。此种方式仍然有缺陷,需要Chrome打开访问本地字体的flag。

最终,在实际项目中,我们放弃了自定义协议检测,参考了夸克网盘的方式,让用户自己下载。如下图:

可能遇到的问题

  • 在Windows 7, Firefox下无法唤起客户端,会出现如下图所示的提示

    解决方法:修改Firefox的配置。

    设置->应用程序->foo->操作:使用其他->选择foo对应路径下的foo.exe。

    其中foo为你的自定义协议。

Reference