草庐IT

chrome插件与本地exe程序之间的信息交互

HHHzy0903 2023-05-20 原文

 

一、概述

        如果想要通过本地exe程序与chrome插件之间的通信,我们需要利用到NativeMessage。

        具体环境与实现步骤见下文。

二、环境

        1. window10操作系统

        2. chrome浏览器

        3. pycharm

三、实现步骤

        1. 自己编写一个chrome插件

                chrome插件的开发详细步骤与文件结构可参考Chrome扩展开发之一——Chrome扩展的文件结构 - 冰糖雪梨不加水 - 博客园 (cnblogs.com)https://www.cnblogs.com/ligerleng/p/gmail_assist_1.html                这里我们需要用到background、content_scripts与manifest.json文件,并使用Test.html进行测试。文件代码如下。

(1)manifest.json中的代码

{ 
   "name" : "FastRun",  //插件名字
    "version" : "1.0.1",  //插件版本号
    "description" : "Launch APP ",   //对插件的描述

    "background" : {    //background的具体引用
     "scripts": ["background.js"] //这里引用js文件background.js
     }, 

    "content_scripts": [  //content_scripts的具体引用
    { 
       //mathes指明哪些网站可以使用content.js 
      "matches": [ "http://*/*", "https://*/*", "ftp://*/*", "file://*/*" ], 
      "js": ["content.js"]  //这里引用js文件content.js
    }  
    ],  

    //permissons是插件的权限,如果要使用nativeMessage就必须在其中加入nativeMessaging
    "permissions" : ["nativeMessaging", "tabs"],  
    "minimum_chrome_version" : "6.0.0.0",  
    "manifest_version": 2  
}

  (2) content.js中的代码

var launch_message;
for (let i = 1; i <= 4; i++) {
//监听,只有监听到来自页面的"myCustomEvent1""myCustomEvent2""myCustomEvent3"...事件才触发
  document.addEventListener('myCustomEvent' + i, function (evt) {
    //向background.js发送信息
    chrome.runtime.sendMessage({
      type: "用户点击了按钮",
      message: evt.detail
    }, function (response) {});
  }, false);
}
//接收来自background.js的消息
chrome.runtime.onMessage.addListener(
  function (request, sender, sendResponse) {
    const message = document.querySelector('.message'); //获取message
    const newH3 = document.createElement('h3'); //创建一个h3标签
    newH3.innerHTML = request; //h3标签的内容为background.js发的消息
    message.appendChild(newH3) //把创建的h3标签添加到message中
    return true;
  });

(3)background.js中的代码

var port = null;
//监听来自content.js的信息
chrome.runtime.onMessage.addListener(
  function (request, sender, sendResponse) {
    //request就是content.js发送过来的消息
    if (request.type == "用户点击了按钮") {
      connectToNativeHost(request.message);
    }
    return true;
  });


//断开连接时触发
function onDisconnected() {
  console.log(chrome.runtime.lastError);
  port = null;
}

//接收到来自exe程序的消息时触发
async function onNativeMessage(message) {
  console.log('接收到从本地应用程序发送来的消息:' + JSON.stringify(message));
  const tabId = await getCurrentTabId()
  chrome.tabs.sendMessage(tabId, JSON.stringify(message), function (response) {});
}

//得到当前tabId
function getCurrentTabId() {
  return new Promise((resolve, reject) => {
    chrome.tabs.query({
      active: true,
      currentWindow: true
    }, function (tabs) {
      resolve(tabs.length ? tabs[0].id : null)
    });
  })
}

//连接到本地主机并获得通信端口
function connectToNativeHost(msg) {
  var nativeHostName = "com.my_company.my_application"; //本地主机名(就是在注册表中的名字)
  port = chrome.runtime.connectNative(nativeHostName); //根据本地主机名得到通信端口
  port.onMessage.addListener(onNativeMessage); //监听exe程序是否发来消息
  port.onDisconnect.addListener(onDisconnected); //监听是否断开连接
  port.postMessage(msg) //向应用程序发送信息
}

 (4) Test.html中的代码

<html>
<style>
  .message {
    width: 500px;
    height: 500px;
    background-color: pink;
  }

  h3 {
    background-color: red;
  }
</style>

<head>
  <script>
    function startApp(i) {
      //创建CustomEvent事件
      var eventName = "myCustomEvent" + i;
      var evt = document.createEvent("CustomEvent");
      evt.initCustomEvent(eventName, true, false, "用户点击了按钮" + i);
      //执行自定义事件
      document.dispatchEvent(evt);
    }
  </script>
</head>

<body>

  <button type="button" onClick="startApp(1)" id="startApp">按钮1</button>
  <button type="button" onClick="startApp(2)" id="startApp">按钮2</button>
  <button type="button" onClick="startApp(3)" id="startApp">按钮3</button>
  <button type="button" onClick="startApp(4)" id="startApp">按钮4</button>
  <div class="message">
    <h1 class="jieshou">接收到的消息:</h1>
  </div>
