【问题标题】:Chrome native host in C++, cannot communicate with ChromeC++ 中的 Chrome 本地主机,无法与 Chrome 通信
【发布时间】:2014-10-27 07:10:06
【问题描述】:

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

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

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

有什么办法解决这个问题吗?

【问题讨论】:

    标签: c++ google-chrome-extension chrome-native-messaging


    【解决方案1】:

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

    Chrome 扩展

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

    以下是示例 ma​​nifest.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/"
      ]
    }
    

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

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

    为您的本地主机添加注册表项后,剩下的唯一事情就是编写您的本地主机。以下 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 个字节并计算消息的大小。我在另一篇文章中解释了如何做到这一点,可以在这里找到:

    How to calculate size of the message sent by chrome extension

    【讨论】:

    • 非常感谢您的回答,我之前尝试过这种方法并使用 html 页面来启动扩展。但是扩展无法接收到html事件,html页面是这样的: function startApp() { var evt = document.createEvent("CustomEvent"); evt.initCustomEvent('myEvent', true, false, "Hello world"); document.dispatchEvent(evt); }
    • 你能分享一个可以启动扩展的html文件样本吗?非常感谢。
    • 说实话,我从来没有使用过 HTML 来加载扩展。我是通过以下方式完成的:导航到 chrome://extensions(您可以在 URL 栏中键入),选择“加载解压扩展...”,然后选择扩展的文件夹(这是一个包含 background.js 的文件夹和 manifest.json 用于扩展)。而且我相信默认情况下该扩展程序处于禁用状态,因此您必须自己启用它。
    • 但是原生应用什么时候启动呢?或者只是在加载扩展时,会自动加载本机应用程序?
    • "当使用 runtime.connectNative 创建消息端口时,Chrome 会启动原生消息主机进程并保持运行,直到端口被销毁。另一方面,当使用 runtime.sendNativeMessage 发送消息时,在不创建消息传递端口的情况下,Chrome 会为每条消息启动一个新的本机消息传递主机进程。”因此,当扩展调用 connectNative 时,Native Messaging 主机会自动启动,只要端口打开,它就会打开。
    【解决方案2】:

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

    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;
    

    【讨论】:

    • 在 C++ 中使用原始缓冲区。呃。
    • inMsg[inLen] 溢出。
    猜你喜欢
    • 2014-05-31
    • 2018-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多