01 chrome Native messaging
Native messaging 扩展解决chrome与本地程序通信的问题,可以解决从浏览器启动二进制文件的问题。
是通过扩展为browser进程发送信息,有browser进程启动二进制文件或者和指定的二进制文件通信。
下图参考自 Chrome Native Messaging技术示例
下面是chrome官方提供的扩展demo。
https://github.com/GoogleChrome/chrome-extensions-samples
02 具体操作步骤
02.01 保存demo代码
把 代码下载到 google的临时目录下,在 %localappdata%\Google\Chrome\User Data\Default\
下创建一个Extensions2目录,将 nativeMessaging
放入。效果如下:
02.02 加载插件
[更多工具] --> [扩展程序] --> [开发者模式] --> [加载已解压的扩展程序] --> [选择 nativeMessaging\app 路径] --> [加载]
加载成功效果
02.03 注册host 信息
运行 nativeMessaging\host\install_host.bat
查看注册表信息,
实际是注册了如下注册表信息
REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo" /ve /t REG_SZ /d "%~dp0com.google.chrome.example.echo-win.json" /f
运行的内容是 native-messaging-example-host.bat
这里如果在该bat文件中加入需要运行的exe,既可以执行对应的exe,或者直接把bat换成想要运行的其他可执行程序。
@echo off
:: Copyright (c) 2013 The Chromium Authors. All rights reserved.
:: Use of this source code is governed by a BSD-style license that can be
:: found in the LICENSE file.
python "%~dp0/native-messaging-example-host" %*
而真正运行的是 python脚本 native-messaging-example-host
,所以电脑上应该配置好python运行环境。
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# A simple native messaging host. Shows a Tkinter dialog with incoming messages
# that also allows to send message back to the webapp.
import struct
import sys
import threading
import Queue
try:
import Tkinter
import tkMessageBox
except ImportError:
Tkinter = None
# On Windows, the default I/O mode is O_TEXT. Set this to O_BINARY
# to avoid unwanted modifications of the input/output streams.
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
# Helper function that sends a message to the webapp.
def send_message(message):
# Write message size.
sys.stdout.write(struct.pack('I', len(message)))
# Write the message itself.
sys.stdout.write(message)
sys.stdout.flush()
# Thread that reads messages from the webapp.
def read_thread_func(queue):
message_number = 0
while 1:
# Read the message length (first 4 bytes).
text_length_bytes = sys.stdin.read(4)
if len(text_length_bytes) == 0:
if queue:
queue.put(None)
sys.exit(0)
# Unpack message length as 4 byte integer.
text_length = struct.unpack('i', text_length_bytes)[0]
# Read the text (JSON object) of the message.
text = sys.stdin.read(text_length).decode('utf-8')
if queue:
queue.put(text)
else:
# In headless mode just send an echo message back.
send_message('{"echo": %s}' % text)
if Tkinter:
class NativeMessagingWindow(Tkinter.Frame):
def __init__(self, queue):
self.queue = queue
Tkinter.Frame.__init__(self)
self.pack()
self.text = Tkinter.Text(self)
self.text.grid(row=0, column=0, padx=10, pady=10, columnspan=2)
self.text.config(state=Tkinter.DISABLED, height=10, width=40)
self.messageContent = Tkinter.StringVar()
self.sendEntry = Tkinter.Entry(self, textvariable=self.messageContent)
self.sendEntry.grid(row=1, column=0, padx=10, pady=10)
self.sendButton = Tkinter.Button(self, text="Send", command=self.onSend)
self.sendButton.grid(row=1, column=1, padx=10, pady=10)
self.after(100, self.processMessages)
def processMessages(self):
while not self.queue.empty():
message = self.queue.get_nowait()
if message == None:
self.quit()
return
self.log("Received %s" % message)
self.after(100, self.processMessages)
def onSend(self):
text = '{"text": "' + self.messageContent.get() + '"}'
self.log('Sending %s' % text)
try:
send_message(text)
except IOError:
tkMessageBox.showinfo('Native Messaging Example',
'Failed to send message.')
sys.exit(1)
def log(self, message):
self.text.config(state=Tkinter.NORMAL)
self.text.insert(Tkinter.END, message + "\n")
self.text.config(state=Tkinter.DISABLED)
def Main():
if not Tkinter:
send_message('"Tkinter python module wasn\'t found. Running in headless ' +
'mode. Please consider installing Tkinter."')
read_thread_func(None)
sys.exit(0)
queue = Queue.Queue()
main_window = NativeMessagingWindow(queue)
main_window.master.title('Native Messaging Example')
thread = threading.Thread(target=read_thread_func, args=(queue,))
thread.daemon = True
thread.start()
main_window.mainloop()
sys.exit(0)
if __name__ == '__main__':
Main()
03 运行效果
这个扩展demo,可以直接使用命令行运行。chrome://apps
查找到插件,点击运行,效果如下:
或者直接地址栏输入 chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/main.html
点击页面的 Connect
按钮,效果如下:
输入一些文字后,点击 Send 按钮的效果
04 Chromium的相关修改
如果是Chromium二次开发的浏览器,只需要加入自己对应的注册表键值即可
05 一些问题
这种方式启动的exe或者脚本,需要另外进行进程管理,chrome关闭时,并不能关闭启动的第三方进程。
如果要使用和chrome一样的进程名,可能需要一些周折。
如果是自己编译的进程好,可以考虑修改utility 及 subtype类型,来模糊第三方进程名称,但是,它依然不是chrome自己的进程,仍然需要自己管理这个特殊的进程。
06 参考
Chrome浏览器扩展开发系列之十四:本地消息机制Native messaging
Google chrome浏览器中通过扩展调用本地应用程序以及和程序相互通讯(C++)
07 使用c++ demo
把python程序换成c++ 代码,效果相同
// chrome_native_messaging_host.exe
// chromeHost.cpp
#include <iostream>
#include <string>
#include <fstream>
#include <Windows.h>
using namespace std;
void func()
{
string message = "{\"text\": \"This is a response message\",\"num\": \"three\"}";
unsigned int len = message.length();
//先输出长度
cout << char(((len >> 0) & 0xFF))
<< char(((len >> 8) & 0xFF))
<< char(((len >> 16) & 0xFF))
<< char(((len >> 24) & 0xFF));
//再输出内容并结束
cout << message;
cout.flush();
}
int main(int argc, char* argv[]) {
string input = "";
while (1)
{
Sleep(5 * 1000);
func();
}
getline(cin, input);
cout << "You entered: " << input << endl;
ofstream myfile;
myfile.open("example1.txt");
myfile << "Writing this to a file.\n";
myfile << input;
myfile.close();
system("pause");
return 0;
}
修改 com.google.chrome.example.echo-win.json
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
"name": "com.google.chrome.example.echo",
"description": "Chrome Native Messaging API Example Host",
//"path": "native-messaging-example-host.bat",
"path": "chrome_native_messaging_host.exe",
"type": "stdio",
"allowed_origins": [
"chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
]
}