7

Parent process write string "Message\n" to child process stdin. But child process don't receive it. Where is the problem in the code?

Qt 4.7.3

Parent process code:

// class TestParent : public QMainWindow
void TestParent::createChildProcess()
{
    childProcess = new QProcess( this );
    connect( childProcess, SIGNAL( started() ),
        this, SLOT( childProcessStarted() ) );
    connect( childProcess, SIGNAL( bytesWritten( qint64 ) ),
        this, SLOT( bytesWritten( qint64 ) ) );
    childProcess->start( "TestChild.exe", QProcess::ReadWrite );
}

void TestParent::writeToChildProcessOutput()
{
    qint64 bytesWritten = childProcess->write( "Message\n" );
    qDebug() << "ret: " << bytesWritten << " bytes written";
}

void TestParent::bytesWritten()
{
    qDebug() << "slot: " << bytesWritten << " bytes written";
}

Child process code:

// class TestChild : public QMainWindow
void TestChild::TestChild()
    // QFile TestChild::input;
    connect( &input, SIGNAL( readyRead() ),
        this, SLOT( readInput() ) );
    input.open( 0, QIODevice::ReadOnly ); // stdin
}

void TestChild::readInput()
{
    QString line;
    line.append( '(' );
    line.append( QString::number( input.bytesAvailable() ) )
    line.append( ')' );
    line.append( input.readAll() );

    list.append( line ); // add line to QListView
}
artyom.stv
  • 2,097
  • 1
  • 24
  • 42
  • Is TestChild a QProcess? and input is a QIODevice, not a subclass? – Chris Sep 17 '11 at 22:14
  • @Chris: No, TestChild - main window object of child process. In parent process we create child one using `childProcess` object. My mistake: `input` is QFile, thanks. – artyom.stv Sep 17 '11 at 22:23

3 Answers3

7

There is no way to portably hookup in the Qt event loop for stdin/stdout events. The following works on non-Windows platforms:

QSocketNotifier *n1 = new QSocketNotifier(0, QSocketNotifier::Read, this);
connect(n1, SIGNAL(activated(int)), this, SLOT(readInputChannel()));

QSocketNotifier *n2 = new QSocketNotifier(0, QSocketNotifier::Exception, this);
connect(n2, SIGNAL(activated(int)), this, SLOT(brokenInputChannel()));

"0" is the file descriptor (stdin).

I would use the above and then simulate something similar on Windows through a blocking thread that reads from stdin and generates a signal:

class StdinThread : public QThread
{
    Q_OBJECT
signals:
    void incomingData(QByteArray data);

public:
    void run(void)
    {
         char buf[1024];
         while (1)
         {
             int sz = fread(buf, 1, 1024, stdin);
             if (sz == 0)
                return;
             emit incomingData(QByteArray(buf, sz));
         }
     }
};

then, in the child process:

StdinThread *t = new StdinThread(this);
connect(t, SIGNAL(incomingData(QByteArray)), this, SLOT(processInputData(QByteArray)));
connect(t, SIGNAL(finished()), this, SLOT(brokenInputChannel()));
t->run();
Giovanni Bajo
  • 1,337
  • 9
  • 15
  • +1. That is helpful, thanks. Don't you know whether `QProcess::write` (stdin piping) works on Windows? In another words: can I write to child process's stdin? – artyom.stv Sep 28 '11 at 20:31
  • Yes you can. Windows supports piping. Just pay attention to buffering. – Giovanni Bajo Sep 29 '11 at 00:09
1

The documentation says that QFile doesn't ever emit the signal readyRead().

But there is a private class: QWinEventNotifier in src/corelib/kernel/qwineventnotifier_p.h(link) that might work with GetStdHandle(STD_INPUT_HANDLE).

Another alternative is to wait for input with a blocking loop inside a dedicated thread:

QTextStream cin(stdin, QIODevice::ReadOnly);
while(!cin.atEnd())
{
   QString line = cin.readLine();
   emit newLine(line);
}

You could also look at other RPC methods (e.g. QLocalSocket, QSharedMemory).

alexisdm
  • 29,448
  • 6
  • 64
  • 99
  • Thanks, will try QWinEventNotifier. But the problem still exists: I have started a timer which each X ms writes to log-file `input.bytesAvailable()`, and it always writes `0`. Is `bytesAvailable()` usage here incorrect too? – artyom.stv Sep 18 '11 at 17:58
  • `QSharedMemory` didn't try. But about `QLocalSocket`, `QLocalServer` (named pipe in Windows): I still have to exchange pipe names (1 for each `QLocalServer`). Is there any other simple method to send pipe name, other than stdin/stdout? – artyom.stv Sep 18 '11 at 18:00
  • 1
    1. You can't peek in stdin buffer with C/C++ standard libraries, so it wouldn't be a surprise if Qt doesn't allow it either. 2. You can send the pipe name as a sub-process arguments. – alexisdm Sep 18 '11 at 18:20
  • And what should I use for child->parent communication? Still use stdout? – artyom.stv Sep 18 '11 at 18:25
  • "You can then write to the process's standard input by calling write(), and read the standard output by calling read(), readLine(), and getChar()." (http://doc.qt.nokia.com/latest/qprocess.html) – artyom.stv Sep 19 '11 at 21:36
  • Yes, you can use stdout but QLocalSocket can be bidirectional so you don't have mix both communication methods. The problem is not that you can't use read/write with the standard input/output of the child process, but that bytesAvailable() on the child process side, doesn't work for stdin. You'd have to use the native API (for example [here](http://www.google.com/codesearch#pFm0LxzAWvs/darwinsource/tarballs/other/Chess-2.1.tar.gz%7CTOs7YXXXh-Q/Chess-2.1/sjeng/utils.c&q=GetStdHandle%20PeekNamedPipe%20-winbase.h&type=cs&l=1017)) otherwise you'd have to use a **blocking** read call. – alexisdm Sep 20 '11 at 03:24
0

What I believe you need to be doing is reading from the actual QProcess object (in this case childProcess, if I understand correctly).

Note that QProcess is actually a subclass of QIODevice, and also take special note of the following functions:

QProcess::setReadChannel()
QProcess::readAllStandardOutput()

You should be able to the readyRead() signal of your QProcess object instead of creating a QFile in an attempt to read stdin. The reason this doesn't work is that your QFile is associated with the same process as your MainWindow, and not your child process.

Chris
  • 17,119
  • 5
  • 57
  • 60
  • No. `childProcess` is a control object in parent process. Using `childProcess` object parent process print string to child process's `stdin` (`TestParent::writeToChildProcessOutput()`). And child process should read that string from `stdin`. So in child process `readyRead()` signal of `QFile input` object should be emitted (but there is no `readyRead()` signal emitted, `TestChild::readInput()` slot is not called). – artyom.stv Sep 17 '11 at 23:17