PDA

View Full Version : Problem using QProcess on Windows XP



shopov
28th April 2010, 11:09
Hi, all

I am trying to write a small program that allows running command line programs and places the standard input, standard output and standard error streams in three distinct QPlainTextEdit widgets for easy editing (I am using this as a simple utility for running different command line programs, e.g. gcc, make, sed etc.). What I do is arrange when a certain action occurs (a button press or hitting enter after a command to run has been entered) to create a new QProcess, I connect slots to get called when there is data on stdout or stderr, and another slot to get notified when the process finishes. I then start() the QProcess, and after having waitForStarted(), I write what I have in the stdin QPlainTextEdit pane to the QProcess, and then I closeWriteChannel(). Here are the guts of the code:




void MainWindow::requestRun(void)
{
if (proc)
return;
proc = new QProcess(this);
connect(proc, SIGNAL(finished(int)), this, SLOT(processFinished()));
connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStdout()));
connect(proc, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStderr()));
ui->pushButtonRun->setEnabled(false);
proc->setWorkingDirectory(workdir.absolutePath());
if (ui->checkBoxMergeOutputs->isChecked())
proc->setProcessChannelMode(QProcess::MergedChannels);

proc->start(ui->comboCmdLine->currentText());

if (!proc->waitForStarted(-1))
{
ui->pushButtonRun->setEnabled(true);
QMessageBox::critical(this, "error", "error running command");
proc->deleteLater();
proc = 0;
return;
}
if (proc)
{
ui->plainTextEditStdout->clear();
proc->write(ui->plainTextEditStdin->toPlainText().toAscii().constData());
/*! \warning it is possible that the processFinished()
* slot gets invoked prior to returning from
* the QProcess::waitForBytesWritten(-1) call
* below; as processFinished() deletes the 'proc'
* variable (and resets it to a null pointer),
* the 'proc' pointer below must be checked if
* usable */
/*! \todo strange... this below - if toggled between
* 'if (1) ...' / 'if (0) ...'
* causes some thread lockup in the qt code... strange thing
* is, on three different machines it behaves in three
* different ways; summarized:
*
* if (0) proc->waitForBytesWritten(-1);
*
* - on a core2 quad machine - does not hang
* - on a core2 duo machine - does not hang
* - on a single core pentium4 - DOES hang
*
* if (1) proc->waitForBytesWritten(-1);
*
* - on a core2 quad machine - DOES hang
* - on a core2 duo machine - does not hang
* - on a single core pentium4 - does not hang
*
* test platforms:
* - core2 quad - os windows xp pro, tested with qt2009.05 and qt2010.02.1
* - core2 duo - os windows xp pro, qt2009.05
* - single core pentium4 - os windows xp home, qt2009.05
*
* when gdb is attached (via qt-creator) to the locked-up process,
* all threads seem to be waiting on events (via waitforsingleobject()),
* only one thread seems to have invoked sleepex(), but unluckily
* gdb seems unable to unwind back to the program to see the locations
* of the calls in the source code; i tried single stepping on the
* core2 quad machine, and it seems the lock-up occurs when in
* qprocess.cpp, around line 820 - in qprocessprivate::cleanup() - when
* calling 'destroyPipe(stdinChannel.pipe);' - but take this as only a
* very rought estimate, i may well be very wrong... i really did not get
* any far when trying to debug this...
*
* i guess this all is some inter-thread synchronization problem, on the
* core2 quad it seems easiest to hang the program - just paste some text
* in the left textedit pane, enter some command in the combobox - i tested with
* 'gcc' and hold down the 'enter' button so that gcc is continuously invoked;
* the program hangs almost immediately... on the core2 duo i did not manage to
* hang the program, and on the single core the program hangs when it doesn't
* on the core2 quad... go figure... */
if (1) proc->waitForBytesWritten(-1);
if (!proc)
{
}
else
{
proc->closeWriteChannel();
}
}
}

void MainWindow::processFinished(void)
{
ui->pushButtonRun->setEnabled(true);
while (!proc->atEnd())
{
//qDebug() << "waiting for end";
//Sleep(500);
}
ui->plainTextEditStderr->appendPlainText(proc->readAllStandardError());
ui->plainTextEditStdout->appendPlainText(proc->readAllStandardOutput());
proc->deleteLater();

proc->write(ui->plainTextEditStdin->toPlainText().toAscii().constData());

proc = 0;
}

void MainWindow::readyReadStdout(void)
{
ui->plainTextEditStdout->appendPlainText(proc->readAllStandardOutput());
}

void MainWindow::readyReadStderr(void)
{
ui->plainTextEditStderr->appendPlainText(proc->readAllStandardError());
}





You can find the complete project as an attachment to this message.

Now, my problem is that on three different machines this code behaves in three different ways and I am absolutely at a loss at what I could be doing wrong... The program hangs on some machines (and on some doesn't) - how to hang the program is detailed in the comments in the code above.

Any comments, advices and assistance on solving this problem are very much appreciated!

Thanks!

Stoyan Shopov

shopov
28th April 2010, 11:26
Sorry, in the code I have posted, the line just above the big comment block reads:



proc->write(ui->plainTextEditStdin->toPlainText().toAscii().constData());


But this is a non-nul terminated char * (as far as I know).

I think it should be:


proc->write(ui->plainTextEditStdin->toPlainText().toAscii());


(If you try to recompile the program, do not forget to make this change after extracting the sources.)

But the problem still persists...

squidge
28th April 2010, 12:59
Have you tried



QString tmp = ui->plainTextEditStdin->toPlainText();
proc->write(tmp.toAscii());


Otherwise you might be a victim of temporary class construction that is destroyed before your write method gets the data and this accesses invalid memory.

shopov
29th April 2010, 12:28
Have you tried



QString tmp = ui->plainTextEditStdin->toPlainText();
proc->write(tmp.toAscii());


Otherwise you might be a victim of temporary class construction that is destroyed before your write method gets the data and this accesses invalid memory.

Just tried this, unfortunately it still doesn't work... Maybe I will have to try deploying my own threads for monitoring the stdout/stderr pipes for data, and for looking if the child process has terminated and see if it works this way... But I still don't understand what the problem is - I mean, the code looks simple and innocent enough to 'just work', which it doesn't...