当前位置: 首页 > news >正文

使用子进程实现 C++ 与 Python 交互式控制台

引言​

在软件开发中,有时我们需要在C++应用程序中嵌入Python解释器,实现两种语言间的交互操作。本文介绍一种基于 ​​Windows 匿名管道​​的技术方案,通过重定向标准输入/输出实现 C++ 对 Python 解释器的实时控制。核心代码仅 200 行,却能构建完整的交互式控制台。

一、技术核心:匿名管道与进程通信​

Windows 管道(CreatePipe)是进程间通信的关键设施。我们通过以下步骤建立通信通道:

  1. 创建双向管道​
CreatePipe(&hChildStd_OUT_Rd_, &hChildStd_OUT_Wr_, &saAttr, 0);
SetHandleInformation(hChildStd_OUT_Rd_, HANDLE_FLAG_INHERIT, 0);
  • 输出管道:子进程(Python)写 → 父进程(C++)读;
  • 输入管道:父进程写 → 子进程读;
  • SetHandleInformation 确保父进程独享管道控制权。
  1. 重定向 Python 的标准流​
    STARTUPINFO 中配置标准流重定向:
STARTUPINFO siStartInfo;
siStartInfo.hStdInput = hChildStd_IN_Rd_;   // Python 从此读命令
siStartInfo.hStdOutput = hChildStd_OUT_Wr_;  // Python 向此写结果
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

二、核心类 ScriptInteractor 剖析​

2.1 进程启动与管道绑定​

通过 CreateProcess 启动 Python 交互环境:

void start_process() {wchar_t cmdLine[] = L"python.exe -i";  // 启动交互式解释器CreateProcess(NULL, cmdLine, ..., &siStartInfo, &piProcInfo);CloseHandle(hChildStd_OUT_Wr_);  // 父进程无需写端CloseHandle(hChildStd_IN_Rd_);   // 父进程无需读端
}

关键点:

  • CREATE_NO_WINDOW 隐藏控制台窗口;
  • 关闭冗余句柄防止资源泄漏。

2.2 异步输出捕获线程

使用独立线程实时捕获Python输出:

