Chrome扩展Native Messaging:JS向C#传递自定义数据失败的问题求助
兄弟,你这问题的核心是完全搞错了Chrome Native Messaging的通信机制!你现在用NamedPipeServerStream的方式完全不符合它的规范,这就是你只拿到奇怪的扩展ID字符串、真实数据传不过去的根本原因。
先给你划重点:Chrome Native Messaging不是通过命名管道通信,而是通过标准输入(stdin)和标准输出(stdout) 和你的C#程序交互,而且消息必须遵循严格的格式:先传4字节无符号小端整数(表示后续JSON数据的字节长度),再跟UTF-8编码的JSON字符串。
接下来一步步帮你修正问题:
1. 先搞定Native Messaging的系统配置(必须!)
Chrome需要知道你的Native Host的存在,以及哪些扩展可以和它通信。以Windows系统为例,你需要完成以下配置:
步骤1:创建Native Host的JSON配置文件
命名为com.my_company.my_application.json,内容如下:
{ "name": "com.my_company.my_application", "description": "我的Native Messaging主机", "path": "C:\\你的C#程序路径\\NativeHost.exe", "type": "stdio", "allowed_origins": [ "chrome-extension://你的扩展ID/" ] }
- 把
path替换为你C#程序编译后exe文件的绝对路径 - 把
你的扩展ID替换为Chrome扩展的真实ID(在扩展管理页面开启开发者模式后就能看到)
步骤2:添加注册表项
打开注册表编辑器(regedit),导航到:HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application
(路径不存在就手动创建)
然后将默认值设置为你刚才创建的JSON配置文件的绝对路径。
2. 重写你的C#代码(遵循Native Messaging协议)
把你的C#代码改成读取标准输入的内容,按照协议解析消息:
using System; using System.IO; using System.Text; using System.Threading.Tasks; using System.Text.Json; namespace NativeMessagingHost { class Program { static async Task Main(string[] args) { try { // 1. 读取消息长度前缀(Native Messaging强制要求的4字节小端整数) var lengthBuffer = new byte[4]; await Console.OpenStandardInput().ReadAsync(lengthBuffer, 0, 4); // 转成无符号整数,注意小端字节序 if (!BitConverter.IsLittleEndian) Array.Reverse(lengthBuffer); uint messageLength = BitConverter.ToUInt32(lengthBuffer, 0); // 2. 读取对应长度的消息内容 var messageBuffer = new byte[messageLength]; await Console.OpenStandardInput().ReadAsync(messageBuffer, 0, (int)messageLength); string messageJson = Encoding.UTF8.GetString(messageBuffer); // 这里就能拿到你从JS发的dados对象了 Console.WriteLine($"收到的消息:{messageJson}"); // 可以转成对象处理 var dados = JsonSerializer.Deserialize<dynamic>(messageJson); Console.WriteLine($"Nome: {dados.nome}, Idade: {dados.idade}"); // 如果需要给JS返回响应,也要遵循同样的格式 var response = new { status = "success", message = "数据已收到" }; string responseJson = JsonSerializer.Serialize(response); byte[] responseBytes = Encoding.UTF8.GetBytes(responseJson); // 先写响应长度前缀 byte[] responseLength = BitConverter.GetBytes((uint)responseBytes.Length); if (!BitConverter.IsLittleEndian) Array.Reverse(responseLength); await Console.OpenStandardOutput().WriteAsync(responseLength, 0, 4); // 再写响应内容 await Console.OpenStandardOutput().WriteAsync(responseBytes, 0, responseBytes.Length); await Console.OpenStandardOutput().FlushAsync(); } catch (Exception ex) { // 调试日志不要输出到stdout(会破坏协议),可以写到文件 File.WriteAllText("native_host_error.log", ex.ToString()); } } } }
代码关键说明:
- 必须先读取4字节的长度前缀,这是Chrome Native Messaging的强制要求
- 不要用
Console.WriteLine直接输出内容到stdout,会破坏协议格式;调试信息可以写到本地文件或者用调试器查看 - 返回响应时也要遵循“长度前缀+JSON内容”的格式
3. 你的JS代码基本没问题,注意两个细节
你的JS代码逻辑是正确的,但要确认:
chrome.runtime.sendNativeMessage的第一个参数(com.my_company.my_application)和配置文件里的name完全一致- 你的扩展ID已经正确添加到配置文件的
allowed_origins中
再贴一遍你的JS代码(确认无误):
const dados = { nome: "João", idade: 30 }; chrome.runtime.sendNativeMessage( 'com.my_company.my_application', dados, function(response) { if (chrome.runtime.lastError) { console.error("Error communicating with the native host:", chrome.runtime.lastError); } else { console.log('Response received: ', response); } } );
4. 测试步骤
- 编译你的C#代码,生成exe文件
- 确认注册表、JSON配置文件的路径和扩展ID都正确
- 重新加载Chrome扩展(扩展管理页面点击“重新加载”)
- 触发你的JS代码(比如点击扩展按钮)
- 查看C#程序的调试日志或文件,应该能收到正确的
{"nome":"João","idade":30}数据
为什么之前只拿到chrome-extension://<>?
因为你用了命名管道,Chrome的Native Messaging机制根本不会连接你的管道,反而你的程序可能误把Chrome启动它时传递的默认信息当成了消息,才出现了那个奇怪的字符串。
备注:内容来源于stack exchange,提问作者Leandro Bruscato

