QProcess problems, output of process

2019-02-14 15:12发布

问题:

I am trying to figure out the use of QProcess. I looked at Qt doc with no luck.
http://doc.qt.io/qt-4.8/qprocess.html

EXAMPLES OF PROBLEM.

Example 1: Code bellow works.

    #include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>    

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QTextStream qout(stdout);    

    QProcess cmd;

    cmd.start("cmd");
    if (!cmd.waitForStarted())  {
        return false;
    }

    cmd.waitForReadyRead();
    QByteArray result = cmd.readAll();
    //qout << result.data() << endl;   //console junk captured, don't show. 

    //My test command
    cmd.write("echo hello");
    cmd.write("\n");

    //Capture my result
    cmd.waitForReadyRead();
    //This is my command shown by cmd, I don't show it, capture & discard it.
    result = cmd.readLine();
    //Read result of my command ("hello") and the rest of output like cur dir.   
    result = cmd.readAll();    
    qout << result.data();

    qout << "\n\n---End, bye----" << endl;
    return a.exec();
}

The output of the above code is

hello

F:\Dev_Qt\expControllingExtConsoleApps-build-desktop>

---End, bye----

The problem is that if I try to use ipconfig or 7zip in this fashion via Qprocess and cmd console, I am unable to see any output from ipconfig or 7zip. I don't know if anything is even done, if something is done then why can't i see the output? Code bellow illustrates.

Example 2: Does not work. Can't use ipconfig.

#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QString>
#include <QProcess>    

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   QTextStream qout(stdout);

   QProcess cmd2;
    cmd2.setWorkingDirectory("C:/Program Files/7-Zip");   //not needed in this example.
    cmd2.setReadChannel(QProcess::StandardOutput);
    cmd2.setProcessChannelMode(QProcess::MergedChannels);

    cmd2.start("cmd");
    if (!cmd2.waitForStarted())
    {
        qout << "Error: Could not start!" << endl;
        return false;
    }

    cmd2.waitForReadyRead();
    QByteArray result = cmd2.readAll();
    qout << result.data() << endl;      //Console version info, etc.

    //My command
    cmd2.write("ipconfig");
    cmd2.write("\n");

    //Capture output of ipconfig command
    //DOES NOT WORK!!
    cmd2.waitForReadyRead();
    while (! cmd2.atEnd())
    {
        result = cmd2.readLine();
        qout << result;
        result.clear();
    }
    qout << endl;

    qout << "\n\n---end----" << endl;
    return a.exec();

}

Output is bellow, it is missing the ipconfig connection information result. No output from ipconfig is captured at all.

Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.

C:\Program Files\7-Zip> ipconfig

---end----

Should have been more like this (with ipconfig result).

Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\noname>ipconfig

Windows IP Configuration

Ethernet adapter Local Area Connection:

    Connection-specific DNS Suffix  . :
    IP Address. . . . . . . . . . . . : 192.172.148.135
    Subnet Mask . . . . . . . . . . . : 255.255.255.0
    Default Gateway . . . . . . . . . : 192.172.148.177

C:\Documents and Settings\noname>

Obviously the output should should have been a little diff than above but the Connection info wich is the output of "ipconfig" should have been captured. In the same way if I try to use 7zip via cmd console... I can not see/capture any output of 7zip. So my question is how can I use command line apps like ipconfig and 7zip via QProcess and cmd console and see the result of the output of these applications?

Example 3: 7zip does not work

#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>    

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   QTextStream qout(stdout);

    QProcess cmd2;
    cmd2.setWorkingDirectory("C:/Program Files/7-Zip");
    cmd2.setReadChannel(QProcess::StandardOutput);
    cmd2.setProcessChannelMode(QProcess::MergedChannels);

    cmd2.start("cmd");
    if (!cmd2.waitForStarted()) {
        return false;
    }

    //My Command
    cmd2.write("7z.exe");
    cmd2.write("\n");

    //Capture output of ipconfig command
    cmd2.waitForReadyRead();
    QByteArray result;

    while (! cmd2.atEnd()) {
        result = cmd2.readLine();
        qout << result;
        result.clear();
    }
    qout << endl;

    qout << "\n\n---end----" << endl;
    return a.exec();
}

Output bellow. Does not show anything from 7zip.

Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.

C:\Program Files\7-Zip>7z.exe

---end----

Output is expected to be along the lines of...

Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\noname>cd C:\Program Files\7-Zip

C:\Program Files\7-Zip>7z.exe

7-Zip 9.15 beta Copyright (c) 1999-2010 Igor Pavlov 2010-06-20

Usage: 7z [...] [...] [<@listfiles...>]

a: Add files to archive
b: Benchmark d: Delete files from archive e: Extract files from archive (without using directory names) l: List contents of archive
t: Test integrity of archive u: Update files to archive x: eXtract files with full paths
-ai[r[-|0]]{@listfile|!wildcard}: Include archives
-ax[r[-|0]]{@listfile|!wildcard}: eXclude archives -bd: Disable percentage indicator
-i[r[-|0]]{@listfile|!wildcard}: Include filenames -m{Parameters}: set compression Method
-o{Directory}: set Output directory -p{Password}: set Password -r[-|0]: Recurse subdirectories -scs{UTF-8 | WIN | DOS}: set charset for list files -sfx[{name}]: Create SFX archive -si[{name}]: read data from stdin -slt: show technical information for l (List) command -so: write data to stdout -ssc[-]: set sensitive case mode -ssw: compress shared files
-t{Type}: Set type of archive -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: Update options -v{Size}[b|k|m|g]: Create volumes -w[{path}]: assign Work directory. Empty path means a temporary directory
-x[r[-|0]]]{@listfile|!wildcard}: eXclude filenames -y: assume Yes on all queries

