chrome-extensions-samples 实战:Native Messaging 原生消息通信完整指南(echo 主机安装与协议解析)
2026/9/21 19:20:25 网站建设 项目流程

chrome-extensions-samples 实战:Native Messaging 原生消息通信完整指南(echo 主机安装与协议解析)

【免费下载链接】chrome-extensions-samplesChrome Extensions Samples项目地址: https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples

本文基于chrome-extensions-samples仓库中的 Native Messaging 示例,完整讲解 Chrome 扩展/应用如何通过原生消息(Native Messaging)API 与本地原生程序通信:包括消息主机的清单文件结构、Windows 与 Mac/Linux 双平台安装与卸载流程、扩展端connectNative调用链,以及原生主机端基于标准输入输出实现的 4 字节长度前缀 + JSON 报文协议。读完本文,你将能独立复现并部署一个可双向收发的原生消息回显(echo)示例。

示例概览:扩展与原生程序如何"握手"

Native Messaging 允许 Web 扩展(Manifest V2 时代也支持 Chrome App)与用户机器上安装的原生可执行程序直接交换消息,适用于需要调用系统能力、本地硬件或已有桌面应用的场景。

本仓库的示例位于_archive/mv2/api/nativeMessaging/(其对应的 Manifest V3 版本位于 api-samples/nativeMessaging/),目录结构如下:

_archive/mv2/api/nativeMessaging/ ├── README.md # 示例说明与安装指引(本文依据的主体文档) ├── app/ # 扩展/应用端(Manifest V2 Chrome App) │ ├── manifest.json │ ├── main.html │ ├── main.js │ └── icon-128.png └── host/ # 原生消息主机(Native Messaging Host) ├── com.google.chrome.example.echo.json # Mac/Linux 主机清单 ├── com.google.chrome.example.echo-win.json # Windows 主机清单 ├── install_host.sh # Mac/Linux 安装脚本 ├── uninstall_host.sh # Mac/Linux 卸载脚本 ├── install_host.bat # Windows 安装脚本 ├── uninstall_host.bat # Windows 卸载脚本 ├── native-messaging-example-host # 主机本体(Python 3 实现) └── native-messaging-example-host.bat # Windows 启动包装脚本

整个示例的通信链路为:Chrome(扩展端)→ 原生消息主机(manifest 指向的可执行程序)→ 回显消息。主机名为com.google.chrome.example.echo,扩展端与主机端均围绕该名字建立绑定关系。

按 README.md 的说明,要让示例跑起来,必须先安装 host 目录下的原生消息主机,否则扩展端调用chrome.runtime.connectNative()会因找不到主机而连接失败。

原生消息主机的清单文件:注册的关键

Chrome 靠一份 JSON 清单(manifest)定位原生主机,这份清单必须被安装到操作系统指定的目录或注册表位置。本示例提供了两个平台版本:

Mac/Linux 版host/com.google.chrome.example.echo.json:

{ "name": "com.google.chrome.example.echo", "description": "Chrome Native Messaging API Example Host", "path": "HOST_PATH", "type": "stdio", "allowed_origins": [ "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/" ] }

Windows 版host/com.google.chrome.example.echo-win.json:

{ "name": "com.google.chrome.example.echo", "description": "Chrome Native Messaging API Example Host", "path": "native-messaging-example-host.bat", "type": "stdio", "allowed_origins": [ "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/" ] }

各字段含义如下:

字段作用本示例取值
name主机唯一标识名,扩展端connectNative(hostName)必须与之一致com.google.chrome.example.echo
description主机用途说明Chrome Native Messaging API Example Host
path主机可执行程序的绝对路径(Linux/Mac)或启动命令(Windows)Linux/Mac 为HOST_PATH占位符,由安装脚本替换;Windows 为native-messaging-example-host.bat
type通信通道类型,原生消息必须为stdiostdio
allowed_origins允许连接本主机的扩展 ID 白名单chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/

两点需要特别说明:

  • 扩展 ID 绑定allowed_origins中硬编码了扩展 IDknldjmfmopnpolahpmmgbagdohdnhkik。该 ID 由 app/manifest.json 顶部的key字段(注释中同样标注了该 ID)决定——也就是"固定 key 的扩展 ID 可预测"这一机制。如果你改动了key或使用未签名的临时加载方式,扩展 ID 会变化,必须同步更新清单中的allowed_origins,否则 Chrome 会拒绝建立连接。
  • Windows 路径差异:Windows 清单的path指向native-messaging-example-host.bat(一个调用 Python 解释器启动主机的批处理脚本),因为注册表安装方式写入的是脚本所在目录的相对名;而 Mac/Linux 清单的path在安装时会被替换成native-messaging-example-host的绝对路径。