</body>

</html>

2.把自己写的chrome插件安装到chrome浏览器中

(1)把上述创建的4个文件放在同一个文件夹中,命名任意

(2)在chrome浏览器地址栏输入:chrome://extensions  进入插件管理页面

(3)打开右上角《开发者模式》

(4)点击左上角《加载已解压的扩展程序》,选择4个文件所在的文件夹并添加

(5)就可以看到插件已经被安装到chrome浏览器当中,如下图 (每个插件都会自动生成一个独一无二的ID值,我们需要保存自己编写插件的ID值,下面需要用到,记得保存!!!)

 

 3. 创建Test.py并写入代码

打开pycharm,创建Test.py文件,其中代码如下:(没有相应包的话需要先导入相应的包)

import json
import sys
import struct
# 读取来自 stdin 的消息并对其进行解码
def get_message():
    raw_length = sys.stdin.buffer.read(4)
    if not raw_length:
        sys.exit(0)
    message_length = struct.unpack('=I', raw_length)[0]
    message = sys.stdin.buffer.read(message_length).decode("utf-8")
    return json.loads(message)

# 根据信息的内容对信息进行编码以便传输。
def encode_message(message_content):
    encoded_content = json.dumps(message_content).encode("utf-8")
    encoded_length = struct.pack('=I', len(encoded_content))
    #  use struct.pack("10s", bytes), to pack a string of the length of 10 characters
    return {'length': encoded_length, 'content': struct.pack(str(len(encoded_content))+"s",encoded_content)}

# 向标准输出发送编码好的消息
def send_message(encoded_message):
    sys.stdout.buffer.write(encoded_message['length'])
    sys.stdout.buffer.write(encoded_message['content'])
    sys.stdout.buffer.flush()

# 持续监听chrome插件发来的消息
while True:
    message = get_message() # 得到来自chrome插件的消息
    if(message=="用户点击了按钮1"): # 根据消息的不同内容,exe程序向chrome插件发送不同的字符串
        send_message(encode_message("来自exe程序的消息:按钮1被点击"))
    elif(message=="用户点击了按钮2"):
        send_message(encode_message("来自exe程序的消息:按钮2被点击"))
    elif(message=="用户点击了按钮3"):
        send_message(encode_message("来自exe程序的消息:按钮3被点击"))
    elif(message=="用户点击了按钮4"):
        send_message((encode_message("来自exe程序的消息:按钮4被点击")))

4.把Test.py打包成exe程序

     在pycharm终端中输入:  pyinstaller -F Test.py (如果没有pyinstaller包的需要先导包)

然后打包好的exe程序就在Test.py所在文件夹下的dist文件夹中,文件名为Test.exe

5.将Test.exe程序写入注册表

(1)首先需要创建一个文件,名为manifest.json(此文件不同于上面创建的manifest.json文件,只是名字一样 , 不要忘记把allowed_origins下的字符串替换成自己所写插件的ID值 !!!),其中代码为:

{
    "name": "com.my_company.my_application",  //此名就是hostName,即主机名
	"description": "Chrome sent message to native app.", //对json文件的描述
	"path": "Test.exe",  //相对路径,打包好的Test.exe相对于本manifest文件的相对路径
	"type": "stdio",     //必填项,意为通过标准输入输出流进行通信
	"allowed_origins": [
		"chrome-extension://jckgmlnngpemfhapkfdapedimdpimlmd/" //中间的字符串是我们编写的插件的ID值,需要自己替换
	]
}

(2)按住 win+r 键打开运行windows命令窗口,输入 regedit 进入注册表编辑器,找到HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts 目录,右键单击NativeMessagingHosts,选择新建,选择项,新建一个名为 com.my_company.my_application 的项(该名就是主机名,名字应与上面那个manifest.json的name值相同),然后选中该新建的项,双击右侧默认,修改数值数据为第二次创建的manifest.json文件在本机的绝对路径,具体操作步骤见下图

 

 6. 测试

完成上述步骤,打开上面创建的 Test.html,出现四个按钮,单击任意一个按钮,chrome插件将向exe程序发送消息,exe程序接收到消息后根据消息返回对应的值并出现在下方。如下图所示

 7.补充

插件当中content.js中输出的内容会被打印在当前页面的控制台(按下键盘F12键即可找到控制台),可以自己添加打印信息进行调试。

插件当中background.js中输出的内容会被打印在当前插件的背景页的控制台中(chrome浏览器地址栏输入chrome://extensions,找到自己写的插件,点击背景页就可以找到控制台),可以自己添加打印信息进行调试。

 

好了,本篇文章就到此结束啦!如果喜欢记得给我点个赞哦!有其他任何疑问信息可以在评论区留言咨询!

 

 

有关chrome插件与本地exe程序之间的信息交互的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  6. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  7. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  8. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  9. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  10. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

随机推荐