void read_output() {while (running_.load()) {PeekNamedPipe(hChildStd_OUT_Rd_, ..., &dwAvailable, ...);if (dwAvailable > 0) {ReadFile(hChildStd_OUT_Rd_, chBuf, ...);std::cout << chBuf;  // 实时打印Python输出}std::this_thread::sleep_for(50ms);}
}
  • PeekNamedPipe 非阻塞检查数据,避免线程死锁;
  • 原子标志 running_实现安全线程退出。

2.3 命令发送与退出控制​

void send_script(const std::string& data) {WriteFile(hChildStd_IN_Wr_, data.c_str(), data.size(), ...);
}
void quit() {send_script("exit()\n");  // 发送Python退出指令running_ = false;         // 停止输出线程
}

三、主循环:交互式控制台实现​

int main() {SetConsoleOutputCP(CP_UTF8);  // 支持中文输出ScriptInteractor interactor;while (true) {std::string line;std::getline(std::cin, line);if (line == "exit()") break;interactor.send_script(line + "\n");  // 发送命令}interactor.quit();  // 优雅退出
}
  • 用户输入直接转发至 Python;
  • 退出时自动清理管道和线程资源。

四、完整源码

ScriptInteractor.hpp

#include <string>
#include <thread>
#include <atomic>
#include <mutex>
#include <iostream>
#include <windows.h>class ScriptInteractor {
public:ScriptInteractor() :hProcess_{}, hChildStd_IN_Rd_{}, hChildStd_IN_Wr_{}, hChildStd_OUT_Rd_{}, hChildStd_OUT_Wr_{} {create_pipes();start_process();// 启动输出读取线程running_.store(true, std::memory_order_release);outputThread_ = std::thread(&ScriptInteractor::read_output, this);}~ScriptInteractor() {// 通知线程退出并等待线程结束running_.store(false, std::memory_order_release);if (outputThread_.joinable()) {outputThread_.join();}// 关闭所有句柄close_handles();}void send_script(const std::string& data) {DWORD dwWritten;WriteFile(hChildStd_IN_Wr_, data.c_str(), static_cast<DWORD>(data.size()), &dwWritten, NULL);}void quit() {running_.store(false, std::memory_order_release);// 发送退出命令send_script("exit()\n");}private:// 创建管道void create_pipes() {SECURITY_ATTRIBUTES saAttr{};saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);saAttr.bInheritHandle = TRUE;saAttr.lpSecurityDescriptor = NULL;// 创建输出管道if (!CreatePipe(&hChildStd_OUT_Rd_, &hChildStd_OUT_Wr_, &saAttr, 0)) {std::cerr << "CreatePipe failed: " << GetLastError() << std::endl;return;}// 确保读取端不被继承if (!SetHandleInformation(hChildStd_OUT_Rd_, HANDLE_FLAG_INHERIT, 0)) {std::cerr << "SetHandleInformation failed: " << GetLastError() << std::endl;return;}// 创建输入管道if (!CreatePipe(&hChildStd_IN_Rd_, &hChildStd_IN_Wr_, &saAttr, 0)) {std::cerr << "CreatePipe failed: " << GetLastError() << std::endl;return;}// 确保写入端不被继承if (!SetHandleInformation(hChildStd_IN_Wr_, HANDLE_FLAG_INHERIT, 0)) {std::cerr << "SetHandleInformation failed: " << GetLastError() << std::endl;return;}}// 启动进程void start_process() {PROCESS_INFORMATION piProcInfo;STARTUPINFO siStartInfo;ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));siStartInfo.cb = sizeof(STARTUPINFO);siStartInfo.hStdError = hChildStd_OUT_Wr_;siStartInfo.hStdOutput = hChildStd_OUT_Wr_;siStartInfo.hStdInput = hChildStd_IN_Rd_;siStartInfo.dwFlags |= STARTF_USESTDHANDLES;// 创建Python进程wchar_t cmdLine[] = L"python.exe -i";BOOL bSuccess = CreateProcess(NULL,           // 应用程序名cmdLine,        // 命令行NULL,           // 进程安全属性NULL,           // 线程安全属性TRUE,           // 继承句柄CREATE_NO_WINDOW, // 创建标志NULL,           // 环境变量NULL,           // 当前目录&siStartInfo,   // STARTUPINFO&piProcInfo     // PROCESS_INFORMATION);if (!bSuccess) {std::cerr << "CreateProcess failed: " << GetLastError() << std::endl;return;}// 保存进程句柄hProcess_ = piProcInfo.hProcess;// 关闭不需要的句柄CloseHandle(piProcInfo.hThread);CloseHandle(hChildStd_OUT_Wr_);CloseHandle(hChildStd_IN_Rd_);}void read_output() {constexpr int BUFFER_SIZE = 4096;DWORD dwRead;CHAR chBuf[BUFFER_SIZE]{};while (running_.load(std::memory_order_acquire)) {// 检查是否有数据可读DWORD dwAvailable;if (!PeekNamedPipe(hChildStd_OUT_Rd_, NULL, 0, NULL, &dwAvailable, NULL) || dwAvailable == 0) {std::this_thread::sleep_for(std::chrono::milliseconds(50));continue;}// 读取数据BOOL bSuccess = ReadFile(hChildStd_OUT_Rd_, chBuf, BUFFER_SIZE - 1, &dwRead, NULL);if (!bSuccess || dwRead == 0) {if (GetLastError() == ERROR_BROKEN_PIPE) {running_.store(false, std::memory_order_release);break;}continue;}// 处理读取到的数据chBuf[dwRead] = '\0';std::cout << chBuf;}}void close_handles() {if (hProcess_) {CloseHandle(hProcess_);}if (hChildStd_IN_Wr_) {CloseHandle(hChildStd_IN_Wr_);}if (hChildStd_OUT_Rd_) {CloseHandle(hChildStd_OUT_Rd_);}}private:// Windows API句柄HANDLE hProcess_;HANDLE hChildStd_IN_Rd_;HANDLE hChildStd_IN_Wr_;HANDLE hChildStd_OUT_Rd_;HANDLE hChildStd_OUT_Wr_;// 线程管理std::thread outputThread_;std::atomic_bool running_;
};

main.cpp

#include "ScriptInteractor.hpp"int main() {// 设置控制台编码为UTF-8SetConsoleOutputCP(CP_UTF8);ScriptInteractor interactor;std::cout << "Python Interactive Console (Windows API)" << std::endl;std::cout << ">>> ";while (true) {std::string line;std::getline(std::cin, line);if (line == "exit()" || line == "quit()") {break;}// 发送命令给Python进程interactor.send_script(line + "\n");}interactor.quit();return 0;
}

五、跨平台解决方案

5.1 Boost.Process 实现方案

Boost.Process是Boost库中用于跨平台进程管理的组件,提供了统一的API来处理不同操作系统的进程管理差异。
核心实现思路:​

#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/asio.hpp>namespace bp = boost::process;class BoostScriptInteractor {
public:BoostScriptInteractor() : io_context_(), outputThread_(), running_(true) {create_process();start_async_read();}void send_script(const std::string& data) {if (stdin_) {stdin_->write(data.c_str(), data.size());stdin_->flush();}}void quit() {running_ = false;send_script("exit()\n");if (child_) {child_->terminate();child_->wait();}}private:void create_process() {// 创建管道stdin_ = std::make_unique<bp::opstream>();stdout_ = std::make_unique<bp::ipstream>();// 启动Python进程child_ = std::make_unique<bp::child>("python -i", // 命令行bp::std_in < *stdin_,bp::std_out > *stdout_,io_context_);}void start_async_read() {outputThread_ = std::thread([this]() {std::string line;while (running_) {if (stdout_ && std::getline(*stdout_, line)) {std::cout << line << std::endl;} else {std::this_thread::sleep_for(std::chrono::milliseconds(50));}}});}private:boost::asio::io_context io_context_;std::unique_ptr<bp::child> child_;std::unique_ptr<bp::opstream> stdin_;std::unique_ptr<bp::ipstream> stdout_;std::thread outputThread_;std::atomic_bool running_;
};

5.2 Qt QProcess 实现方案

Qt 的 QProcess 类提供了强大的跨平台进程管理能力,特别适合GUI应用程序。
核心实现思路:

#include <QProcess>
#include <QThread>class QtScriptInteractor : public QObject {Q_OBJECT
public:QtScriptInteractor(QObject *parent = nullptr) : QObject(parent) {process_.setProgram("python");process_.setArguments(QStringList() << "-i");// 连接信号槽connect(&process_, &QProcess::readyReadStandardOutput, this, &QtScriptInteractor::onReadyRead);connect(&process_, &QProcess::readyReadStandardError, this, &QtScriptInteractor::onReadyRead);// 启动进程process_.start();process_.waitForStarted();}void send_script(const std::string& data) {if (process_.state() == QProcess::Running) {process_.write(data.c_str(), data.size());}}void quit() {send_script("exit()\n");process_.closeWriteChannel();process_.waitForFinished();}public slots:void onReadyRead() {QByteArray output = process_.readAllStandardOutput();QByteArray error = process_.readAllStandardError();if (!output.isEmpty()) {std::cout << output.constData();}if (!error.isEmpty()) {std::cerr << error.constData();}}private:QProcess process_;
};
http://www.dtcms.com/a/515997.html

相关文章:

  • 网站建设登录界面代码上海app开发费用
  • 复习下线性代数,使用向量平移拼接两段线
  • 南通网站定制哪家好北京西站附近景点
  • 网站建设大怎么做网页自我介绍
  • 光子精密3D工业相机:赋能国产“2D+3D”精密测量微米级迭代
  • HTTP 三次握手最终状态变更的时机
  • ROS跑ORB-SLAM3遇见的问题总结
  • 晋中路桥建设集团有限公司网站网站开发费用科目
  • kubernetes K8s的监控系统Prometheus 酷炫整体容器监控(三)
  • 记录一次 K8s 环境中 DNS 解析延迟导致 WebClient 请求失败的排查过程
  • 美的网站建设水平swot分析陕西网站建设的目的
  • 安科瑞能源物联网云平台光伏防逆流解决方案
  • Vivado调用FFT IP核进行数据频谱分析
  • 数据结构之顺序表:一款优秀的顺序存储结构
  • 如何将联系人从iPhone转移到iQOO
  • 广州营销型网站成都网站建设app开发
  • 个体户做网站有用吗外链工厂
  • LVDS系列32:Xilinx 7系 ADC LVDS接口参考设计(三)
  • TPS62402DRCR双通道同步降压DC-DC转换器 TI德州仪器 降压转换器 芯片解析
  • 项目实践4—全球证件智能识别系统(Qt客户端开发+FastAPI后端人工智能服务开发)
  • 下载asp网站哪里有免费网站可以看
  • 公司网站怎么做才高大上wordpress好用的模板
  • <自用文 重装 Windows 11 后> ssh-agent 配置
  • web网页开发,在线%考试,教资,题库%系统demo,基于vue,html,css,python,flask,随机分配,多角色,前后端分离,mysql数据库
  • SQL入门:别名使用完全指南
  • 有什么做兼职的好的网站吗网站和服务器的关系
  • 湘潭建网站网站版式分类
  • 基于Flask的志愿者管理系统
  • .NET实现多任务异步与并行处理的详细步骤
  • stripe 支付对接