Qt QProcess 进程间通信读写数据通信

本文介绍了如何使用Qt的QProcess  进行程序开发,包括启动进程间通信、设置环境变量、通用方法;方便在日常开发中使用;

1.使用Qt进行程序开发,可以通过QProcess类用于启动外部程序并与其进行通信.;

进程A(例如主程序)创建了一个QProcess B,这个B就称为A的子进程,而A称为B的父进程。

1.1. 运行进程 

可以使用 Start  or Open

要启动进程,需要运行的程序的名称和命令行参数作为参数传递给start()。参数以QStringList形式提供。
start()方法原型:

void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite)
void start(const QString &command, OpenMode mode = ReadWrite)

也可以使用setProgram()setArguments()设置要运行的程序,然后调用start()open()
以下是setProgram()setArguments()open()函数原型:


 bool open(OpenMode mode = ReadWrite) Q_DECL_OVERRIDE;

QString program() const;

 void setProgram(const QString &program);

QStringList arguments() const;

 void setArguments(const QStringList & arguments);

1.2 环境运行设置

因为有一些程序会有依赖,所以需要设备运行环境:

通过调用setProcessEnvironment()为进程设置环境变量。

要设置工作目录,请调用setWorkingDirectory()。默认情况下,进程在调用进程的当前工作目录中运行

原型:

void QProcess::setProcessEnvironment(const QProcessEnvironment &environment)

样例:

  QProcess process;
  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
  env.insert("PATH", "E:\\Program"); // Add an environment variable
  process.setProcessEnvironment(env);
  process.start("app.exe");

1.3. 进程间通信
1.3.1关于父进程读写子进程的数据,主要是用到

子进程接收到了父进程数据,两个信号会发射出来:
void readyReadStandardError()
void readyReadStandardOutput()

父进程通过上面这两个信号,判断读取子进程发来的消息;

QByteArray QProcess::readAllStandardError()
QByteArray QProcess::readAllStandardOutput()

QProcess::write()发出信息

1.3.2 关于子进程读写父进程的数据,主要是用到

Windows中:需要开启一个线程来管理stdin的文件变化,这个需要使用Windows API函数
linux中:使用QSocketNotifier 监听 stdin文件,当改文件有变化是,读取信息

 

ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);// get  hstdinDup handle data

这个是阻塞函数,类似控制台程序中的cin >> data,或者gets()。

因此需要开启一个线程来管理stdin的文件变化(函数中有涉及到while阻塞,一直检测标准输入通道stdin是否有可读取的信息,因此另开线程),在pro文件中添加:QT +=concurrent。实例代码如下:

QFuture<void> fu=QtConcurrent::run(this,&Widget::readStdin);// open a thread

void Widget::readStdin()
{
    bool ok=true;
    char chbuf[1024]; 
    DWORD dwRead; 
    HANDLE hStdinDup;//HANDLE  
 
    const HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);//GetStdHandle 
    if(hStdin==INVALID_HANDLE_VALUE)// 
        return;
    DuplicateHandle(GetCurrentProcess(),hStdin,
                    GetCurrentProcess(),&hStdinDup,0,false,DUPLICATE_SAME_ACCESS); 
    CloseHandle(hStdin);

    while(ok)
    {
        ok=ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);//

        emit sig_log(QLatin1String("ok is:")+QString::number(ok));
        if(ok &&dwRead!=0)
        {
            emit sig_receivedCommand(QString::fromUtf8(chbuf,dwRead));
        }
    }
 
}


 linux平台:QFile来读取标准输入

QProcess子进程通过QFile来读取标准输入来接收父进程信息。通过QFile绑定QSocketNotifier来接收标准输入的实时信号,因为QSocketNotifier的activated信号可以通过标准输入是否有消息实时触发。

QProcess子进程通过QFile绑定标准输出stdout来发送消息给父进程。
注意:子进程读取信息不能通过QFile的readline等接口读取父进程信息,因为QFile他会读取标准输入的所有信息,并且不到长度就没有返回
 

m_file.open(stdin, QFile::ReadOnly); //stdin=0
     if (m_file.isOpen())
     {
         m_clientsocket = new QSocketNotifier(m_file.handle(), QSocketNotifier::Read,this);
         connect(m_clientsocket,SIGNAL(activated(int)),this,SLOT(slotreaddata(int)));
     }void MainWindow::slotreaddata(int fd)
 {
     if(fd != m_file.handle() )
     return;    char buf[128] = {0};
     read(fd,buf,sizeof(buf));      
     m_readedit->append(QString::fromUtf8("rev msg:%1").arg(buf));
}

2. 程序样例:

主进程
 

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    if(m_pProcess)
    {
        m_pProcess->close();
    }
    delete ui;
}

void MainWindow::on_btn_invokingClient_clicked()
{
    if(!m_pProcess)
    {
        m_pProcess = new QProcess(this);
        // 完成时调用
        connect(m_pProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus){
            Q_UNUSED(exitCode)
            Q_UNUSED(exitStatus)
        });

        // 进程错误时触发
        connect(m_pProcess, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), this, [=](QProcess::ProcessError error1){
            Q_UNUSED(error1)
        });

        // 读取
        connect(m_pProcess, &QProcess::readyRead, this, [this](){
            if(!m_pProcess)
                return;
            QString strOutput = QString("[客户端发送输出 ] %1").arg(QString(m_pProcess->readAllStandardOutput()));
            ui->textBrowser->append(strOutput);
        });
        // 读取标准错误信息
        connect(m_pProcess, &QProcess::readyReadStandardError, this, [=](){
            QString strError = QString("[客户端发送错误 ] %1").arg(QString(m_pProcess->readAllStandardError()));
            ui->textBrowser->append(strError);
        });
        // 状态改变时触发
        connect(m_pProcess, &QProcess::stateChanged, this, [=](QProcess::ProcessState state){
            Q_UNUSED(state)
        });

    }
    m_pProcess->start("G:/workspace/Qt_process-invoke-client/build-client-Desktop_Qt_5_12_4_MSVC2017_64bit-Debug/debug/client.exe");
}

