Chrome native messaging host with C# app in windows

由於工作需要使用到chrome擴充功能跟C#寫的app做資料傳輸,所以學了一下

以下開始來看,先看chrome extension的部分:

chrome的manifest.json:
{
  "name": "Native Messaging Windows",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Communicate with a native application.",
  "icons": {
    "16": "icons/icon_windows.png",
    "48": "icons/icon_windows.png",
    "128": "icons/icon_windows.png"
  },
  "app": {
    "launch": {
      "local_path": "main.html"
    }
  },
  "permissions": [
    "nativeMessaging"
  ]
}

main.html:
<!DOCTYPE html>
 
<html>
  <head>
    <script src='./main.js'></script>
  </head>
  <body>
    <button id='connect-button'>Connect</button>
    <input id='input-text' type='text' />
    <button id='send-message-button'>Send</button>
    <div id='response'></div>
  </body>
</html>

main.js:


var port = null;
var getKeys = function(obj){
   var keys = [];
   for(var key in obj){
      keys.push(key);
   }
   return keys;
}

function appendMessage(text) {
  document.getElementById('response').innerHTML += "<p>" + text + "</p>";
}

function updateUiState() {
  if (port) {
    document.getElementById('connect-button').style.display = 'none';
    document.getElementById('input-text').style.display = 'block';
    document.getElementById('send-message-button').style.display = 'block';
  } else {
    document.getElementById('connect-button').style.display = 'block';
    document.getElementById('input-text').style.display = 'none';
    document.getElementById('send-message-button').style.display = 'none';
  }
}

function sendNativeMessage() {
  message = {"text": document.getElementById('input-text').value};
  port.postMessage(message);
  appendMessage("Sent message: <b>" + JSON.stringify(message) + "</b>");
}

function onNativeMessage(message) {
  appendMessage("Received message: <b>" + JSON.stringify(message) + "</b>");
}

function onDisconnected() {
  appendMessage("Failed to connect: " + chrome.runtime.lastError.message);
  port = null;
  updateUiState();
}

function connect() {
  var hostName = "com.hulk.chrome.example";
  appendMessage("Connecting to native messaging host <b>" + hostName + "</b>")
  port = chrome.runtime.connectNative(hostName);
  appendMessage("connectNative done");
  port.onMessage.addListener(onNativeMessage);
  port.onDisconnect.addListener(onDisconnected);
  updateUiState();
  appendMessage("updateUiState done");
}

document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('connect-button').addEventListener(
      'click', connect);
  document.getElementById('send-message-button').addEventListener(
      'click', sendNativeMessage);
  updateUiState();
});

以上是chrome extension的部分

接下來看host app的部分
C# NativeMessageWindows code:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace NativeMessagingWindows
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //Console.WriteLine("Form1");
            JObject data;
            while ((data = Read()) != null)
            {
                var message = data["text"].Value<string>();
                this.label1.Text += "Received message form windows: " + message + "\n";
                var processed = ProcessMessage(data);
                Write(processed);
                if (processed == "exit")
                {
                    return;
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            JObject data;
            while ((data = Read()) != null)
            {
                var message = data["text"].Value<string>();
                this.label1.Text += "Received message form windows: " + message + "\n";
                var processed = ProcessMessage(data);
                Write(processed);
                if (processed == "exit")
                {
                    return;
                }
            }

            if (!string.IsNullOrEmpty(this.textBox1.Text))
            {
                string inputText = this.textBox1.Text;
                this.label1.Text += "Send message form windows: " + inputText + "\n";
                Write(inputText);
            }
        }

        public static string ProcessMessage(JObject data)
        {
            var message = data["text"].Value<string>();
            switch (message)
            {
                case "test":
                    return "testing!";
                case "exit":
                    return "exit";
                default:
                    return "echo: " + message;
            }
        }

        public static JObject Read()
        {
            var stdin = Console.OpenStandardInput();
            var length = 0;

            var lengthBytes = new byte[4];
            stdin.Read(lengthBytes, 0, 4);
            length = BitConverter.ToInt32(lengthBytes, 0);

            var buffer = new char[length];
            using (var reader = new StreamReader(stdin))
            {
                while (reader.Peek() >= 0)
                {
                    reader.Read(buffer, 0, buffer.Length);
                }
            }

            return (JObject)JsonConvert.DeserializeObject<JObject>(new string(buffer));
        }

        public static void Write(JToken data)
        {
            var json = new JObject();
            json["data"] = data;

            var bytes = System.Text.Encoding.UTF8.GetBytes(json.ToString(Formatting.None));

            var stdout = Console.OpenStandardOutput();
            stdout.WriteByte((byte)((bytes.Length >> 0) & 0xFF));
            stdout.WriteByte((byte)((bytes.Length >> 8) & 0xFF));
            stdout.WriteByte((byte)((bytes.Length >> 16) & 0xFF));
            stdout.WriteByte((byte)((bytes.Length >> 24) & 0xFF));
            stdout.Write(bytes, 0, bytes.Length);
            stdout.Flush();
        }

    }
}

在windows下必須要向系統註冊這個app
創建一個 com.hulk.chrome.example.json 檔案,內容如下:
{
  "name": "com.hulk.chrome.example",
  "description": "Hulk's Application",
  // "path": "C:\\Native\\NativeMessagingWindows.bat",
  "path": "C:\\NativeMessagingWindows.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://pdaadhknglcfmbocbpnknnkmglkohafo/"
  ]
}
json檔內的name要跟main.js的host name要一樣才找得到
path:位置的反斜線要兩個,windows的跳脫字元
chrome extension 號碼也要修改

完成這步以後我們需要在WIndows 登錄檔 中加入google 專案具體如下:

此處我在HKEY_Current_User及HKEY_LOCAL_MACHINE都有做設定
執行-> Regedit -> HKEY_Current_User->Software->Google->Chrome->NativeMessagingHosts->新建一個叫com.hulk.chrome.example的機碼,同時在這個項裡預設值設定為我們Native Messaging 的 位置 C:\Native\com.hulk.chrome.example.json

把執行檔放到該放的位置
把擴充功能加入,並且修改Chrome extension id

Chrome瀏覽器輸入
chrome-extension://pdaadhknglcfmbocbpnknnkmglkohafo/main.html
即可跟app做資料傳輸

不過app畫面不會執行起來,這個地方我還在研究,找不到原因
如果自己開啟app再開網頁,自己開起來的app windows沒有反應

狀況1:開啟網頁按connect,輸入一些字按send,然後按重新整理網頁,app windows才會跳出,可是也中斷連線了

這問題還在了解~~~

留言

這個網誌中的熱門文章

Visual Sudio2019創建MFC ActiveX工程製作IE OCX插件

Little Endian VS Big Endian

Converting Between Byte Arrays and Hexadecimal Strings in Java