现有需求是实现通过点击大屏上的特定区域,触发并打开相应的数字孪生程序。
通过node的 exec 方法,可以实现打开本地应用、执行脚本、传递参数等一系列功能。例如下方是一个打开记事本的node示例:
exec
1234567
exec('notepad.exe', (err, stdout, stderr) => { if (err) { console.error(err); return; } // ...});
官方文档对于exec的介绍
该方案优势在于 低成本 和 访问速度快 :改动内容较少,只需在本地启动Node.js并运行项目,另一方面将代码部署在内网还能加快加载速度并减少云服务费用。但是开发团队位置与大屏展示地点不在一起,导致 大屏内容更新会更麻烦 。
类似于钉钉、QQ、百度网盘等应用,都有对应的URL Protocol来启动客户端应用,如果第三方公司愿意给数字孪生大屏加上相应的URL Protocol协议,也能实现该需求。
在网页唤起“钉钉”应用的对话框
在网上搜到了一篇资料《 在网页中执行本地exe程序的两种方式 》,其中有提到自定义添加URL Protocol协议的方法。这个方案改动更小,只需要再注册一个协议即可,缺点是 在协议中需要固定程序路径 。
首先排除了方案一,相较于三方交付的数字孪生应用,内部自研的数据大屏改动概率会更高一些。例如有些时候会根据来访用户定制一些展示内容、新的业务板块需要放在大屏展示等等。另一个因素是展示地点缺乏技术人员,其他人去更换程序、启动项目会比较麻烦,出现问题也不好排错。
其次排除了方案二,对于委外研发的项目,每一次微小改动都涉及到付费调整的问题。即使是修改贴图logo也需要额外签订合同和付费,涉及到费用问题和定制流程都比较麻烦。
所以最后选择了方案三,由内部研发人员编写一个注册表脚本,提供给展示地点的工作人员,由他们在本地电脑上双击运行脚本添加注册项。
安装和卸载脚本比较简单,这里就直接提供了示例文件,只需要修改图中红色背景部分的内容即可。
文件名示例:installDemoProtocol.reg
1234567891011121314151617
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\" "
红底部分为需要调整的内容
文件名示例:uninstallDemoProtocol.reg
123
Windows Registry Editor Version 5.00[-HKEY_CLASSES_ROOT\demoprotocol]
在网页端只需要访问 demoprotocol:// 即可打开对应程序,可以用按钮或者链接形式触发打开动作。当然,肯定会有 监听应用是否成功打开 的需求,但这一块是没有现成API来判断的,只能用一些奇淫巧计来完成。思路是这样的: 当在浏览器中触发URL Protocol协议时,浏览器会弹出对话框与用户确认,这个行为会导致页面失焦 。
demoprotocol://
所以可以通过判断访问 demoprotocol:// 后的1秒内,页面是否失焦来判断应用是否被打开。当然, 如果用户在访问链接的一秒内切换到了其他应用,也会被误判成打开成功 ,但这是小概率事件,也无法避免。
下方是一个简单的示例页面:
1234567891011121314151617181920212223242526272829303132333435363738394041424344
<!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; // 创建一个隐藏的 iframe 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); // 超时时间设置为1.5秒 // 尝试调用自定义协议 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>