安装主机:Windows 注册表方式

在 Windows 上,运行 host 目录下的 install_host.bat 即可完成当前用户的安装:

:: Change HKCU to HKLM if you want to install globally. :: %~dp0 is the directory containing this bat script and ends with a backslash. REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo" /ve /t REG_SZ /d "%~dp0com.google.chrome.example.echo-win.json" /f

脚本原理(与 README.md 描述一致):

  1. 在注册表HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo下创建键;
  2. 将该键的默认值(/ve)设置为指向host\com.google.chrome.example.echo-win.json的完整路径(通过%~dp0取脚本所在目录,末尾带反斜杠);
  3. Chrome 启动时读取该注册表位置,按清单中的path启动主机。

扩展说明:

  • 全局安装:如需为所有用户安装,把脚本中的HKCU改为HKLM即可(注册表路径变为HKLM\Software\Google\Chrome\NativeMessagingHosts\...)。注意 README 明确提示:机器上需要安装 Python,因为 Windows 主机是通过 native-messaging-example-host.bat 调用python来运行主机的:
@echo off python "%~dp0/native-messaging-example-host" %*
  • 卸载:运行 uninstall_host.bat,它会同时删除 HKCU 与 HKLM 两处注册表项:
REG DELETE "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo" /f REG DELETE "HKLM\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo" /f

安装主机:Mac 与 Linux 目录方式

在 Mac 和 Linux 上,直接运行 host/install_host.sh:

host/install_host.sh

脚本首先根据系统与运行权限计算目标目录,从源码可见完整的分支逻辑:

平台普通用户(仅当前用户)root/admin(全用户)
macOS$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts/Library/Google/Chrome/NativeMessagingHosts
Linux$HOME/.config/google-chrome/NativeMessagingHosts/etc/opt/chrome/native-messaging-hosts

随后脚本依次执行:

  1. mkdir -p "$TARGET_DIR"创建目录;
  2. cp "$DIR/$HOST_NAME.json" "$TARGET_DIR"拷贝主机清单;
  3. sed -i -e "s/HOST_PATH/$ESCAPED_HOST_PATH/"将清单中的HOST_PATH占位符替换为主机脚本的绝对路径;
  4. chmod o+r让所有用户可读清单(root 安装时还会对主机脚本执行chmod a+x赋予执行权限);
  5. 输出安装成功信息。

对应地,host/uninstall_host.sh 会删除目标目录下的com.google.chrome.example.echo.json完成卸载:

host/uninstall_host.sh

README 特别说明:默认只安装给运行脚本的用户;若以sudo host/install_host.sh执行,则会安装到系统级目录供所有用户使用。

扩展端:发起连接与收发消息

扩展端代码位于 app/main.js,核心是调用chrome.runtime.connectNative(hostName)建立长连接。关键逻辑如下:

var port = null; function connect() { var hostName = "com.google.chrome.example.echo"; appendMessage("Connecting to native messaging host <b>" + hostName + "</b>") port = chrome.runtime.connectNative(hostName); port.onMessage.addListener(onNativeMessage); port.onDisconnect.addListener(onDisconnected); updateUiState(); } function sendNativeMessage() { message = {"text": document.getElementById('input-text').value}; port.postMessage(message); appendMessage("Sent message: <b>" + JSON.stringify(message) + "</b>"); } function onNativeMessage(message) { appendMessage("Received message: <b>" + JSON.stringify(message) + "</b>"); } function onDisconnected() { appendMessage("Failed to connect: " + chrome.runtime.lastError.message); port = null; updateUiState(); }

调用链与要点:

  • 连接connectNative(hostName)返回一个Port对象,hostName必须与主机清单中的name完全一致(即com.google.chrome.example.echo);
  • 收消息:通过port.onMessage.addListener(onNativeMessage)监听原生主机发来的消息;
  • 发消息:通过port.postMessage(message)发送 JSON 对象(本示例发送{"text": "..."});
  • 断线处理port.onDisconnect触发时,通过chrome.runtime.lastError.message获取失败原因(典型如主机未安装、扩展 ID 不匹配),并将port置空以复位 UI 状态;
  • UI 状态机updateUiState()根据port是否存在切换"Connect / Send"按钮与输入框的显隐,避免在未连接时发送消息。

界面(app/main.html)提供了 Connect 按钮、文本输入框、Send 按钮以及用于回显收发记录的#response区域。

扩展本身需要声明nativeMessaging权限(见 app/manifest.json):

