Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome native host in C++, cannot communicate with Chrome

I am trying to implement a Chrome extension using runtime.connectNative and postMessage. I am following the Chrome documentation, downloaded the native messaging example, and changed the native app to using C++.

However, the native app cannot receive the message from the Chrome extension.

Meanwhile, when the native app using the printf function write message to chrome extension, the extension can not receive and the message just shown in the console.

Any ideas how to solve the problem?

like image 276
katherine Avatar asked Oct 27 '14 07:10

katherine


2 Answers

You didn't provide a lot information about what you actually tried, so I will do my best to explain the steps needed to implement Chrome Extension, Native Messaging host and establish communication between them. (Please examine the following link to obtain more information about Chrome Native Messaging: Chrome Native Messaging How to.

CHROME EXTENSION

Firstly, we need to set up Chrome extension. As this will be very simple extension, we need only manifest.json file (please note this is extension's manifest file - native host will have its own manifest file as well) and background.js javascript implementation.

The following is sample manifest.json file:

{
  "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
}

Important things here are that implementation will be provided in background.js, minimum supported Chrome version is 29 and HTTP and HTTPS are both supported.

Next, background.js file has the following content:

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"});

The code itself is pretty self-explanatory - we try to connect to native host identified by com.dolby.native_messaging_host key (I will come to this in a minute). Then, we register a listener for onMessage event (this event is triggered when native host sends a message to the chrome extension). We also register a listener for disconnect event (for example when native host dies this event will be triggered). And finally, we send a message using postMessage method.

NATIVE MESSAGING HOST

Now, native host also has its own manifest.json file. Very simple manifest.json file for native host is as follows:

{
  "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/"
  ]
}

Couple of interesting things here: name identifies the key under which this native host is registered. Path is full path to native host executable. Type of communication stdio means we are using standard input/output for communication (only type currently supported). And finally, allowed_origins specify which extensions can communicate with this native host - so you have to find out what is your extension's key!.

The next step is to register this Native Messaging host in registry (for Windows) and specify the location to its manifest file. The following screenshots explains how to this for Windows (examine provided link to find out how to do this in OSX and Linux):

Registry entry for native messaging host (Windows OS only)

After you've added registry entry for your native host the only remaining thing is to write your native host. The following C++ code implements simple native host that reads messages from the standard input and writes response to standard output (when you send #STOP# message the native host exits):

#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;
}

Messages sent by extension to native host is formed in a way that first byte stores the number of bytes in the message. So the first thing native host must do is to read the first 4 bytes and calculate the size of the message. I explained how to do this in another post that can be found here:

How to calculate size of the message sent by chrome extension

like image 83
dbajgoric Avatar answered Oct 25 '22 11:10

dbajgoric


For future Google people, here's how I do it:

C style

Reading

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);

Writing

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++ style

Reading

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;

Writing

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;
like image 28
user1112789 Avatar answered Oct 25 '22 11:10

user1112789