一、背景
某项目包含一个用于展示的数字大屏和多个数字孪生应用。大屏由内部团队开发并云端部署,而数字孪生应用则由第三方开发并安装在本地电脑。
现有需求是实现通过点击大屏上的特定区域,触发并打开相应的数字孪生程序。
二、方案汇总
2.1 方案一:将大屏改为本地部署,通过node进行exe调用
通过node的exec
方法,可以实现打开本地应用、执行脚本、传递参数等一系列功能。例如下方是一个打开记事本的node示例:
1 2 3 4 5 6 7
| exec('notepad.exe', (err, stdout, stderr) => { if (err) { console.error(err); return; } });
|
官方文档对于exec的介绍
该方案优势在于低成本和访问速度快:改动内容较少,只需在本地启动Node.js并运行项目,另一方面将代码部署在内网还能加快加载速度并减少云服务费用。但是开发团队位置与大屏展示地点不在一起,导致大屏内容更新会更麻烦。
2.2 方案二:改造数字孪生应用,增加URL Protocol协议
类似于钉钉、QQ、百度网盘等应用,都有对应的URL Protocol来启动客户端应用,如果第三方公司愿意给数字孪生大屏加上相应的URL Protocol协议,也能实现该需求。
在网页唤起“钉钉”应用的对话框
2.3 方案三:修改注册表,添加自定义的URL Protocol协议
在网上搜到了一篇资料《在网页中执行本地exe程序的两种方式》,其中有提到自定义添加URL Protocol协议的方法。这个方案改动更小,只需要再注册一个协议即可,缺点是在协议中需要固定程序路径。
三、确定方案
首先排除了方案一,相较于三方交付的数字孪生应用,内部自研的数据大屏改动概率会更高一些。例如有些时候会根据来访用户定制一些展示内容、新的业务板块需要放在大屏展示等等。另一个因素是展示地点缺乏技术人员,其他人去更换程序、启动项目会比较麻烦,出现问题也不好排错。
其次排除了方案二,对于委外研发的项目,每一次微小改动都涉及到付费调整的问题。即使是修改贴图logo也需要额外签订合同和付费,涉及到费用问题和定制流程都比较麻烦。
所以最后选择了方案三,由内部研发人员编写一个注册表脚本,提供给展示地点的工作人员,由他们在本地电脑上双击运行脚本添加注册项。
四、脚本及调试文件
安装和卸载脚本比较简单,这里就直接提供了示例文件,只需要修改图中红色背景部分的内容即可。
4.1 安装脚本
文件名示例:installDemoProtocol.reg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\demoprotocol] @="demoprotocol Protocol" "URL Protocol"=""
[HKEY_CLASSES_ROOT\demoprotocol\DefaultIcon] @="C:\\Program Files (x86)\\demo\\XXX.eve"
[HKEY_CLASSES_ROOT\demoprotocol\shell] @=""
[HKEY_CLASSES_ROOT\demoprotocol\shell\open] @=""
[HKEY_CLASSES_ROOT\demoprotocol\shell\open\command] @="\"C:\\Program Files (x86)\\demo\\XXX.eve\" "
|
红底部分为需要调整的内容
4.2 卸载脚本
文件名示例:uninstallDemoProtocol.reg
1 2 3
| Windows Registry Editor Version 5.00
[-HKEY_CLASSES_ROOT\demoprotocol]
|
红底部分为需要调整的内容
4.3 网页应用
在网页端只需要访问demoprotocol://
即可打开对应程序,可以用按钮或者链接形式触发打开动作。当然,肯定会有监听应用是否成功打开的需求,但这一块是没有现成API来判断的,只能用一些奇淫巧计来完成。思路是这样的:当在浏览器中触发URL Protocol协议时,浏览器会弹出对话框与用户确认,这个行为会导致页面失焦。
所以可以通过判断访问demoprotocol://
后的1秒内,页面是否失焦来判断应用是否被打开。当然,如果用户在访问链接的一秒内切换到了其他应用,也会被误判成打开成功,但这是小概率事件,也无法避免。
下方是一个简单的示例页面:
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 41 42 43 44
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Custom Protocol Detection</title> <script> function detectCustomProtocol() { let start = Date.now(); let protocolCalled = false;
const iframe = document.createElement("iframe"); iframe.style.display = "none"; document.body.appendChild(iframe);
setTimeout(() => { if (!protocolCalled) { alert("Custom protocol call failed or blocked."); } document.body.removeChild(iframe); }, 1500);
try { iframe.contentWindow.location.href = "demoprotocol://";
window.addEventListener("blur", () => { let end = Date.now(); if (end - start < 1000) { protocolCalled = true; } });
} catch (e) { alert("Custom protocol call failed or blocked."); } } </script> </head> <body> <button onclick="detectCustomProtocol()">Test Custom Protocol</button> </body> </html>
|
五、参考资料