87

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

http://

mailto://

custom://

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

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

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

感谢您可以分享的任何想法。

4

13 回答 13

37

这将是一种非常非常 老套的方法......但这会奏效吗?

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

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

于 2009-05-07T20:41:49.157 回答
18

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

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进行讨论。

于 2012-09-15T18:26:05.743 回答
17

HTML5 定义了自定义方案和内容处理程序(据我所知,Firefox 是目前唯一的实现者),但不幸的是,目前无法检查处理程序是否已经存在——它已经被提出,但没有后续行动。这似乎是有效使用自定义处理程序的关键特性,我们作为开发人员应该注意这个问题,以便实现它。

于 2011-04-25T18:25:06.180 回答
13

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

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

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

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

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

于 2009-11-06T15:56:14.830 回答
11

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

每个示例使用 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/'
于 2010-01-24T08:34:42.673 回答
6

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

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

  2. 尝试打开程序,并传递意图数据。

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

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

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

于 2012-04-26T07:47:53.257 回答
4

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

这是脚本:

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,您就知道通知用户应用程序(即,它的自定义协议)未安装。

于 2014-02-26T23:17:01.950 回答
2

你可以尝试这样的事情:

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
        }
    }
}
于 2010-05-10T20:30:59.047 回答
2

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

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

于 2009-09-28T06:12:09.603 回答
2

这是 Microsoft 支持为 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,:(还没有答案

于 2010-12-06T08:53:53.817 回答
1

我正在尝试做类似的事情,我刚刚发现了一个适用于 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 版本仍然会在您尝试访问具有未知协议的链接时弹出默认警报,但它会在警报解除后运行您的代码。

于 2010-01-29T20:20:50.267 回答
0

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

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

于 2009-05-08T02:00:50.217 回答
0

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

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