原生消息传递

扩展程序和应用可以使用与其他消息传递 API 类似的 API 与原生应用交换消息。支持此功能的原生应用必须注册知道如何与扩展程序进行通信的原生消息传递主机。Chrome 会在单独的进程中启动主机,并使用标准输入和标准输出流与其进行通信。

原生消息传递主机

如需注册原生消息传递主机,应用必须安装用于定义原生消息传递主机配置的清单文件。以下是清单文件的示例:

{
  "name": "com.my_company.my_application",
  "description": "My Application",
  "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
  ]
}

原生消息传递主机清单文件必须是有效的 JSON,并且包含以下字段:

名称说明
name本地消息传递主机的名称。客户端将此字符串传递给 runtime.connectNativeruntime.sendNativeMessage。此名称只能包含小写字母数字字符、下划线和英文句点。名称不能以点开头或结尾,点后面也不能跟其他点。
description简短的应用说明。
path本地消息传递主机二进制文件的路径。在 Linux 和 OSX 上,该路径必须是绝对路径。在 Windows 上,它可以相对于清单文件所在的目录。主机进程会在启动时将当前目录设为包含主机二进制文件的目录。例如,如果此参数设置为 C:\Application\nm_host.exe,则该参数将以当前目录 C:\Application\ 启动。
type用于与原生消息传递主机通信的接口的类型。此参数目前只有一个可能的值:stdio。它表示 Chrome 应使用 stdinstdout 与主机通信。
allowed_origins应有权访问原生消息传递主机的扩展程序的列表。允许使用 chrome-extension://*/* 等通配符。

原生消息传递主机位置

清单文件的位置取决于平台。

Windows 上,清单文件可位于文件系统中的任何位置。应用安装程序必须创建注册表项 HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\_com.my_company.my_application_HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\_com.my_company.my_application_,并将该键的默认值设为清单文件的完整路径。例如,使用以下命令:

REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application" /ve /t REG_SZ /d "C:\path\to\nmh-manifest.json" /f

或使用以下 .reg 文件:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application]
@="C:\\path\\to\\nmh-manifest.json"

当 Chrome 查找本地消息传递主机时,会先查询 32 位注册表,然后查询 64 位注册表。

OS XLinux 上,原生消息传递主机清单文件的位置因浏览器(Google Chrome 或 Chromium)而异。系统级原生消息传递主机在固定位置查询,而用户级原生消息传递主机是在用户个人资料目录内名为 NativeMessagingHosts 的子目录中查找。

  • OS X(系统级)
    • Google Chrome:/Library/Google/Chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • Chromium:/Library/Application Support/Chromium/NativeMessagingHosts/_com.my_company.my_application_.json
  • OS X(特定于用户的默认路径)
    • Google Chrome:~/Library/Application Support/Google/Chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • Chromium:~/Library/Application Support/Chromium/NativeMessagingHosts/_com.my_company.my_application_.json
  • Linux(系统级)
    • Google Chrome:/etc/opt/chrome/native-messaging-hosts/_com.my_company.my_application_.json
    • Chromium:/etc/chromium/native-messaging-hosts/_com.my_company.my_application_.json
  • Linux(特定于用户的默认路径)
    • Google Chrome:~/.config/google-chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • Chromium:~/.config/chromium/NativeMessagingHosts/_com.my_company.my_application_.json

原生消息传递协议

Chrome 会在单独的进程中启动每个原生消息传递主机,并使用标准输入 (stdin) 和标准输出 (stdout) 与其通信。双向消息采用相同的格式:每条消息都使用 JSON 编码进行 UTF-8 编码序列化,并且前面以原生字节顺序为 32 位消息长度。来自原生消息传递主机的单条消息大小上限为 1 MB,这主要是为了保护 Chrome 免受原生应用行为异常的影响。发送到原生消息传递主机的消息大小上限为 4 GB。

原生消息传递主机的第一个参数是调用方的来源,通常为 chrome-extension://[ID of allowed extension]。这样一来,当原生消息传递主机清单allowed_origins 键中指定了多个扩展名时,原生消息传递主机就能识别消息的来源。警告:在 Windows 中的 Chrome 54 及更低版本中,源站会作为第二个参数(而不是第一个参数)传递。

