You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox扩展Native Messaging关联Python脚本subprocess.Popen执行失败求助

Firefox Native Messaging Python脚本无法执行subprocess.Popen,但能正常回复消息

问题描述

我开发了一个测试Firefox Native Messaging功能的简单扩展,扩展端运行正常:访问《HTML basics》页面时,会通过stdin向本地Python脚本发送“ping”消息,期望脚本回复消息并通过subprocess.Popen执行进程。但测试发现,Python脚本能正常回复消息,但subprocess.Popen(["explorer", "C:/Temp"])无论放在脚本何处都无法执行。单独运行仅含该Popen代码的脚本可正常打开资源管理器窗口。我尝试重构Python代码并关闭stdin流,但问题依旧。Python脚本无控制台窗口,难以排查阻塞位置,单独测试发现脚本会卡在sys.stdin.buffer.read(4)处。

扩展代码

Manifest.json

{
"name": "test",
"manifest_version": 2,
"version": "1.0",

"browser_action": {"default_icon": "icons/message.svg"},
"browser_specific_settings": {"gecko": {"id": "test@example.org","strict_min_version": "50.0"}},

"background": {"scripts": ["background.js"]},
"permissions": ["tabs","activeTab", "webRequest", "<all_urls>", "nativeMessaging"]
}

background.js

browser.webRequest.onCompleted.addListener(sendNativeMsg, {urls:["https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics"]}); 
function onResponse(response) {console.log(`INCOMING MSG: ${response}`);}

function sendNativeMsg(activeTab) {
    let thisMsg = "ping"
    console.log(`OUTGOING MSG: "${thisMsg}"`);  

    let sending = browser.runtime.sendNativeMessage("test", thisMsg);
    sending.then(onResponse);
    }

Python脚本代码

import sys
import json
import struct
import subprocess

# Read a message from stdin and decode it.
def getMessage():
    rawLength = sys.stdin.buffer.read(4)
    if len(rawLength) == 0:
        sys.exit(0)
    messageLength = struct.unpack('@I', rawLength)[0]
    message = sys.stdin.buffer.read(messageLength).decode('utf-8')
    return json.loads(message)

# Encode a message for transmission,
def encodeMessage(messageContent):
    encodedContent = json.dumps(messageContent, separators=(',', ':')).encode('utf-8')
    encodedLength = struct.pack('@I', len(encodedContent))
    return {'length': encodedLength, 'content': encodedContent}

# Send an encoded message to stdout
def sendMessage(encodedMessage):
    sys.stdout.buffer.write(encodedMessage['length'])
    sys.stdout.buffer.write(encodedMessage['content'])
    sys.stdout.buffer.flush()

while True:
    subprocess.Popen(["explorer", "C:/Temp"])       #This line is never executed. The lines after here are executed.
    receivedMessage = getMessage()
    if receivedMessage == "ping":
        sendMessage(encodeMessage('stdin was "' + receivedMessage + '", Task is done'))

解决方案

问题根源

脚本卡在sys.stdin.buffer.read(4)导致subprocess.Popen无法执行——while True循环启动后,会先进入getMessage()的阻塞读取,直到收到扩展的消息才会继续向下执行。加上Firefox调用的Native脚本是后台无控制台进程,即使Popen执行也可能因环境问题无法正常弹出窗口。

修复步骤

  1. 调整执行顺序,确保Popen在收到消息后触发
    将Popen代码移到收到“ping”消息的分支内,避免因阻塞读取导致代码无法执行:

    while True:
        receivedMessage = getMessage()
        if receivedMessage == "ping":
            # 先执行Popen再回复消息
            subprocess.Popen(["explorer", "C:/Temp"], creationflags=subprocess.CREATE_NO_WINDOW)
            sendMessage(encodeMessage('stdin was "' + receivedMessage + '", Task is done'))
    
  2. 添加Windows环境专属参数
    在Windows下,后台进程启动GUI程序需要指定creationflags=subprocess.CREATE_NO_WINDOW,避免子进程继承父进程的后台属性,确保资源管理器窗口正常弹出。

  3. 调试辅助技巧
    可以临时添加日志输出到文件,排查代码执行情况:

    import logging
    logging.basicConfig(filename='native_msg_debug.log', level=logging.DEBUG)
    
    # 在关键位置插入日志
    if receivedMessage == "ping":
        logging.debug("准备执行Popen")
        subprocess.Popen(["explorer", "C:/Temp"], creationflags=subprocess.CREATE_NO_WINDOW)
        logging.debug("Popen执行完成")
        sendMessage(encodeMessage('stdin was "' + receivedMessage + '", Task is done'))
    

关于stdin的说明

Native Messaging要求脚本保持stdin打开以持续监听扩展消息,关闭stdin会导致脚本直接退出,不符合长连接规范。你遇到的阻塞是正常的监听逻辑,只需确保业务代码在收到消息后执行即可。

内容的提问来源于stack exchange,提问作者Ralf_Reddings

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:43:26