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?
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):
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
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;
Using inotify to monitor a directory for any new file created in the directory by adding a watch on the directory by
fd = inotify_init();
wd = inotify_add_watch(fd, "filename_with_path", IN_CLOSE_WRITE);
inotify_add_watch(fd, directory_name, IN_CLOSE_WRITE);
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
while(true) {
char buf[buf_len];
int no_of_events, count = 0;
no_of_events = read(fd, buf, buf_len);
while(count < no_of_events) {
struct inotify_event *event = (struct inotify_event *) &buf[count];
if (event->len) {
if (event->mask & IN_CLOSE_WRITE) {
if (!(event->mask & IN_ISDIR)) {
//It's here multiple times
}
}
}
count += event_size + event->len;
}
When I scp a file to the directory, this loops infinitely. What is the problem with this code ? It shows the same event name and event mask too. So , it shows that the event for the same, infinite times.
There are no break statements. If I find an event, I just print it and carry on waiting for another event on read(), which should be a blocking call. Instead, it starts looping infinitely. This means, read doesn't block it but returns the same value for one file infinitely.
This entire operation runs on a separate boost::thread.
EDIT:
Sorry all. The error I was getting was not because of the inotify but because of sqlite which was tricky to detect at first. I think I jumped the gun here. With further investigation, I did find that the inotify works perfectly well. But the error actually came from the sqlite command : ATTACH
That command was not a ready-only command as it was supposed to. It was writing some meta data to the file. So inotify gets notification again and again. Since they were happening so fast, it screwed up the application.I finally had to breakup the code to understand why.
Thanks everyone.
I don't see anything wrong with your code...I'm running basically the same thing and it's working fine. I'm wondering if there's a problem with the test, or some part of the code that's omitted. If you don't mind, let's see if we can remove any ambiguity.
Can you try this out (I know it's almost the same thing, but just humor me) and let me know the results of the exact test?
1) Put the following code into test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/inotify.h>
int main (int argc, char *argv[])
{
char target[FILENAME_MAX];
int result;
int fd;
int wd; /* watch descriptor */
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
strcpy (target, ".");
fd = inotify_init();
if (fd < 0) {
printf ("Error: %s\n", strerror(errno));
return 1;
}
wd = inotify_add_watch (fd, target, IN_CLOSE_WRITE);
if (wd < 0) {
printf ("Error: %s\n", strerror(errno));
return 1;
}
while (1) {
char buff[buf_len];
int no_of_events, count = 0;
no_of_events = read (fd, buff, buf_len);
while (count < no_of_events) {
struct inotify_event *event = (struct inotify_event *)&buff[count];
if (event->len){
if (event->mask & IN_CLOSE_WRITE)
if(!(event->mask & IN_ISDIR)){
printf("%s opened for writing was closed\n", target);
fflush(stdout);
}
}
count += event_size + event->len;
}
}
return 0;
}
2) Compile it with gcc:
gcc test.c
3) kick it off in one window:
./a.out
4) in a second window from the same directory try this:
echo "hi" > blah.txt
Let me know if that works correctly to show output every time the file is written to and does not loop as your code does. If so, there's something important your omiting from your code. If not, then there's some difference in the systems.
Sorry for putting this in the "answer" section, but too much for a comment.
My guess is that read is returning -1 and since you dont ever try to fix the error, you get another error on the next call to read which also returns -1.
First time poster long time reader.
I've been playing round with reading in data from a bluetooth GPS unit.
I can connect to it using hyperterm and see the data
The following log is from the hyperterm
$GPRMC,195307.109,A,5208.2241,N,00027.7689,W,000.0,345.8,310712,,,A*7E
$GPVTG,345.8,T,,M,000.0,N,000.0,K,A*07
$GPGGA,195308.109,5208.2242,N,00027.7688,W,1,04,2.1,58.9,M,47.3,M,,0000*7E
$GPGSA,A,3,19,03,11,22,,,,,,,,,5.5,2.1,5.0*3F
$GPRMC,195308.109,A,5208.2242,N,00027.7688,W,000.0,345.8,310712,,,A*73
$GPVTG,345.8,T,,M,000.0,N,000.0,K,A*07
$GPGGA,195309.109,5208.2243,N,00027.7688,W,1,04,2.1,58.9,M,47.3,M,,0000*7E
END LOG
The following log is from my C++ program
$GPGSV,3,3,12,14,20,105,16,28,18,323,,08,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š945.109,A,5208.2386,N,00027.7592,W,000.0,169.5,8,323,,08,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š310712,,,A*70
$GPVTG,169.5,T,,M,000.0,N,000.0,K,A*06
8,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š310712,,,A*70
$GPVTG,169.5,T,,M,000.0,N,000.0,K,A*06
8,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š$GPGGA,195946.109,5208.2386,N,00027.7592,W,1.0,K,A*06
8,07,288,,16,01,178,*7A
END LOG
THE PROBLEM
I've left the line feeds as they come, the C++ output has extra line feeds, not sure why?
The C++ log also has some funky chars...?
The Code
for (int n=0;n<100;n++) {
char INBUFFER[100];
cv::waitKey(1000);
bStatus = ReadFile(comport, // Handle
&INBUFFER, // Incoming data
100, // Number of bytes to read
&bytes_read, // Number of bytes read
NULL);
cout << "bStatus " << bStatus << endl;
if (bStatus != 0)
{
// error processing code goes here
}
LogFile << INBUFFER;
}
I'm using settings...
comSettings.BaudRate = 2400;
comSettings.StopBits = ONESTOPBIT;
comSettings.ByteSize = 8;
comSettings.Parity = NOPARITY;
comSettings.fParity = FALSE;
...which as far as I can tell are the same as the settings used by hyperterm.
Any hints on what I'm doing wrong?
cheers!
UPDATE
So after updating to use bytes_read and account for the extra LF at the end of NMEA data I now have...
if (bytes_read!=0) {
for (int i=0; i < bytes_read; i++) {
LogFile << INBUFFER[i];
}
}
Which appears to have fixed things!
$GPGGA,215057.026,5208.2189,N,00027.7349,W,1,04,6.8,244.6,M,47.3,M,,0000*41
$GPGSA,A,3,32,11,01,19,,,,,,,,,9.7,6.8,7.0*3D
$GPRMC,215057.026,A,5208.2189,N,00027.7349,W,002.0,208.7,310712,,,A*74
$GPVTG,208.7,T,,M,002.0,N,003.8,K,A*09
$GPGGA,215058.026,5208.2166,N,00027.7333,W,1,04,6.8,243.1,M,47.3,M,,0000*42
Thanks folks, your help was much appreciated.
You have a bytes_read var, but you don't do anything with it? Seems to me that you're dumping the entire INBUFFER to the file, no matter how many/few bytes are actually loaded into it?