summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qprocess_win.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/corelib/io/qprocess_win.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/corelib/io/qprocess_win.cpp')
-rw-r--r--src/corelib/io/qprocess_win.cpp855
1 files changed, 855 insertions, 0 deletions
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
new file mode 100644
index 0000000000..625ed9853f
--- /dev/null
+++ b/src/corelib/io/qprocess_win.cpp
@@ -0,0 +1,855 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprocess.h"
+#include "qprocess_p.h"
+#include "qwindowspipewriter_p.h"
+
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <private/qwineventnotifier_p.h>
+#include <private/qthread_p.h>
+#include <qdebug.h>
+
+#include "private/qfsfileengine_p.h" // for longFileName
+
+
+#ifndef QT_NO_PROCESS
+
+QT_BEGIN_NAMESPACE
+
+//#define QPROCESS_DEBUG
+
+#define NOTIFYTIMEOUT 100
+
+static void qt_create_pipe(Q_PIPE *pipe, bool in)
+{
+ // Open the pipes. Make non-inheritable copies of input write and output
+ // read handles to avoid non-closable handles (this is done by the
+ // DuplicateHandle() call).
+
+#if !defined(Q_OS_WINCE)
+ SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
+
+ HANDLE tmpHandle;
+ if (in) { // stdin
+ if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024))
+ return;
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
+ &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS))
+ return;
+ } else { // stdout or stderr
+ if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024))
+ return;
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
+ &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS))
+ return;
+ }
+
+ CloseHandle(tmpHandle);
+#else
+ Q_UNUSED(pipe);
+ Q_UNUSED(in);
+#endif
+}
+
+/*
+ Create the pipes to a QProcessPrivate::Channel.
+
+ This function must be called in order: stdin, stdout, stderr
+*/
+bool QProcessPrivate::createChannel(Channel &channel)
+{
+ Q_Q(QProcess);
+
+ if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
+ return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
+ &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
+ }
+
+ if (channel.type == Channel::Normal) {
+ // we're piping this channel to our own process
+ qt_create_pipe(channel.pipe, &channel == &stdinChannel);
+
+ return true;
+ } else if (channel.type == Channel::Redirect) {
+ // we're redirecting the channel to/from a file
+ SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+
+ if (&channel == &stdinChannel) {
+ // try to open in read-only mode
+ channel.pipe[1] = INVALID_Q_PIPE;
+ channel.pipe[0] =
+ CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (channel.pipe[0] != INVALID_Q_PIPE)
+ return true;
+
+ q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
+ } else {
+ // open in write mode
+ channel.pipe[0] = INVALID_Q_PIPE;
+ channel.pipe[1] =
+ CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (channel.pipe[1] != INVALID_Q_PIPE) {
+ if (channel.append) {
+ SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
+ }
+ return true;
+ }
+
+ q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
+ }
+
+ // could not open file
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+ cleanup();
+ return false;
+ } else {
+ Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
+
+ Channel *source;
+ Channel *sink;
+
+ if (channel.type == Channel::PipeSource) {
+ // we are the source
+ source = &channel;
+ sink = &channel.process->stdinChannel;
+
+ if (source->pipe[1] != INVALID_Q_PIPE) {
+ // already constructed by the sink
+ // make it inheritable
+ HANDLE tmpHandle = source->pipe[1];
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
+ GetCurrentProcess(), &source->pipe[1],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ return false;
+
+ CloseHandle(tmpHandle);
+ return true;
+ }
+
+ Q_ASSERT(source == &stdoutChannel);
+ Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
+
+ qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
+ sink->pipe[0] = source->pipe[0];
+ source->pipe[0] = INVALID_Q_PIPE;
+
+ return true;
+ } else {
+ // we are the sink;
+ source = &channel.process->stdoutChannel;
+ sink = &channel;
+
+ if (sink->pipe[0] != INVALID_Q_PIPE) {
+ // already constructed by the source
+ // make it inheritable
+ HANDLE tmpHandle = sink->pipe[0];
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
+ GetCurrentProcess(), &sink->pipe[0],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ return false;
+
+ CloseHandle(tmpHandle);
+ return true;
+ }
+ Q_ASSERT(sink == &stdinChannel);
+ Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
+
+ qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
+ source->pipe[1] = sink->pipe[1];
+ sink->pipe[1] = INVALID_Q_PIPE;
+
+ return true;
+ }
+ }
+}
+
+void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
+{
+ if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) {
+ delete pipeWriter;
+ pipeWriter = 0;
+ }
+
+ if (pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(pipe[0]);
+ pipe[0] = INVALID_Q_PIPE;
+ }
+ if (pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(pipe[1]);
+ pipe[1] = INVALID_Q_PIPE;
+ }
+}
+
+
+static QString qt_create_commandline(const QString &program, const QStringList &arguments)
+{
+ QString args;
+ if (!program.isEmpty()) {
+ QString programName = program;
+ if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
+ programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
+ programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
+
+ // add the prgram as the first arg ... it works better
+ args = programName + QLatin1Char(' ');
+ }
+
+ for (int i=0; i<arguments.size(); ++i) {
+ QString tmp = arguments.at(i);
+ // in the case of \" already being in the string the \ must also be escaped
+ tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") );
+ // escape a single " because the arguments will be parsed
+ tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") );
+ if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ QString endQuote(QLatin1Char('\"'));
+ int i = tmp.length();
+ while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) {
+ --i;
+ endQuote += QLatin1Char('\\');
+ }
+ args += QLatin1String(" \"") + tmp.left(i) + endQuote;
+ } else {
+ args += QLatin1Char(' ') + tmp;
+ }
+ }
+ return args;
+}
+
+static QByteArray qt_create_environment(const QHash<QString, QString> &environment)
+{
+ QByteArray envlist;
+ if (!environment.isEmpty()) {
+ QHash<QString, QString> copy = environment;
+
+ // add PATH if necessary (for DLL loading)
+ if (!copy.contains(QLatin1String("PATH"))) {
+ QByteArray path = qgetenv("PATH");
+ if (!path.isEmpty())
+ copy.insert(QLatin1String("PATH"), QString::fromLocal8Bit(path));
+ }
+
+ // add systemroot if needed
+ if (!copy.contains(QLatin1String("SYSTEMROOT"))) {
+ QByteArray systemRoot = qgetenv("SYSTEMROOT");
+ if (!systemRoot.isEmpty())
+ copy.insert(QLatin1String("SYSTEMROOT"), QString::fromLocal8Bit(systemRoot));
+ }
+
+ int pos = 0;
+ QHash<QString, QString>::ConstIterator it = copy.constBegin(),
+ end = copy.constEnd();
+
+ static const wchar_t equal = L'=';
+ static const wchar_t nul = L'\0';
+
+ for ( ; it != end; ++it) {
+ uint tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
+ // ignore empty strings
+ if (tmpSize == sizeof(wchar_t) * 2)
+ continue;
+ envlist.resize(envlist.size() + tmpSize);
+
+ tmpSize = it.key().length() * sizeof(wchar_t);
+ memcpy(envlist.data()+pos, it.key().utf16(), tmpSize);
+ pos += tmpSize;
+
+ memcpy(envlist.data()+pos, &equal, sizeof(wchar_t));
+ pos += sizeof(wchar_t);
+
+ tmpSize = it.value().length() * sizeof(wchar_t);
+ memcpy(envlist.data()+pos, it.value().utf16(), tmpSize);
+ pos += tmpSize;
+
+ memcpy(envlist.data()+pos, &nul, sizeof(wchar_t));
+ pos += sizeof(wchar_t);
+ }
+ // add the 2 terminating 0 (actually 4, just to be on the safe side)
+ envlist.resize( envlist.size()+4 );
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ }
+ return envlist;
+}
+
+void QProcessPrivate::startProcess()
+{
+ Q_Q(QProcess);
+
+ bool success = false;
+
+ if (pid) {
+ CloseHandle(pid->hThread);
+ CloseHandle(pid->hProcess);
+ delete pid;
+ pid = 0;
+ }
+ pid = new PROCESS_INFORMATION;
+ memset(pid, 0, sizeof(PROCESS_INFORMATION));
+
+ q->setProcessState(QProcess::Starting);
+
+ if (!createChannel(stdinChannel) ||
+ !createChannel(stdoutChannel) ||
+ !createChannel(stderrChannel))
+ return;
+
+#if defined(Q_OS_WINCE)
+ QString args = qt_create_commandline(QString(), arguments);
+#else
+ QString args = qt_create_commandline(program, arguments);
+ QByteArray envlist;
+ if (environment.d.constData())
+ envlist = qt_create_environment(environment.d.constData()->hash);
+#endif
+ if (!nativeArguments.isEmpty()) {
+ if (!args.isEmpty())
+ args += QLatin1Char(' ');
+ args += nativeArguments;
+ }
+
+#if defined QPROCESS_DEBUG
+ qDebug("Creating process");
+ qDebug(" program : [%s]", program.toLatin1().constData());
+ qDebug(" args : %s", args.toLatin1().constData());
+ qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
+#endif
+
+#if defined(Q_OS_WINCE)
+ QString fullPathProgram = program;
+ if (!QDir::isAbsolutePath(fullPathProgram))
+ fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
+ fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
+ (wchar_t*)args.utf16(),
+ 0, 0, false, 0, 0, 0, 0, pid);
+#else
+ DWORD dwCreationFlags = CREATE_NO_WINDOW;
+ dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
+ STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
+ };
+ success = CreateProcess(0, (wchar_t*)args.utf16(),
+ 0, 0, TRUE, dwCreationFlags,
+ environment.isEmpty() ? 0 : envlist.data(),
+ workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(),
+ &startupInfo, pid);
+ if (!success) {
+ // Capture the error string before we do CloseHandle below
+ q->setErrorString(QProcess::tr("Process failed to start: %1").arg(qt_error_string()));
+ }
+
+ if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = INVALID_Q_PIPE;
+ }
+ if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+#endif // Q_OS_WINCE
+
+ if (!success) {
+ cleanup();
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+ q->setProcessState(QProcess::NotRunning);
+ return;
+ }
+
+ q->setProcessState(QProcess::Running);
+ // User can call kill()/terminate() from the stateChanged() slot
+ // so check before proceeding
+ if (!pid)
+ return;
+
+ if (threadData->eventDispatcher) {
+ processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
+ QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
+ processFinishedNotifier->setEnabled(true);
+ notifier = new QTimer(q);
+ QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
+ notifier->start(NOTIFYTIMEOUT);
+ }
+
+ // give the process a chance to start ...
+ Sleep(SLEEPMIN * 2);
+ _q_startupNotification();
+}
+
+bool QProcessPrivate::processStarted()
+{
+ return processState == QProcess::Running;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStdout() const
+{
+ if (stdoutChannel.pipe[0] == INVALID_Q_PIPE)
+ return 0;
+
+ DWORD bytesAvail = 0;
+#if !defined(Q_OS_WINCE)
+ PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
+#endif
+ if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
+ QByteArray buf(bytesAvail, 0);
+ DWORD bytesRead = 0;
+ if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
+ HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdout) {
+ DWORD bytesWritten = 0;
+ WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0);
+ }
+ }
+ bytesAvail = 0;
+ }
+#endif
+ return bytesAvail;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStderr() const
+{
+ if (stderrChannel.pipe[0] == INVALID_Q_PIPE)
+ return 0;
+
+ DWORD bytesAvail = 0;
+#if !defined(Q_OS_WINCE)
+ PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
+#endif
+ if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
+ QByteArray buf(bytesAvail, 0);
+ DWORD bytesRead = 0;
+ if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
+ HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
+ if (hStderr) {
+ DWORD bytesWritten = 0;
+ WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0);
+ }
+ }
+ bytesAvail = 0;
+ }
+#endif
+ return bytesAvail;
+}
+
+qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
+{
+ DWORD read = qMin(maxlen, bytesAvailableFromStdout());
+ DWORD bytesRead = 0;
+
+ if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0))
+ return -1;
+ return bytesRead;
+}
+
+qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
+{
+ DWORD read = qMin(maxlen, bytesAvailableFromStderr());
+ DWORD bytesRead = 0;
+
+ if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0))
+ return -1;
+ return bytesRead;
+}
+
+
+static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
+{
+ DWORD currentProcId = 0;
+ GetWindowThreadProcessId(hwnd, &currentProcId);
+ if (currentProcId == (DWORD)procId)
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+
+ return TRUE;
+}
+
+void QProcessPrivate::terminateProcess()
+{
+ if (pid) {
+ EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
+ PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
+ }
+}
+
+void QProcessPrivate::killProcess()
+{
+ if (pid)
+ TerminateProcess(pid->hProcess, 0xf291);
+}
+
+bool QProcessPrivate::waitForStarted(int)
+{
+ Q_Q(QProcess);
+
+ if (processStarted())
+ return true;
+
+ if (processError == QProcess::FailedToStart)
+ return false;
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+bool QProcessPrivate::waitForReadyRead(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+ return false;
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ if (!writeBuffer.isEmpty() && !_q_canWrite())
+ return false;
+ if (pipeWriter && pipeWriter->waitForWrite(0))
+ timer.resetIncrements();
+ bool readyReadEmitted = false;
+ if (bytesAvailableFromStdout() != 0) {
+ readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted;
+ timer.resetIncrements();
+ }
+
+ if (bytesAvailableFromStderr() != 0) {
+ readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted;
+ timer.resetIncrements();
+ }
+
+ if (readyReadEmitted)
+ return true;
+
+ if (!pid)
+ return false;
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
+ // find the return value if there is noew data to read
+ _q_processDied();
+ return false;
+ }
+
+ Sleep(timer.nextSleepTime());
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+bool QProcessPrivate::waitForBytesWritten(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+ return false;
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ // Check if we have any data pending: the pipe writer has
+ // bytes waiting to written, or it has written data since the
+ // last time we called pipeWriter->waitForWrite().
+ bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
+
+ // If we don't have pending data, and our write buffer is
+ // empty, we fail.
+ if (!pendingDataInPipe && writeBuffer.isEmpty())
+ return false;
+
+ // If we don't have pending data and we do have data in our
+ // write buffer, try to flush that data over to the pipe
+ // writer. Fail on error.
+ if (!pendingDataInPipe) {
+ if (!_q_canWrite())
+ return false;
+ }
+
+ // Wait for the pipe writer to acknowledge that it has
+ // written. This will succeed if either the pipe writer has
+ // already written the data, or if it manages to write data
+ // within the given timeout. If the write buffer was non-empty
+ // and the pipeWriter is now dead, that means _q_canWrite()
+ // destroyed the writer after it successfully wrote the last
+ // batch.
+ if (!pipeWriter || pipeWriter->waitForWrite(0))
+ return true;
+
+ // If we wouldn't write anything, check if we can read stdout.
+ if (bytesAvailableFromStdout() != 0) {
+ _q_canReadStandardOutput();
+ timer.resetIncrements();
+ }
+
+ // Check if we can read stderr.
+ if (bytesAvailableFromStderr() != 0) {
+ _q_canReadStandardError();
+ timer.resetIncrements();
+ }
+
+ // Check if the process died while reading.
+ if (!pid)
+ return false;
+
+ // Wait for the process to signal any change in its state,
+ // such as incoming data, or if the process died.
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
+ _q_processDied();
+ return false;
+ }
+
+ // Only wait for as long as we've been asked.
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+
+bool QProcessPrivate::waitForFinished(int msecs)
+{
+ Q_Q(QProcess);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ if (!writeBuffer.isEmpty() && !_q_canWrite())
+ return false;
+ if (pipeWriter && pipeWriter->waitForWrite(0))
+ timer.resetIncrements();
+
+ if (bytesAvailableFromStdout() != 0) {
+ _q_canReadStandardOutput();
+ timer.resetIncrements();
+ }
+
+ if (bytesAvailableFromStderr() != 0) {
+ _q_canReadStandardError();
+ timer.resetIncrements();
+ }
+
+ if (!pid)
+ return true;
+
+ if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
+ _q_processDied();
+ return true;
+ }
+
+ if (timer.hasTimedOut())
+ break;
+ }
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+
+void QProcessPrivate::findExitCode()
+{
+ DWORD theExitCode;
+ if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
+ exitCode = theExitCode;
+ //### for now we assume a crash if exit code is less than -1 or the magic number
+ crashed = (exitCode == 0xf291 || (int)exitCode < 0);
+ }
+}
+
+void QProcessPrivate::flushPipeWriter()
+{
+ if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
+ pipeWriter->waitForWrite(ULONG_MAX);
+ }
+}
+
+qint64 QProcessPrivate::pipeWriterBytesToWrite() const
+{
+ return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
+}
+
+qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::WriteError;
+ q->setErrorString(QProcess::tr("Error writing to process"));
+ emit q->error(processError);
+ return -1;
+#endif
+
+ if (!pipeWriter) {
+ pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
+ pipeWriter->start();
+ }
+
+ return pipeWriter->write(data, maxlen);
+}
+
+bool QProcessPrivate::waitForWrite(int msecs)
+{
+ Q_Q(QProcess);
+
+ if (!pipeWriter || pipeWriter->waitForWrite(msecs))
+ return true;
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+void QProcessPrivate::_q_notified()
+{
+ notifier->stop();
+
+ if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0)))
+ _q_canWrite();
+
+ if (bytesAvailableFromStdout())
+ _q_canReadStandardOutput();
+
+ if (bytesAvailableFromStderr())
+ _q_canReadStandardError();
+
+ if (processState != QProcess::NotRunning)
+ notifier->start(NOTIFYTIMEOUT);
+}
+
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(workingDir);
+ QString args = qt_create_commandline(QString(), arguments);
+#else
+ QString args = qt_create_commandline(program, arguments);
+#endif
+
+ bool success = false;
+
+ PROCESS_INFORMATION pinfo;
+
+#if defined(Q_OS_WINCE)
+ QString fullPathProgram = program;
+ if (!QDir::isAbsolutePath(fullPathProgram))
+ fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/')));
+ fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
+ (wchar_t*)args.utf16(),
+ 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
+#else
+ STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ success = CreateProcess(0, (wchar_t*)args.utf16(),
+ 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
+ workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
+ &startupInfo, &pinfo);
+#endif // Q_OS_WINCE
+
+ if (success) {
+ CloseHandle(pinfo.hThread);
+ CloseHandle(pinfo.hProcess);
+ if (pid)
+ *pid = pinfo.dwProcessId;
+ }
+
+ return success;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS