Host nativo de Chrome en C++, no se puede comunicar con Chrome

Host nativo de Chrome en C++, no se puede comunicar con Chrome

No proporcionó mucha información sobre lo que realmente intentó, por lo que haré todo lo posible para explicar los pasos necesarios para implementar Chrome Extension, Native Messaging host y establecer comunicación entre ellos. (Examine el siguiente enlace para obtener más información sobre Chrome Native Messaging:Chrome Native Messaging How to.

EXTENSIÓN CROMADA

En primer lugar, debemos configurar la extensión de Chrome. Como esta será una extensión muy simple, solo necesitamos manifest.json (tenga en cuenta que este es el archivo de manifiesto de la extensión; el host nativo también tendrá su propio archivo de manifiesto) y la implementación de javascript de background.js.

El siguiente es un ejemplo de manifest.json archivo:

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

Las cosas importantes aquí son que la implementación se proporcionará en background.js, la versión mínima de Chrome admitida es 29 y HTTP y HTTPS son compatibles.

A continuación, background.js archivo tiene el siguiente contenido:

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

El código en sí se explica por sí mismo:tratamos de conectarnos al host nativo identificado por la clave com.dolby.native_messaging_host (llegaré a esto en un minuto). Luego, registramos un oyente para el evento onMessage (este evento se activa cuando el host nativo envía un mensaje a la extensión de Chrome). También registramos un oyente para el evento de desconexión (por ejemplo, cuando el host nativo muere, se activará este evento). Y finalmente, enviamos un mensaje usando el método postMessage.

HOST DE MENSAJERÍA NATIVO

Ahora, el host nativo también tiene su propio archivo manifest.json. El archivo manifest.json muy simple para host nativo es el siguiente:

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

Un par de cosas interesantes aquí:el nombre identifica la clave bajo la cual está registrado este host nativo. La ruta es la ruta completa al ejecutable del host nativo. El tipo de comunicación stdio significa que estamos usando una entrada/salida estándar para la comunicación (el único tipo admitido actualmente). Y, por último, allow_origins especifica qué extensiones pueden comunicarse con este host nativo, ¡así que debe averiguar cuál es la clave de su extensión! .

El siguiente paso es registrar este host de Native Messaging en el registro (para Windows) y especificar la ubicación de su archivo de manifiesto. Las siguientes capturas de pantalla explican cómo hacerlo para Windows (examine el enlace proporcionado para saber cómo hacerlo en OSX y Linux):

Después de agregar una entrada de registro para su host nativo, lo único que queda es escribir su host nativo. El siguiente código de C++ implementa un host nativo simple que lee mensajes de la entrada estándar y escribe la respuesta en la salida estándar (cuando envía el mensaje #STOP#, el host nativo sale):

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

Los mensajes enviados por extensión al host nativo se forman de manera que el primer byte almacena la cantidad de bytes en el mensaje. Entonces, lo primero que debe hacer el host nativo es leer los primeros 4 bytes y calcular el tamaño del mensaje. Expliqué cómo hacer esto en otra publicación que se puede encontrar aquí:

Cómo calcular el tamaño del mensaje enviado por la extensión de Chrome


Para los futuros usuarios de Google, así es como lo hago:

Estilo C

Lectura

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

Escribir

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

Estilo C++

Lectura

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;

Escribir

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;