您的位置:首页 > 其它

Tips on Chrome Native Messaging

2015-11-24 23:29 295 查看


Tips on Chrome Native Messaging

Jul 22, 2015  http://zodiacg.net/2015/07/chrome-nativemessaging-tips/

最近在写Bilidan-helper,整个插件的核心就是Chrome Extension API里的Native
Messaging。这套API比起NPAPI/PPAPI显然要易用的多,当然功能也简单的多。功能简单不要紧,但这套API做的也很不完善,如果各方面都没问题正常工作是可以的,出了问题的话就不好调试和判断了。
稍微记录一点使用Native Messaging的经验,文档里有的东西就尽量不说了。

###Host部分

范例代码中的Host部分使用Python写的,严格的说是Python2。不加改动的话是无法在Python3下运行的。主要问题出现在
sys.stdout
sys.stdin
上,需要把:
sys.stdout.write()
改为
sys.stdout.buffer.write()
sys.stdin.read()
同理改成
sys.stdin.buffer.read()


我最后使用的Host是直接从范例代码改的。范例代码中Windows的Host使用了一个bat来调用Python来执行脚本。但是如果要打包发布的话,让用户装个Python不太现实……所以可以使用cxfreeze打包成exe直接调用。
在这里我遇到的问题是我仍然需要bat来帮我修改PATH环境变量方便Host调用其它程序,千万记得要把bat文件改成跟host的exe不一样的名字。不然bat很容易就开始不停调用自身了……

运行于命令行界面的Host程序被Chrome调用的时候不会显示命令行窗口。Bilidan调用了mpv,mpv在命令行输出的状态信息也不会显示出来(虽然不会被Chrome当做Host的输出处理)。同样所有输出到命令行的内容(比如错误)也不会被你看到的。
而且直接输出到标准输出,没有添加消息长度的内容会导致Chrome读取到错误的消息长度而直接将Host关闭。

在OSX/Linux下如果从shell启动Chrome,那么Chrome的错误提示会直接输出到shell窗口中,你的Host程序的错误输出也会显示出来。Bilidan调用了mpv,mpv的状态信息也会显示出来。
所以如果是跨平台的应用非常建议使用OSX/Linux开发。Windows下可以使用Sawbuck和启动时为Chrome添加命令行参数来看到Chrome的错误日志,但是看不到Host抛出的错误。可以给Host加上输出日志文件的功能来解决调试问题。

Windows下Host的安装,注册表项只有Chrome一种,Chromium也使用Chrome的注册表项(数字极速之流没测试过)。而且对Host安装目录没有空格、汉字之类的要求,都是可以的。

对Host的Json做出了修改的话,不需要重启浏览器也可以生效。在浏览器运行中添加、删除Host也不需要重启。似乎浏览器会在每次调用Native Host的时候查找。

###插件/应用部分

如果没有特别需求尽量不要使用
sendMessageNative
函数。尤其是开发初段还没有完善插件和Host之间沟通问题的时候。因为
sendMessageNative
不需要创建Port,很难像使用
connectNative
后再
postMessage
一样根据调用函数后Port的状态来帮助确定问题所在。

使用
connectNative
sendMessageNative
连接Host如果不成功,Chrome自己的日志里会有相应记录,比如
Specified
native messaging host not found.
。在Windows下需要用Sawbuck查看,OSX/Linux可以从shell启动来查看。

Chrome新出的Event Page很实用,但是要求所有打开的port都关闭(不管是普通的port还是Native Messaging的port)才能自动卸载脚本。需要合理规划port的开启和关闭。

连接Host不成功是最烦人的错误。因为Chrome的安全限制,既无法让插件安装Host,也无法让Host向Chrome添加插件。
connectNative
不会返回失败的,即使没有安装对应的Host,也会照常返回一个port。所以如何正确的在插件中检测Native
Messaging Host是否安装成功呢?

我研究了老半天,发现Host不存在的情况下,
connectNative
返回的port会立刻断开。而对断开的port调用
postMessage
会返回错误:
Attempting
to use a disconnected port object
。所以可以利用这个来判断。最后的成品代码是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

var port=null;
var testflag=false;
function onMessage(message){
switch (message) {
case 'pong':
if(testflag){
testflag = false;
port.disconnect();
port = null;
}
console.log("Connection test success");
break;
default:
console.log("Unrecognized command" + message.command);
}
}

function connectionTest(){
testflag = true;
var host_name = "com.nativemessaging.host";
port = chrome.runtime.connectNative(host_name);
port.onMessage.addListener(onMessage);
port.postMessage("ping");  //注2
setTimeout(testSend,3000);  //注1
}

function testSend(){
if(testflag){
try{
port.postMessage("ping");
}catch(e){
console.log("Connection test fail");
}
}
testflag = false;
port = null;
}

connectionTest
函数就是用来测试连接的。
host_name
位置改为所要测试的Host,
onMessage
postMessage
也根据自己编写的Host的情况对应修改发送和返回的内容。
可以发现我把
try...catch
捕获
postMessage
异常的部分放到了3秒后执行(注1的位置)。因为Chrome有个很坑爹的问题……如果在
connectionTest
里,
connectNative
onMessage.addListener
之后直接就
postMessage
的话,是不会产生错误的!必须要等一点时间,虽然这个一点很短,短到如果你在console里手动输入,即使是快速的粘贴代码,也会产生错误。所以使用了
setTimeout
testSend
函数放到了3秒后执行,确保能产生错误。
而注2的部分,则是为了如果Host正常安装了的话,可以立刻得到成功的返回,而不用等到3秒后。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: