如何在前端调用电脑系统里的东西?

由于浏览器安全限制,前端是无法调用电脑里已安装的程序的(例如启动某个程序)。如何在用户知情同意的前提下来突破这个限制呢?

有一种办法:做一个桌面客户端,通过前端来进行调用。

方案一:本地Web Server

参见在Web开发中,如果无法回避ActiveX控件——一种让用户在非IE浏览器调用控件的思路

按照这个方案,前端不仅可以调用Active X控件,还可以做其他事情——只要是Windows应用程序能做的,前端都能通过HTTP API来间接做到。

如果需要双向通信,可以对Windows程序做一些改进,将其变成WebSocket服务器、WebRTC服务器。

方案二:自定义协议

如果只需要单方面发指令,那么不必开发复杂的HTTP Server,注册一个自定义协议,然后前端就可以一键启动,像百度网盘、打开迅雷那样。Windows程序端只要对参数进行检查和处理即可。

协议配置

在Windows系统,通过修改注册表实现。假如协议为hello://,那么需要进行以下配置:

  • HKEY_CLASSES_ROOT
    • hello,在该层级中建立一个值,Url Protocol,类型REG_SZ,内容为空
      • shell
        • open
          • command,默认值为启动指令,例如C:\xxx\xxx.exe --start "%1"

在C#配置自安装时可以这样写:

var key = Registry.ClassesRoot.OpenSubKey("hello", true);
if (key == null)
{
    key = Registry.ClassesRoot.CreateSubKey("hello");
}
key.SetValue("Url Protocol", "", RegistryValueKind.String);
var shell = key.CreateSubKey("shell");
var open = shell.CreateSubKey("open");
var command = open.CreateSubKey("command");
var path = Application.ExecutablePath;
command.SetValue("", "\"" + path + "\" --start \"%1\"", RegistryValueKind.String);

解析指令

当前端调用hello://aaa?bbb=ccc时,程序收到的参数则是--start "hello://aaa?bbb=ccc",在Main函数则需要对前端传过来的命令行参数进行解析和处理。

以C#的System.CommandLine库为例(需要安装NuGet程序包,并在安装之前勾选“包括预发行版”):

static async Task<int> Main(string[] args) {
    // ......

    var startOption = new Option<string>(
        name: "--start", 
        description: "启动应用", 
        getDefaultValue: ()=>""
    );

    var rootCommand = new RootCommand("")
    {
        startOption
    };

    rootCommand.SetHandler((url) =>
    {
        // 在这里处理url,例如
        if (url == "")
        {
            Application.Run(new MainForm());
        }
        else
        {
            var uri = new Uri(url);
            var path = uri.GetLeftPart(UriPartial.Path).Replace(uri.Scheme + "://", "");
            var query = HttpUtility.ParseQueryString(uri.Query);
            // path为路径
            // query为URL问号右面的参数,通过query.Get("xxx")获取
        }
    }, startOption);

    return await rootCommand.InvokeAsync(args);

}

前端启动

直接设置一个链接hello://xxxxxx?xxxx=xxx即可。

注意URL不能无限长。IE长度不能超过2083,Chrome不能超过8182,Firefox不超过65536。

如何检测是否安装了辅助程序

自定义协议没有回执,如果未启动成功,需要设法提醒用户安装客户端。如何判断自定义协议执行成功了呢?有一种办法是判断window是否失去了焦点,因为执行自定义协议会启动本机程序,会导致浏览器失去焦点,所以可以使用这种“黑科技”来判断。

有人已对启动和检测是否启动成功做了封装,详细代码参见ismailhabib/custom-protocol-detection

方案比较

方案 优点 缺点
方案一:本地Web Server 可进行复杂、双向交互 实现复杂,需专门处理HTTPS问题
方案二:自定义协议 实现简单,不需要考虑HTTPS 前端只能单向发指令,没有回执,无法获取信息