【问题标题】:How to detect browser's protocol handlers?如何检测浏览器的协议处理程序?
【发布时间】:2010-10-24 14:52:51
【问题描述】:

我创建了一个自定义 URL 协议处理程序。

http://

mailto://

custom://

我已经注册了一个 WinForms 应用程序来做出相应的响应。这一切都很好。

但我希望能够优雅地处理用户尚未安装自定义 URL 协议处理程序的情况。

为了能够做到这一点,我需要能够检测浏览器的注册协议处理程序,我会假设来自 JavaScript。但我一直无法找到一种方法来轮询信息。我希望找到解决这个问题的方法。

感谢您分享的任何想法。

【问题讨论】:

  • 我认为这只能在 chrome(即 XPCOM、ActiveX 等)代码中实现。否则,这将是一个隐私问题(“我们检测到您使用 Eudora。今天切换到 FooMail!”)。但请说明您感兴趣的浏览器/操作系统。
  • 好点,但我很高兴知道注册了一些东西来处理我的专有协议 acsfs:// Windows IE、FireFox,最好是 Safari
  • 这个问题你解决了吗?

标签: javascript browser protocols protocol-handler moniker


【解决方案1】:

这将是一个 very, very hacky 的方式来做到这一点......但这会奏效吗?

  • 正常放入链接...
  • 但附加一个 onclick 处理程序,它设置一个计时器并为窗口添加一个 onblur 处理程序
  • (理论上)如果浏览器处理链接(应用程序 X)将加载从窗口中窃取焦点...
  • 如果 onblur 事件触发,请清除计时器...
  • 否则在 3-5 秒内让您的超时触发...并通知用户“嗯,看起来您没有安装 Mega Uber Cool 应用程序...您想现在安装它吗?(好的)(取消)”

远非防弹......但它可能会有所帮助?

【讨论】:

  • :D 这是一个聪明的主意。似乎有办法,因为这似乎是一种普遍的需求。
  • 在 Mac 上的 Firefox(可能更多浏览器)中,即使使用自定义协议的应用程序无法启动,窗口也会失去焦点并触发 onblur。
  • 这对于 Chrome 来说是一个不错的解决方案,因为它没有任何类型的协议错误处理。这已与此处的其他浏览器检测结合使用:rajeshsegu.com/2012/09/browser-detect-custom-protocols/…
  • 协议检测最近一直是个头疼的问题。 ;) 上述方法似乎有点工作......使用在rajeshsegu.com/fun/code/browser/detect.html 找到的rajesh 的现场示例我正在“真正”进入Chrome。但是如果我重构使得没有 results() 函数(它又使用一个警告框——一个阻塞调用)并让它只返回一个布尔值,我就错了。我以某种方式感觉到阻塞警报也是以某种方式强制计时的一种骇人听闻的方式……任何见解,@scunliffe?
  • 对于那些寻找不再可用的 rajeshsegu 页面的人,我使用 archive.org 获取源代码并将其导入到 jsfiddle 中:jsfiddle.net/stefansundin/0uca4h2o
【解决方案2】:

没有很好的跨浏览器方式来做到这一点。在 Win8+ 上的 IE10+ 中,新的msLaunchUri api 使您能够启动协议,如下所示:

navigator.msLaunchUri('skype:123456', 
  function() 
  { 
    alert('success');
  }, 
  function()
  {
    alert('failed');
  } 
); 

如果未安装协议,将触发失败回调。否则,协议将启动并触发成功回调。

我在这里进一步讨论这个话题: https://web.archive.org/web/20180308105244/https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/

这个话题是最近(2021 年)感兴趣的;请参阅https://github.com/fingerprintjs/external-protocol-flooding 进行讨论。

【讨论】:

  • 它不起作用 - 在 Windows 7、IE 浏览器中测试
  • msLaunchUri 仅适用于 Windows 8+。
  • 链接失效了。
【解决方案3】:

HTML5 定义了Custom scheme and content handlers(据我所知,Firefox 是迄今为止唯一的implementor),但不幸的是,目前无法检查处理程序是否已经存在——它一直是proposed,但没有关注-向上。这似乎是有效使用自定义处理程序的关键特性,我们作为开发人员应该注意这个问题,以便实现它。

