使用 aardio 编写浏览器本地应用 (Native Messaging) 扩展分为两个主要部分:
运行发布后的 EXE 自动安装消息服务端。在安装代码中配置允许调用此 EXE 的浏览器扩展 ID。
安装未打包扩展会动态生成扩展 ID(每次都会变化)。aardio 允许在参数中指定扩展目录并自动获取动态生成的扩展 ID。示例:
web.nativeMessaging.install(
allowed_origins = {"\crx\nativeMessagingTest"}
)
在浏览器扩展管理页面启用开发者模式,安装包含 manifest.json 文件的未打包扩展。
{
"manifest_version": 3,
"permissions": [
"nativeMessaging",
"https://*/*"
]
}
以下是 aardio 实现的 Native Messaging 服务端示例代码:
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio工程15";right=759;bottom=469)
winform.add(
edit={cls="edit";left=24;top=15;right=726;bottom=428;edge=1;multiline=1;z=1}
)
/*}}*/
//创建浏览器本地消息主机
import web.nativeMessaging;
var host = web.nativeMessaging();
//如果不是在浏览器中启动 host 返回 null
if( !host ){
//安装浏览器本地消息主机
import web.nativeMessaging.install;
//自动生成配置文件,并在注册表中写入此应用
var json = web.nativeMessaging.install(
//本地应用名,命名规则与变量命名规则类似,只能使用字母数字,可用圆点分隔名称
name = "com.my_company.my_application";
//这个是描述,其实没什么用
description = "My Application";
//允许他调用此消息主机的浏览器扩展 ID 数组
allowed_origins = {
"agnhjnpjidnjcppanhimaidodnbnhhbp";//可以指定扩展 ID
"\crx\nativeMessagingTest";//也可以指定解压的扩展目录路径,自动转换为扩展 ID
"\crx\nativeMessagingTest.pem";//也可以指定私钥文件,自动转换为扩展 ID
}
)
winform.edit.print("本地应用服务端已注册成功")
winform.edit.print(json);
}
else{
//在Native Messaging管道中启动,客户端已连交接触发此事件
host.onOpen = function(extension,parentWindow){
winform.edit.print("客户端已连接:",extension)
//不能使用 win.setParent()把chrome搞成父窗口,这样chrome会崩溃
win.setOwner(winform.hwnd,parentWindow);
}
//客户端关闭时触发此事件
host.onClose = function(){
winform.edit.print("客户端已断开,即将退出")
win.quitMessage();//必须及时退出
}
//客户端发了JSON对象过来,注意data是一个经过JSON解析得到的对象,不是JSON字符串
host.onMessage = function(data){
winform.edit.print("收到数据",data);
host.send("这是来自aardio的数据");
}
//遇到错误了
host.onError = function(err){
winform.edit.print(err);
}
//运行消息主机,这个函数只是启动监听线程,不会阻塞
host.run();
}
winform.show()
win.loopMessage();
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Native Messaging</title>
<style>
body {
min-width: 300px;
font-family: Arial, sans-serif;
margin: 20px;
}
#message-form {
display: flex;
flex-direction: column;
}
input, button {
margin: 10px 0;
}
button {
padding: 10px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
}
</style>
</head>
<body>
<h1>Native Messaging</h1>
<form id="message-form">
<input type="text" id="message-input" placeholder="输入消息">
<button type="button" id="send-button">发送消息</button>
</form>
<p id="msg"></p>
<script src="popup.js"></script>
</body>
</html>
// 这里的参数改为本地应用的标识名
var port = chrome.runtime.connectNative('com.my_company.my_application');
// 连接成功后的处理函数
function onConnected(port) {
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
document.getElementById('send-button').disabled = false;
}
// 本地应用消息处理函数
function onNativeMessage(msg) {
const msgElement = document.getElementById("msg");
if (msgElement) {
msgElement.innerText = "本地应用发过来的对象:" + JSON.stringify(msg);
}
}
// 断开连接处理函数
function onDisconnected() {
const msgElement = document.getElementById("msg");
if (msgElement) {
msgElement.innerText = "已断开连接";
}
document.getElementById('send-button').disabled = true;
}
// 发送消息函数
function sendNativeMessage(message) {
if (port) {
port.postMessage(message);
} else {
console.error("无法发送消息,端口未连接");
}
}
// 发送按钮点击事件处理
document.getElementById('send-button').addEventListener('click', () => {
const messageInput = document.getElementById('message-input');
const messageText = messageInput.value;
if (messageText) {
sendNativeMessage({ text: messageText });
messageInput.value = ''; // 清空输入框
} else {
alert('请输入消息');
}
});
// 错误处理
try {
onConnected(port);
} catch (error) {
console.error("连接本地应用时发生错误:", error);
}
{
"manifest_version": 3,
"name": "Native Messaging Test",
"description": "Native Messaging",
"version": "1.0",
"permissions": [
"nativeMessaging",
"https://*/*",
"http://*/*"
],
"action": {
"default_icon": "app.ico",
"default_popup": "popup.html"
}
}