使用 runtime.connectNative 创建消息传递端口时,Chrome 会启动原生消息传递主机进程并使其保持运行状态,直到端口销毁为止。另一方面,当使用 runtime.sendNativeMessage 发送消息时(未创建消息传递端口)时,Chrome 会为每条消息启动新的原生消息传递主机进程。在这种情况下,系统会将主机进程生成的第一条消息作为对原始请求的响应进行处理,即 Chrome 会将它传递给调用 runtime.sendNativeMessage 时指定的响应回调。在这种情况下,原生消息传递主机生成的所有其他消息都会被忽略。

在 Windows 上,系统还会向原生消息传递主机传递一个命令行参数,其中包含发起调用的 Chrome 原生窗口的句柄:--parent-window=<decimal handle value>。这样,原生消息传递主机就可以创建正确父级的原生界面窗口。请注意,如果调用上下文是后台脚本页面,则此值将为 0。

连接到原生应用

向原生应用收发消息与跨扩展程序消息传递非常相似。主要区别在于,使用的是 runtime.connectNative 而非 runtime.connect,且使用的是 runtime.sendNativeMessage 而非 runtime.sendMessage。 只有在应用的清单文件中声明“nativeMessaging”权限后,才能使用这些方法。

以下示例会创建一个连接到原生消息传递主机 com.my_company.my_applicationruntime.Port 对象,开始监听来自该端口的消息并发送一条传出消息:

var port = chrome.runtime.connectNative('com.my_company.my_application');
port.onMessage.addListener(function(msg) {
  console.log("Received" + msg);
});
port.onDisconnect.addListener(function() {
  console.log("Disconnected");
});
port.postMessage({ text: "Hello, my_application" });

runtime.sendNativeMessage 可用于在不创建端口的情况下向原生应用发送消息,例如:

chrome.runtime.sendNativeMessage('com.my_company.my_application',
  { text: "Hello" },
  function(response) {
    console.log("Received " + response);
  });

调试原生消息传递

当原生消息传递主机无法启动、写入 stderr 或违反通信协议时,输出将写入 Chrome 的错误日志。在 Linux 和 OS X 上,只需从命令行启动 Chrome 并在终端中查看其输出,即可轻松访问此日志。在 Windows 上,按照如何启用日志记录中的说明使用 --enable-logging

下面是一些错误以及有关如何解决这些问题的提示:

  • 未能启动原生消息传递主机。
    • 检查您是否有足够的权限来执行该文件。
  • 指定的原生消息传递主机名无效。
    • 检查名称中是否包含无效字符。只能使用小写的字母数字字符、下划线和英文句点。名称不能以点开头或结尾,点后面不能跟其他点。
  • 原生主机已退出。
    • 在 Chrome 读取消息之前,原生消息传递主机的管道就已断开。这很可能是从原生消息传递主机发起的。
  • 找不到指定的原生消息传递主机。
    • 该名称在扩展名和清单文件中是否拼写正确?
    • 清单是否存放在正确的目录中?名称是否正确?如需了解预期格式,请参阅原生消息传递主机位置
    • 清单文件的格式是否正确?特别是,JSON 语法是否正确?这些值是否与原生消息传递主机清单的定义匹配?
    • path 中指定的文件是否存在?在 Windows 上,路径可以是相对的,但在 OS X 和 Linux 上,路径必须是绝对的。
  • 原生消息传递主机主机名未注册。(仅适用于 Windows)
    • 在 Windows 注册表中找不到原生消息传递主机。使用 regedit 仔细检查密钥是否确实已创建,以及其格式是否符合原生消息传递主机位置中载述的格式。
  • 禁止访问指定的原生消息传递主机。
    • 扩展程序的来源是否列在 allowed_origins 中?
  • 与原生消息传递主机通信时出错。
    • 这是一个很常见的错误,表示原生消息传递主机中通信协议的实现不正确。
    • 确保 stdout 中的所有输出均遵循原生消息传递协议。如果您要输出某些数据以进行调试,请将数据写入 stderr
    • 请确保 32 位消息长度采用平台的原生整数格式(小端字节序/大端字节序)。
    • 消息长度不得超过 1024*1024。
    • 消息大小必须等于消息中的字节数。这可能与字符串的“长度”不同,因为字符可以由多个字节表示。
    • 仅限 Windows:确保该程序的 I/O 模式已设置为 O_BINARY。默认情况下,I/O 模式为 O_TEXT,这会损坏消息格式,因为换行符 (\n = 0A) 会替换为 Windows 样式的行尾 (\r\n = 0D 0A)。可以使用 __setmode 设置 I/O 模式。