src/corelib/io/qprocess_win.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/io/qprocess_win.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,850 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 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 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