您现在的位置是:网站首页> C/C++

Qt-QProcess-启动子进程-控制台进程隐藏-获取子进程标准输出和返回码

  • C/C++
  • 2023-10-07
  • 585人已阅读
摘要

网上已经有很多教程了,这里再说一下:

//注意,mProcess为共享指针,要特别注意,信号和槽的连接要使用原始指针(也就是mProcess.get()函数)

std::shared_ptr<QProcess> mProcess;    

mProcess = std::make_shared<QProcess>();

 

mProcess->setProgram(cmd);

mProcess->setArguments(splitProcessCommand(arguments));

mProcess->setProcessChannelMode(QProcess::MergedChannels);   //设置读取标准输出模式 

mProcess->setProcessEnvironment(env);                        //设置环境变量

mProcess->setWorkingDirectory(workingDir);                   //这种工作目录

 

 

ok = connect(mProcess.get(), &QProcess::readyReadStandardError,[this](){ 

        this->log(QString::fromLocal8Bit( mProcess->readAllStandardError()));

    });

    

ok = connect(mProcess.get(), &QProcess::readyReadStandardOutput,[this](){

        //这样就实现把结果exe的信息给显示在控制台了

        this->log(QString::fromLocal8Bit( mProcess->readAllStandardOutput()));    

    });

 

mProcess->start();

mProcess->waitForStarted(5000);

 

//这里可以给它传入一些数据

mProcess->write(readFileToByteArray(mInputFile));

mProcess->closeWriteChannel();

 

 

//如果啥时候想终止这个进程,还可以用下面的代码

mProcess->closeReadChannel(QProcess::StandardOutput);

mProcess->closeReadChannel(QProcess::StandardError);

mProcess->closeWriteChannel();

mProcess->terminate();

mProcess->kill();

 

 

 

//这里面就能获得启动的exe的标准输出信息了

void DebugTarget::log(const QString &msg)

{

    emit debugTargetOutput(msg);

}

特别注意:


如果需要修改process的控制台窗口是否显示,则用下面的函数