【讨论】:

    【解决方案4】:

    似乎没有直接的方法通过 javascript 来检测是否存在已注册协议处理程序的已安装应用程序。

    在 iTunes 模型中,Apple 为其服务器提供 url,然后服务器提供运行一些 javascript 的页面:

    http://ax.itunes.apple.com/detection/itmsCheck.js

    因此 iTunes 安装程序显然为主要浏览器部署了插件,然后可以检测到它们的存在。

    如果您的插件已安装,那么您可以合理地确定重定向到您的应用特定的 url 会成功。

    【讨论】:

    • 这应该是最可靠的解决方案。但我的意思是你需要为大多数浏览器安装并创建一个插件,这有点棘手。我可以更友好地把用户重定向到下载页面。
    • 为什么不使用这里提到的 FireBreath? stackoverflow.com/a/14758085/427793
    【解决方案5】:

    最简单的解决方案是在第一时间询问用户。

    每个示例使用 Javascript 确认对话框:

    You need this software to be able to read this link. Did you install it ?
    
    if yes: create a cookie to not ask next time; return false and the link applies
    if false: window.location.href = '/downloadpage/'
    

    【讨论】:

    • Cookie 可以每天清除干净。有没有更好的方法可以制作基于 Flash 的 cookie?
    • 如果 cookie 被删除,用户将再次收到提示。
    • 我认为vscode使用这种方法,查看他们的网站marketplace.visualstudio.com
    【解决方案6】:

    如果您可以控制您尝试运行的程序(代码),那么查看用户是否成功运行应用程序的一种方法是:

    1. 在尝试打开自定义协议之前,向服务器脚本发出 AJAX 请求,将用户的意图保存在数据库中(例如,保存用户 ID 和他想做什么)。

    2. 尝试打开程序,传递intent数据。

    3. 让程序向服务器发出请求以删除数据库条目(使用意图数据查找正确的行)。

    4. 让 javascript 轮询服务器一段时间以查看数据库条目是否消失。如果条目消失了,您将知道用户已成功打开应用程序,否则条目将保留(您可以稍后使用 cronjob 将其删除)。

    这个方法我没试过,只是想了一下。

    【讨论】:

      【解决方案7】:

      我终于获得了一个结合了this 和一个超级简单的 Safari 扩展的跨浏览器(Chrome 32、Firefox 27、IE 11、Safari 6)解决方案。在这个和这个other question 中以一种或另一种方式提到了这个解决方案的大部分内容。

      这是脚本:

      function launchCustomProtocol(elem, url, callback) {
          var iframe, myWindow, success = false;
      
          if (Browser.name === "Internet Explorer") {
              myWindow = window.open('', '', 'width=0,height=0');
              myWindow.document.write("<iframe src='" + url + "'></iframe>");
      
              setTimeout(function () {
                  try {
                      myWindow.location.href;
                      success = true;
                  } catch (ex) {
                      console.log(ex);
                  }
      
                  if (success) {
                      myWindow.setTimeout('window.close()', 100);
                  } else {
                      myWindow.close();
                  }
      
                  callback(success);
              }, 100);
          } else if (Browser.name === "Firefox") {
              try {
                  iframe = $("<iframe />");
                  iframe.css({"display": "none"});
                  iframe.appendTo("body");
                  iframe[0].contentWindow.location.href = url;
      
                  success = true;
              } catch (ex) {
                  success = false;
              }
      
              iframe.remove();
      
              callback(success);
          } else if (Browser.name === "Chrome") {
              elem.css({"outline": 0});
              elem.attr("tabindex", "1");
              elem.focus();
      
              elem.blur(function () {
                  success = true;
                  callback(true);  // true
              });
      
              location.href = url;
      
              setTimeout(function () {
                  elem.off('blur');
                  elem.removeAttr("tabindex");
      
                  if (!success) {
                      callback(false);  // false
                  }
              }, 1000);
          } else if (Browser.name === "Safari") {
              if (myappinstalledflag) {
                  location.href = url;
                  success = true;
              } else {
                  success = false;
              }
      
              callback(success);
          }
      }
      

      Safari 扩展很容易实现。它由一行注入脚本组成:

      myinject.js:

      window.postMessage("myappinstalled", window.location.origin);
      

      那么在网页JavaScript中,需要先注册消息事件,如果收到消息则设置一个标志:

      window.addEventListener('message', function (msg) {
          if (msg.data === "myappinstalled") {
              myappinstalledflag = true;
          }
      }, false);
      

      这假定与自定义协议关联的应用程序将管理 Safari 扩展程序的安装。

      在所有情况下,如果回调返回 false,您就知道通知用户应用程序(即自定义协议)未安装。

      【讨论】:

      • 它不适用于 IE。它应该如何工作?在该窗口内定义的myWindow.location.href; 和 iframe src 之间有什么联系?它应该抛出异常吗?与是否支持自定义协议无关。
      【解决方案8】:

      您说您需要检测浏览器的协议处理程序 - 真的吗?

      如果您做了类似从 sourceforge 下载文件时发生的事情会怎样?假设您要打开 myapp://something。与其简单地创建指向它的链接,不如创建指向通过 HTTP 访问的另一个 HTML 页面的链接。然后,在该页面上,假设您正在尝试为他们打开应用程序。如果它不起作用,他们需要安装您的应用程序,他们可以通过单击您提供的链接来完成。如果它确实有效,那么你就准备好了。

      【讨论】:

      • 建议这没有用是无益的。在我看来,能够有条件地显示启动或下载链接 - 甚至在单击链接时有条件地启动或下载将是一种更好的用户体验,而不是告诉用户他们需要先安装。
      【解决方案9】:

      这是微软支持推荐的 IE 方法

      http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx#related_topics

      “如果您对安装在用户机器上的二进制文件有一定的控制权,那么在脚本中检查 UA 似乎是一种相关的方法: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform " -- 由 M$ 支持

      每个网页都可以访问 userAgent 字符串,如果您删除自定义发布平台值,使用 navigator.userAgent 在 javascript 中检测这一点非常简单。

      幸运的是,其他主要浏览器,如 Firefox 和 Chrome(除了 Safari :( ),在单击带有自定义协议的链接并且未在用户计算机上安装该协议时不会抛出“找不到页面”错误。IE 是在这里非常无情,任何在不可见框架中单击或捕获 javascript 错误的技巧都不起作用,最终会出现丑陋的“网页无法显示”错误。我们在此案例中使用的技巧是用浏览器特定图像通知用户点击自定义协议链接将打开一个应用程序。如果他们没有找到打开的应用程序,他们可以单击“安装”页面。就 XD 而言,这个 wprks 比 IE 的 ActiveX 方法好得多。 对于 FF 和 Chrome,只需继续启动自定义协议,无需任何检测。让用户告诉你他看到了什么。 对于 Safari,:( 还没有答案

      【讨论】:

      【解决方案10】:

      你可以试试这样的:

      function OpenCustomLink(link) {
      
          var w = window.open(link, 'xyz', 'status=0,toolbar=0,menubar=0,height=0,width=0,top=-10,left=-10');
          if(w == null) {            
              //Work Fine
          }
          else {
              w.close();
              if (confirm('You Need a Custom Program. Do you want to install?')) {
                  window.location = 'SetupCustomProtocol.exe'; //URL for installer
              }
          }
      }
      

      【讨论】:

      • 不,在 Firefox 27 中不起作用。(未在其他浏览器中测试)
      • 不工作 - 在 Windows 7、IE、Firefox、Opera、Chrome 中测试
      【解决方案11】:

      我正在尝试做类似的事情,我刚刚发现了一个适用于 Firefox 的技巧。如果你将它与 IE 的技巧结合起来,你可以拥有一个在两种主要浏览器上都可以使用的工具(我不确定它是否适用于 Safari,我知道它在 Chrome 中不起作用)

      if (navigator.appName=="Microsoft Internet Explorer" && document.getElementById("testprotocollink").protocolLong=="Unknown Protocol") {
          alert("No handler registered");
      } else {
          try {
              window.location = "custom://stuff";
          } catch(err) {
              if (err.toString().search("NS_ERROR_UNKNOWN_PROTOCOL") != -1) {
                  alert("No handler registered");
              }
          }
      }
      

      为了使其正常工作,您还需要在页面某处有一个隐藏链接,如下所示:

      <a id="testprotocollink" href="custom://testprotocol" style="display: none;">testprotocollink</a>
      

      这有点hacky,但它有效。不幸的是,Firefox 版本仍然会在您尝试访问具有未知协议的链接时弹出默认警报,但它会在警报解除后运行您的代码。

      【讨论】:

      • 我发现我仍然收到“Firefox 不知道如何打开此地址,因为协议 (tel) 未与任何程序关联。”捕获块之前的消息
      • protocolLong 仅返回“已知”协议(file:,mailto:,gopher:,ftp:,http:,https:,news:)的结果,而不返回其他应用程序协议的结果。
      【解决方案12】:

      这不是一项简单的任务;一种选择可能是使用签名代码,您可以利用它来访问注册表和/或文件系统(请注意,这是一个very expensive 选项)。代码签名也没有统一的 API 或规范,因此您需要为每个目标浏览器生成特定的代码。支持的噩梦。

      另外,我知道Steam,游戏内容交付系统,似乎也没有解决这个问题。

      【讨论】:

      • 签名代码应该由与处理 custom:// 的应用程序一起安装的证书签名,因此不需要像威瑞信这样的人签名的非常昂贵的应用程序证书。
      【解决方案13】:

      这是另一个 hacky 答案,需要在启动时将您的应用程序(希望是轻量级)修改为“电话回家”。

      1. 用户单击链接,该链接尝试启动应用程序。一个特别的 标识符放在链接中,以便将其传递给 应用程序启动时。网络应用显示了一个微调器或类似的东西。
      2. 然后网页开始检查 来自具有相同唯一 ID 的应用的“应用电话回家”事件。
      3. 启动时,您的应用程序会向您的 Web 应用程序发送 HTTP 具有唯一标识符,以指示存在。
      4. 要么网页看到应用程序最终启动,要么继续显示“请下载”页面。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-25
        • 1970-01-01
        • 2015-05-02
        • 2017-10-10
        • 1970-01-01
        • 2014-09-06
        • 2016-12-01
        • 1970-01-01
        相关资源
        最近更新 更多