void MainWindow::on_btn_sendValueToClient_clicked()
{
    if(!m_pProcess)
        return;
    // 可输中文
    m_pProcess->write(ui->lineEdit->text().toUtf8());
}

子进程:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QFile>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("client");

    QFuture<void> f1 = QtConcurrent::run(this, &MainWindow::readstdin);
    connect(this,&MainWindow::sig_receivedCommand, this, [&](QString text){
        ui->textBrowser->append("[读取标准输入 ] "+ text);
    });
    connect(this, &MainWindow::sig_log, this, &MainWindow::slot_print);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_btn_sendOutputToInvoker_clicked()
{
    QString text = ui->lineEdit->text();
    if(text.isEmpty()) return;
#if 0
    QFile fileout;
    if(fileout.open(stdout,QIODevice::WriteOnly)){
        fileout.write(text.toStdString().c_str());
    }else{
        printErr("open fail");
    }
    fileout.close();
#else
    std::cout << text.toStdString() << std::endl;
#endif
}

void MainWindow::printErr(const QString &errText)
{
    QFile fileerr;
    if(fileerr.open(stderr,QIODevice::WriteOnly)){
        fileerr.write(errText.toStdString().c_str());
    }
    fileerr.close();
}

void MainWindow::on_btn_sendErrToInvoker_clicked()
{
    QString text = ui->lineEdit->text();
    if(text.isEmpty()) return;
#if 1
    QFile fileerr;
    if(fileerr.open(stderr,QIODevice::WriteOnly)){
        fileerr.write(text.toStdString().c_str());
    }
    fileerr.close();
#else
    std::cerr << text.toStdString() << std::endl;
#endif
}

void MainWindow::slot_print(const QString &text)
{
    ui->textBrowser->append("[读取标准输入 ] "+ text);
}
void MainWindow::readstdin()
{
    bool ok = true;
    char chBuf[4096];
    DWORD dwRead;
    HANDLE hStdinDup;

    const HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (hStdin == INVALID_HANDLE_VALUE)
        return;

    DuplicateHandle(GetCurrentProcess(), hStdin,
                    GetCurrentProcess(), &hStdinDup,
                    0, false, DUPLICATE_SAME_ACCESS);

    CloseHandle(hStdin);
    while (ok) {
        ok = ReadFile(hStdinDup, chBuf, sizeof(chBuf), &dwRead, NULL);
        // emit sig_log(QLatin1String("ok is:")+QString::number(ok));
        if (ok && dwRead != 0)
        {
            emit sig_receivedCommand(QString::fromUtf8(chBuf, dwRead));
        }
    }
}

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: QProcessQt框架中的一个类,用于在一个进程中启动和控制外部程序。它提供了一种进程间通信的机制,使得父进程可以与子进程进行数据的交换和通信。 使用QProcess进行进程间通信可以通过以下步骤实现: 1. 创建一个QProcess对象,可以通过构造函数主动创建,或者通过new运算符动态创建一个对象。 2. 设置要启动的外部程序的路径和命令行参数,可以通过setProgram()和setArguments()函数来实现。 3. 可选择设置一些其它属性,例如工作目录(setWorkingDirectory())、环境变量(setEnvironment())等。 4. 调用start()函数启动子进程。在启动后,子进程会自动运行,并根据程序的执行结果发出相应的信号。 5. 通过信号和槽机制,父进程可以与子进程进行通信。例如,可以通过readyRead()信号和readAll()函数来读取子进程的标准输出信息,也可以通过write()函数向子进程的标准输入写入数据。 6. 子进程执行完毕后会发出相应的finished()信号,可以通过connect()函数将此信号连接到一个槽函数,从而实现对子进程的处理。 通过以上步骤,可以实现父进程与子进程之间的通信。例如,可以通过QProcess启动一个外部程序,然后通过读取子进程的输出信息来获取一些结果,或者通过向子进程的标准输入写入数据来实现交互。 总而言之,QProcess提供了一种简单而灵活的进程间通信机制,使得父进程能够方便地与子进程进行数据交换和通信。同时,QProcess还提供了一些其它的接口函数,用于控制和监控子进程的执行状态,以及设置各种属性,提高了进程通信的灵活性和可扩展性。 ### 回答2: `QProcess`是Qt中用于控制子进程的类。它不仅可以执行外部的可执行文件,还可以与其进行进程间通信。 `QProcess`中的进程通信功能可以通过以下几种方式实现: 1. 通过输入输出:`QProcess`提供了标准输入、标准输出和标准错误输出的接口,可以通过这些接口实现与子进程之间的通信。通过`write()`函数可以向子进程发送数据,而通过`readyRead()`信号则可以接收子进程发送的数据。 2. 通过信号与槽:`QProcess`还提供了`finished()`信号,当子进程退出时会发出该信号。我们可以连接这个信号来执行一些特殊的操作,比如在子进程退出后进行一些清理工作。 3. 通过环境变量:子进程可以通过使用`processEnvironment()`函数获取`QProcess`的环境变量,从而与主进程共享一些配置信息。 总之,借助于`QProcess`,我们可以很方便地实现进程间的通信。无论是通过发送和接收数据、使用信号与槽机制还是共享环境变量,`QProcess`都提供了灵活的接口,让我们可以轻松实现进程间的通信需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋恋西风

up up up

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值