web页面调用本地程序
起因:
最近由于项目需要在web页面中调用本地部署的exe程序;进而对该功能实现做了对应了解;以及存在的问题进行记录。
要实现该功能就不得不说浏览器自定义协议;解决办法:那么它是什么呢?
浏览器自定义协议:
浏览器自定义协议,其实是微软提供 Asynchronous Pluggable Protocols;可以用来注册本地应用程序到 URI Scheme
https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85)
实现自定义协议方式—添加注册表:
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT协议名称] @="程序运行地址" "URL Protocol"="" [HKEY_CLASSES_ROOTcalldemoDefaultIcon] @="程序运行地址,1" [HKEY_CLASSES_ROOTcalldemoshell] [HKEY_CLASSES_ROOTcalldemoshellopen] [HKEY_CLASSES_ROOTcalldemoshellopencommand] @="程序地址" "%1""
自定义协议实现示例:
示例实现:实现一个本地Exe,并注册到注册表中;并运行效果。(程序比较简单,可以查看github)
程序实现写入注册表主要逻辑:
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main(string[] args) { RegisterUrlProtocol(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var from = new Form1(); //显示输入参数 from.Args = args; Application.Run(from); }
/// <summary> /// 注册自定义协议 /// </summary> private static void RegisterUrlProtocol() { try { //检查是否注册自定义协议:如未注册则注册 Register register = new Register("calldemo", RegDomain.ClassesRoot); if (!register.IsSubKeyExist("calldemo")) { //注册: register.CreateSubKey(); register.WriteRegeditKey("", $"{Application.ExecutablePath}"); register.WriteRegeditKey("URL Protocol", ""); if (!register.IsSubKeyExist(@"calldemoDefaultIcon")) { register.CreateSubKey(@"calldemoDefaultIcon"); register.SubKey = @"calldemoDefaultIcon"; register.WriteRegeditKey("", $"{Application.ExecutablePath},1"); } if (!register.IsSubKeyExist(@"calldemoshell")) { register.CreateSubKey(@"calldemoshell"); register.CreateSubKey(@"calldemoshellopen"); register.CreateSubKey(@"calldemoshellopencommand"); register.SubKey = @"calldemoshellopencommand"; //添加默认键 register.WriteRegeditKey("", $""{Application.ExecutablePath}" "%1""); } } } catch (Exception e) { MessageBox.Show(e.Message); throw; } }
创建检验html:
<a href="calldemo:123qwe">UrlProtocolDemo</a>
运行效果:
github地址:https://github.com/cwsheng/URLProtocolDemo.git
问题记录:
1、关于js中检验浏览器自定义协议是否存在,现在没有教好的解决办法?
开源项目:https://github.com/ismailhabib/custom-protocol-detection(亲测无效,且不维护了)
https://github.com/Veryfirefly/custom-protocol-detection(原理同上,也无效)
问题:https://stackoverflow.com/questions/836777/how-to-detect-browsers-protocol-handlers
2、每次调用启动exe,都会新运行一个程序实例;可以通过程序实现判断该程序是否已经在运行。
#region 确保程序只运行一个实例 private static Process RunningInstance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(current.ProcessName); //遍历与当前进程名称相同的进程列表 foreach (Process process in processes) { //如果实例已经存在则忽略当前进程 if (process.Id != current.Id) { //保证要打开的进程同已经存在的进程来自同一文件路径 if (Assembly.GetExecutingAssembly().Location.Replace("/", "\") == current.MainModule.FileName) { //返回已经存在的进程 return process; } } } return null; } //3.已经有了就把它激活,并将其窗口放置最前端 private static void HandleRunningInstance(Process instance) { ShowWindowAsync(instance.MainWindowHandle, 1); //调用api函数,正常显示窗口 SetForegroundWindow(instance.MainWindowHandle); //将窗口放置最前端 } [DllImport("User32.dll")] private static extern bool ShowWindowAsync(System.IntPtr hWnd, int cmdShow); [DllImport("User32.dll")] private static extern bool SetForegroundWindow(System.IntPtr hWnd); #endregion
最后:
当然该方式不一定是唯一实现方式,也可以尝试使用WebAssembly实现本地运行程序逻辑,本次未进行验证
如果js判断自定义协议是否存在,有好到方法也希望能得到大家的解答。