C:\Program Files\7-Zip>

回答1:

eI see one big problem. Under windows you issue a commend pressing the Enter key. Writing

cmd.write("command");
cmd.write("\n");


just isn't enough you have to write

cmd.write("command");
cmd.write("\n\r");

Notice the trailing \r. Try this, it should work better, and by better I mean 7zip. I don't know if you'll get ipconfig to work properly.

Good luck and best regards
D

EDIT Here is a working solution:


#include <QtCore/QCoreApplication>
#include <QtCore/QProcess>
#include <QtCore/QString>
#include <QtCore/QTextStream>

// Not clean, but fast
QProcess *g_process = NULL;

// Needed as a signal catcher
class ProcOut : public QObject
{
  Q_OBJECT
public:
  ProcOut (QObject *parent = NULL);
  virtual ~ProcOut() {};

public slots:
  void readyRead();
  void finished();
};

ProcOut::ProcOut (QObject *parent /* = NULL */):
QObject(parent)
{}

void
ProcOut::readyRead()
{
  if (!g_process)
    return;

  QTextStream out(stdout);
  out << g_process->readAllStandardOutput() << endl;
}

void
ProcOut::finished()
{
  QCoreApplication::exit (0);
}

int main (int argc, char **argv)
{
  QCoreApplication *app = new QCoreApplication (argc, argv);

  ProcOut *procOut = new ProcOut();
  g_process        = new QProcess();

  QObject::connect (g_process, SIGNAL(readyReadStandardOutput()),
    procOut, SLOT(readyRead()));
  QObject::connect (g_process, SIGNAL(finished (int, QProcess::ExitStatus)),
    procOut, SLOT(finished()));

  g_process->start (QLatin1String ("cmd"));
  g_process->waitForStarted();

  g_process->write ("ipconfig\n\r");

  // Or cmd won't quit
  g_process->write ("exit\n\r");

  int result = app->exec();

  // Allright, process finished.
  delete procOut;
  procOut = NULL;

  delete g_process;
  g_process = NULL;

  delete app;
  app = NULL;

  // Lets us see the results
  system ("pause");

  return result;
}

#include "main.moc"

Hope that helps. It worked everytime on my machine.



回答2:

Even though Dariusz Scharsig already provided a solution to the problem, I would like to point out what I believe to be the actual problem(s) which can be solved using the signal slot mechanism.

Problem 1. The condition in your while loop is based on bool QProcess::atEnd () const which is according to QProcess Documentation states:

Reimplemented from QIODevice::atEnd().

Returns true if the process is not running, and no more data is available for reading; otherwise returns false.

But if you looking the documentation for QIODevice::atEnd(), it states:

Returns true if the current read and write position is at the end of the device (i.e. there is no more data available for reading on the device); otherwise returns false.

For some devices, atEnd() can return true even though there is more data to read. This special case only applies to devices that generate data in direct response to you calling read() (e.g., /dev or /proc files on Unix and Mac OS X, or console input / stdin on all platforms).

Solution 1. Change the while loop condition to check the state of your process: while(cmd2.state()!=QProcess::NotRunning){.

Problem 2. You use cmd2.waitForReadyRead(); outside of the loop. Perhaps some data is ready for reading now and when you finished reading, some more gets made available:

  • you read the commands you just wrote : ipconfig\n
  • ipconfig takes some time to start up and send text to the console. But by then you have already exited your loop because atEnd() gave true even though your process is still running.

Solution 2. place the waitForReadyRead() inside your loop.

Consequence 2. waitForReadyRead() will tell you when there is data available, which could be more than one Line, so you should consequently also change the cmd2.ReadLine() to cmd2.ReadAll().

Problem 3. As documented in QProcess::closeWriteChannel()

Closing the write channel is necessary for programs that read input data until the channel has been closed.

Solution 3. One of the following options should work when finished writing your inputs

  • End the process: cmd2.write("exit\n");
  • close the Writechannel: cmd2.closeWriteChannel();

Working code:

#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QString>
#include <QProcess>    

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QTextStream qout(stdout);
    QByteArray result;
    QProcess cmd2;

    cmd2.setReadChannel(QProcess::StandardOutput);
    cmd2.setProcessChannelMode(QProcess::MergedChannels);
    cmd2.start("cmd");
    if (!cmd2.waitForStarted()){
        qout << "Error: Could not start!" << endl;
        return 0;
    }
    cmd2.write("ipconfig\n");
    cmd2.closeWriteChannel();   //done Writing

    while(cmd2.state()!=QProcess::NotRunning){
        cmd2.waitForReadyRead();
        result = cmd2.readAll();
        qout << result;
    }
    qout << endl << "---end----" << endl;
    return a.exec();
}

I wrote this answer just to explain the way I understand your problem and found a solution but would like to emphasize that the Preferable Solution is to use the Signal/Slot Mechanism as presented by Dariusz.



标签: qt qt4