{ "name": "Native Messaging Example", "version": "1.0", "manifest_version": 2, "description": "Send a message to a native application.", "app": { "launch": { "local_path": "main.html" } }, "permissions": ["nativeMessaging"] }

注意这是一个 Manifest V2 时代的 Chrome App(使用app.launch.local_path启动入口)。在 Manifest V3 扩展中,权限声明位置不变(permissions: ["nativeMessaging"]),而连接 API 同样为chrome.runtime.connectNative——仓库中 api-samples/nativeMessaging/extension/ 即提供了 MV3 版本的对应实现,可对照阅读。

原生主机端:stdio 通道与 4 字节长度前缀协议

原生主机本体是 Python 3 脚本 host/native-messaging-example-host,它以type: "stdio"与 Chrome 通信:通过标准输入读取扩展发来的消息,通过标准输出写回响应

通信协议(Native Messaging 的标准报文格式)在源码中有完整实现:

发送消息(向扩展)

def send_message(message): # Write message size. sys.stdout.buffer.write(struct.pack('I', len(message))) # Write the message itself. sys.stdout.write(message) sys.stdout.flush()

接收消息(来自扩展)

def read_thread_func(queue): while 1: # Read the message length (first 4 bytes). text_length_bytes = sys.stdin.buffer.read(4) if len(text_length_bytes) == 0: ... sys.exit(0) # Unpack message length as 4 byte integer. text_length = struct.unpack('@I', text_length_bytes)[0] # Read the text (JSON object) of the message. text = sys.stdin.buffer.read(text_length).decode('utf-8') ...

即每条消息的编码为:前 4 字节(原生小端@I)是消息体字节长度,其后紧跟 UTF-8 编码的 JSON 文本。收发双方都必须遵循这一帧格式,才能保证 stdin/stdout 通道不会错位。

其余实现要点:

  • Windows 二进制模式:脚本开头针对win32平台调用msvcrt.setmode将 stdin/stdout 切换为O_BINARY,避免 Windows 文本模式对流内容的改写破坏二进制帧;
  • 回显与退出:收到{"text":"exit"}时退出循环;在无图形界面(headless)模式下,主机直接把收到的 JSON 包装成{"echo": <原消息>}返回;
  • Tkinter 图形界面:若系统装有 Tkinter,主机启动一个NativeMessagingWindow(文本框 + 输入框 + Send 按钮),收到的消息会显示在窗口中,也可手动输入消息回发给扩展——这正是示例名 "echo" 的交互体验:双向消息都会在扩展端 main.html 的响应区与主机窗口中同步可见;
  • 线程模型:主线程运行 Tkinter 事件循环,另起守护线程read_thread_func持续读取 stdin,通过queue把消息投递给 UI 线程处理,避免阻塞消息读取。

运行与验证步骤

结合 README.md 与源码,完整运行流程如下:

  1. 安装原生主机(二选一):
    • Windows:在 host 目录运行install_host.bat(需安装 Python);
    • Mac/Linux:运行host/install_host.sh(全局安装可加sudo);
  2. 在 Chrome 中加载扩展/应用目录_archive/mv2/api/nativeMessaging/app/chrome://extensions开启开发者模式后"加载已解压的扩展程序");MV3 版本则加载 api-samples/nativeMessaging/extension/;
  3. 打开应用主界面,点击Connect——若主机安装成功,扩展会建立到com.google.chrome.example.echo的连接;
  4. 在输入框键入文本并点击Send,扩展端界面与原生主机窗口(Tkinter 可用时)都会显示收到的消息,实现双向回显;
  5. 需要清理时,分别运行uninstall_host.bathost/uninstall_host.sh卸载主机。

若连接失败,优先排查三个位置:allowed_origins中的扩展 ID 是否与当前加载扩展一致、清单path指向的程序是否存在且可执行、Windows 注册表/HKCU 或系统目录中的清单是否就位。

小结

这个 echo 示例完整覆盖了 Native Messaging 的全链路要素:主机清单的name/path/type/allowed_origins配置、Windows 注册表与 Mac/Linux 目录两种注册机制、扩展端connectNative的收发与断线处理,以及主机端基于 4 字节长度前缀 + JSON 的 stdio 帧协议。对照仓库中 app/ 与 host/ 的源码,可以清晰理解 Chrome 与本地程序之间每一次消息往返的底层路径——这也是开发真实原生消息主机(如调用本地 CLI、系统 API 或专用硬件)时可直接复用的最小可运行模板。

【免费下载链接】chrome-extensions-samplesChrome Extensions Samples项目地址: https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询