10

我正在尝试使用runtime.connectNativeand实现 Chrome 扩展postMessage。我正在关注 Chrome 文档,下载了本机消息传递示例,并将本机应用程序更改为使用 C++。

但是,本机应用程序无法接收来自 Chrome 扩展程序的消息。

同时,当原生应用程序使用printf向chrome扩展写消息功能时,扩展无法接收,消息只是显示在控制台中。

任何想法如何解决这个问题?

4

2 回答 2

25

你没有提供很多关于你实际尝试的信息,所以我会尽力解释实现 Chrome 扩展、本地消息传递主机和在它们之间建立通信所需的步骤。(请查看以下链接以获取有关 Chrome 原生消息传递的更多信息:Chrome 原生消息传递如何

铬扩展

首先,我们需要设置 Chrome 扩展程序。由于这将是一个非常简单的扩展,我们只需要manifest.json文件(请注意这是扩展的清单文件 - 本地主机也将有自己的清单文件)和 background.js javascript 实现。

以下是示例manifest.json文件:

{
  "name": "Test extension",
  "description": "Native messaging test",
   "permissions": [
                    "nativeMessaging",
                    "tabs",
                    "activeTab",
                    "background",
                    "http://*/", "https://*/"
                    ],
  "background": {
    "scripts": ["background.js"]
  },
  "version": "1.0",
  "minimum_chrome_version": "29",
  "manifest_version": 2
}

这里重要的是,将在 background.js 中提供实现,支持的最低 Chrome 版本为 29,并且支持 HTTP 和 HTTPS。

接下来,background.js文件有以下内容:

var port = chrome.runtime.connectNative('com.dolby.native_messaging_host');

port.onMessage.addListener(function(msg) {
  console.log(msg.text);
});

port.onDisconnect.addListener(function() {
  console.log("Disconnected");
});

port.postMessage({"text":"This is message from Chrome extension"});

代码本身是不言自明的——我们尝试连接到由 com.dolby.native_messaging_host 键标识的本机主机(我稍后会谈到)。然后,我们为 onMessage 事件注册一个监听器(该事件在原生主机向 chrome 扩展发送消息时触发)。我们还为断开事件注册了一个侦听器(例如,当本地主机死亡时,将触发此事件)。最后,我们使用 postMessage 方法发送消息。

本机消息传递主机

现在,本地主机也有自己的 manifest.json 文件。非常简单的原生主机 manifest.json 文件如下:

{
  "name": "com.dolby.native_messaging_host",
  "description": "Native messaging host",
  "path": "C:\\Users\\dbajg\\Desktop\\Native-messaging-host\\Debug\\Native-messaging-host.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://bjgnpdfhbcpjdfjoplajcmbleickphpg/"
  ]
}

这里有一些有趣的事情: name 标识了该本地主机注册的密钥。路径是本机主机可执行文件的完整路径。通信类型 stdio 表示我们使用标准输入/输出进行通信(仅当前支持的类型)。最后,allowed_origins 指定哪些扩展可以与这个本地主机通信——所以你必须找出你的扩展的密钥是什么!.

下一步是在注册表中注册此 Native Messaging 主机(对于 Windows)并指定其清单文件的位置。以下屏幕截图解释了如何在 Windows 上执行此操作(查看提供的链接以了解如何在 OSX 和 Linux 中执行此操作):

本机消息传递主机的注册表项(仅限 Windows 操作系统)

在为您的本地主机添加注册表项之后,剩下的唯一事情就是编写您的本地主机。以下 C++ 代码实现了简单的本地主机,它从标准输入读取消息并将响应写入标准输出(当您发送 #STOP# 消息时,本地主机退出):

#include <iostream>
#include <string>

int main(){
    std::string oneLine = "";

    while (1){
        unsigned int length = 0;

        //read the first four bytes (=> Length)
        /*for (int i = 0; i < 4; i++)
        {
            int read_char = getchar();
            length += read_char * (int) pow(2.0, i*8);
            std::string s = std::to_string((long long)read_char) + "\n";
            fwrite(s.c_str(), sizeof(char), s.size(), f);
            fflush(f);
        }*/

        //Neat way!
        for (int i = 0; i < 4; i++)
        {
            unsigned int read_char = getchar();
            length = length | (read_char << i*8);
        }

        //read the json-message
        std::string msg = "";
        for (int i = 0; i < length; i++)
        {
            msg += getchar();
        }

        std::string message = "{\"text\":\"This is a response message\"}";
        // Collect the length of the message
        unsigned int len = message.length();

        // Now we can output our message
        if (msg == "{\"text\":\"#STOP#\"}"){
            message = "{\"text\":\"EXITING...\"}";
            len = message.length();

            std::cout   << char(len>>0)
                        << char(len>>8)
                        << char(len>>16)
                        << char(len>>24);

            std::cout << message;
            break;
        }
        
        // return stdin message
        len = length;
        std::cout   << char(len>>0)
                    << char(len>>8)
                    << char(len>>16)
                    << char(len>>24);

        std::cout << msg << std::flush;

        // return response message
        // std::cout    << char(len>>0)
        //          << char(len>>8)
        //          << char(len>>16)
        //          << char(len>>24);
        //  
        // std::cout << message << std::flush;
    }
    
    return 0;
}

通过扩展发送到本机主机的消息以第一个字节存储消息中字节数的方式形成。所以原生主机要做的第一件事就是读取前 4 个字节并计算消息的大小。我在另一篇文章中解释了如何做到这一点,可以在这里找到:

如何计算 chrome 扩展发送的消息的大小

于 2014-10-27T08:55:56.550 回答
6

对于未来的谷歌人,我是这样做的:

C风格

阅读

char bInLen[4];
read(0, bInLen, 4); // 0 is stdin
unsigned int inLen = *(unsigned int *)bInLen;
char *inMsg = (char *)malloc(inLen);
read(0, inMsg, inLen);
inMsg[inLen] = '\0';
...
free(inMsg);

写作

char *outMsg = "{\"text\":\"This is a response message\"}";
unsigned int outLen = strlen(outMsg);
char *bOutLen = (char *)&outLen;
write(1, bOutLen, 4); // 1 is stdout
write(1, outMsg, outLen);
fflush(stdout);

C++ 风格

阅读

char bInLen[4];
cin.read(bInLen, 4);
unsigned int inLen = *reinterpret_cast<unsigned int *>(bInLen);
char *inMsg = new char[inLen];
cin.read(inMsg, inLen);
string inStr(inMsg); // if you have managed types, use them!
delete[] inMsg;

写作

string outMsg = "{\"text\":\"This is a response message\"}";
unsigned int outLen = outMsg.length();
char *bOutLen = reinterpret_cast<char *>(&outLen);
cout.write(bOutLen, 4);
cout << outMsg << flush;
于 2016-01-28T11:31:31.310 回答