mProcess->setCreateProcessArgumentsModifier([this](QProcess::CreateProcessArguments * args)

{   

        //下面这两行让后台exe弹出一个窗口         

        args->flags |=  CREATE_NEW_CONSOLE;            

        args->flags &= ~CREATE_NO_WINDOW;

}

切记:在这个函数里,下面这一行,千万不要用,否则qprocess关闭了标准输出,我们就获取不到输出了(我踩了这个巨坑,一天才找出来)

               args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES;


当然,如果不通过信号与槽函数机制,自己主动去读取mProcee的标准输出也可以的,那就用


mProcee->readAll()

mProcee->write()


2.QProcess

QProcess是Qt提供的强大进程交互工具。简单的进程调用,采用上述模式即可解决,而父子进程之间更复杂的交互,QProcess提供了更好的解决方案。


2.1.基础用法-start和startDetached

start是一体式的:外部程序启动后,将随主程序的退出而退出;

startDetached是分离式的:外部程序启动后,不会随主程序的退出而退出。

重要区别:如果是start则回调都可以正常接收到信息;如果是startDetached则回调无法正常接收到信息。

如果是简单调用,建议采用startDetached,需要获取进程执行的各种状态,建议采用start。QProcess启动的控制台程序,都是隐藏窗口。范例代码如下:


#include <QProcess>

static void TestStartDetached(){

//startDetached是个静态函数,可以直接调用,一共有四种形式

QProcess::startDetached(QString::fromLocal8Bit("./TestConsoleApplication1.exe"));

QProcess::startDetached(QString::fromLocal8Bit("./TestConsoleApplication1.exe"), 

QStringList() << "para1" << "para2");

}


static void TestStart(){

QProcess* tQProcess = new QProcess;

//子进程会随着tQProcess的销毁而关闭,如果不想随着函数的调用结束而关闭,需要在堆上申请内存。

tQProcess->start(QString::fromLocal8Bit("./TestConsoleApplication1.exe"),

QStringList()<<"para1"<<"para2" );

//tQProcess->deleteLater();

}


static void TestStart2(){

//显示窗口-极少使用

QProcess *tQProcess = new QProcess();

tQProcess->start("cmd.exe");

tQProcess->write("cd /d E:/work/CurrentProject/微博/QtConnect/x64/Release && start TestConsoleApplication1.exe\n");

}


2.2.获取子进程的标准输出

进程具有两个预定义的输出通道:标准输出通道(stdout)提供常规控制台输出。标准错误通道(stderr)通常提供由进程打印的错误。

这些通道代表两个单独的数据流,可以通过调⽤setReadChannel()函数在它们之间切换。当前读取通道上有可⽤数据时,QProcess发出readyRead()信号。当有新的标准输出数据可⽤时,它也会发出readyReadStandardOutput()信号,⽽当有新的标准错误数据可⽤时,它会发出readyReadStandardError()信号。⽆需调⽤read()函数,readLine()函数或getChar()函数,⽽是可以通过调⽤readAllStandardOutput()函数或readAllStandardError()函数显式地从两个通道之⼀读取所有数据。


进程同步API

waitForStarted()函数——阻塞直到进程开始。

waitForReadyRead()函数——阻塞直到有新数据可在当前读取通道上读取为⽌。

waitForBytesWritten()函数——阻塞直到将⼀个有效载荷数据写⼊该进程为⽌。

waitForFinished()函数——阻塞直到过程完成。


3.代码范例

3.1.等待进程执行完毕,获取所有的输出

代码如下所示:


#include <qDebug>

#include <QProcess>

static void TestGetOutData(){

QProcess process;

process.start("ping www.baidu.com");

process.waitForFinished();

QByteArray allOutData = process.readAll();

if (allOutData.isEmpty())

{

allOutData = process.readAllStandardOutput();

if (allOutData.isEmpty())

{

allOutData = process.readAllStandardError();

}

}

//中文乱码问题

qDebug() << QString::fromStdString(allOutData.data());

}


3.2.子进程返回信号

进程结束,返回进程结束参数。


void QtConnect::finished(int exitCode, QProcess::ExitStatus exitStatus)

{

qDebug() << "finished";

qDebug() << exitCode;// 被调用程序的main返回的int

qDebug() << exitStatus;// QProcess::ExitStatus(NormalExit)

}


void QtConnect::Test() {

QProcess* tQProcess = new QProcess;

tQProcess->start(QString::fromLocal8Bit("E:/work/CurrentProject/微博/QtConnect/x64/Debug/TestConsoleApplication1.exe"));

connect(tQProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(finished(int, QProcess::ExitStatus)));

}


3.3.进程是否启动

void QtConnect::Test() {

QProcess* tQProcess = new QProcess;

tQProcess->start(QString::fromLocal8Bit("E:/work/CurrentProject/微博/QtConnect/x64/Debug/TestConsoleApplication1.exe"));

if (!tQProcess->waitForStarted())

{

qDebug() << "成功!";

}

else

{

qDebug() << "失败!";

}

}


3.4.执行命令行

void QtConnect::Test() {

QStringList arguments;

arguments << "/c" << "dir";//

QProcess tProcess(this);

tProcess.setProcessChannelMode(QProcess::MergedChannels);

tProcess.start("cmd.exe", arguments);

tProcess.waitForStarted();

tProcess.waitForFinished();

QString strResult = QString::fromLocal8Bit(tProcess.readAll());

qDebug() << strResult;

}


3.5.与子进程交互

未来有需要再写,本质就是利用通道来进行数据的交互。


QProcess gzip;

    gzip.start("gzip", QStringList() << "-c");

    if (!gzip.waitForStarted())

        return false;


    gzip.write("Qt rocks!");

    gzip.closeWriteChannel();


    if (!gzip.waitForFinished())

        return false;


    QByteArray result = gzip.readAll();


Top