diff options
Diffstat (limited to 'src/corelib/io/qprocess_win.cpp')
-rw-r--r-- | src/corelib/io/qprocess_win.cpp | 499 |
1 files changed, 263 insertions, 236 deletions
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 0088284d2d..e64b133815 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -1,44 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2017 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2017 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //#define QPROCESS_DEBUG +#include <qdebug.h> +#include <private/qdebug_p.h> + #include "qprocess.h" #include "qprocess_p.h" #include "qwindowspipereader_p.h" @@ -49,9 +16,9 @@ #include <qfileinfo.h> #include <qrandom.h> #include <qwineventnotifier.h> +#include <qscopedvaluerollback.h> #include <private/qsystemlibrary_p.h> #include <private/qthread_p.h> -#include <qdebug.h> #include "private/qfsfileengine_p.h" // for longFileName @@ -61,6 +28,10 @@ QT_BEGIN_NAMESPACE +constexpr UINT KillProcessExitCode = 0xf291; + +using namespace Qt::StringLiterals; + QProcessEnvironment QProcessEnvironment::systemEnvironment() { QProcessEnvironment env; @@ -85,6 +56,46 @@ QProcessEnvironment QProcessEnvironment::systemEnvironment() #if QT_CONFIG(process) +namespace { +struct QProcessPoller +{ + QProcessPoller(const QProcessPrivate &proc); + + int poll(const QDeadlineTimer &deadline); + + enum { maxHandles = 4 }; + HANDLE handles[maxHandles]; + DWORD handleCount = 0; +}; + +QProcessPoller::QProcessPoller(const QProcessPrivate &proc) +{ + if (proc.stdinChannel.writer) + handles[handleCount++] = proc.stdinChannel.writer->syncEvent(); + if (proc.stdoutChannel.reader) + handles[handleCount++] = proc.stdoutChannel.reader->syncEvent(); + if (proc.stderrChannel.reader) + handles[handleCount++] = proc.stderrChannel.reader->syncEvent(); + + handles[handleCount++] = proc.pid->hProcess; +} + +int QProcessPoller::poll(const QDeadlineTimer &deadline) +{ + DWORD waitRet; + + do { + waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE, + deadline.remainingTime(), TRUE); + } while (waitRet == WAIT_IO_COMPLETION); + + if (waitRet - WAIT_OBJECT_0 < handleCount) + return 1; + + return (waitRet == WAIT_TIMEOUT) ? 0 : -1; +} +} // anonymous namespace + static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag) { // Anomymous pipes do not support asynchronous I/O. Thus we @@ -129,6 +140,7 @@ static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag) DWORD dwError = GetLastError(); if (dwError != ERROR_PIPE_BUSY || !--attempts) { qErrnoWarning(dwError, "QProcess: CreateNamedPipe failed."); + SetLastError(dwError); return false; } } @@ -143,8 +155,10 @@ static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag) FILE_FLAG_OVERLAPPED, NULL); if (hClient == INVALID_HANDLE_VALUE) { + DWORD dwError = GetLastError(); qErrnoWarning("QProcess: CreateFile failed."); CloseHandle(hServer); + SetLastError(dwError); return false; } @@ -161,10 +175,12 @@ static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag) WaitForSingleObject(overlapped.hEvent, INFINITE); break; default: + dwError = GetLastError(); qErrnoWarning(dwError, "QProcess: ConnectNamedPipe failed."); CloseHandle(overlapped.hEvent); CloseHandle(hClient); CloseHandle(hServer); + SetLastError(dwError); return false; } } @@ -190,8 +206,13 @@ bool QProcessPrivate::openChannel(Channel &channel) switch (channel.type) { case Channel::Normal: { // we're piping this channel to our own process - if (&channel == &stdinChannel) - return qt_create_pipe(channel.pipe, true, FALSE); + if (&channel == &stdinChannel) { + if (!qt_create_pipe(channel.pipe, true, FALSE)) { + setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno)); + return false; + } + return true; + } if (&channel == &stdoutChannel) { if (!stdoutChannel.reader) { @@ -204,8 +225,10 @@ bool QProcessPrivate::openChannel(Channel &channel) q->connect(stderrChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError())); } } - if (!qt_create_pipe(channel.pipe, false, FALSE)) + if (!qt_create_pipe(channel.pipe, false, FALSE)) { + setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno)); return false; + } channel.reader->setHandle(channel.pipe[0]); channel.reader->startAsyncRead(); @@ -254,7 +277,6 @@ bool QProcessPrivate::openChannel(Channel &channel) setErrorAndEmit(QProcess::FailedToStart, QProcess::tr("Could not open output redirection for writing")); } - cleanup(); return false; } case Channel::PipeSource: { @@ -271,8 +293,10 @@ bool QProcessPrivate::openChannel(Channel &channel) Q_ASSERT(source == &stdoutChannel); Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); - if (!qt_create_pipe(source->pipe, /* in = */ false, TRUE)) // source is stdout + if (!qt_create_pipe(source->pipe, /* in = */ false, TRUE)) { // source is stdout + setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno)); return false; + } sink->pipe[0] = source->pipe[0]; source->pipe[0] = INVALID_Q_PIPE; @@ -291,8 +315,10 @@ bool QProcessPrivate::openChannel(Channel &channel) Q_ASSERT(sink == &stdinChannel); Q_ASSERT(source->process == this && source->type == Channel::PipeSource); - if (!qt_create_pipe(sink->pipe, /* in = */ true, TRUE)) // sink is stdin + if (!qt_create_pipe(sink->pipe, /* in = */ true, TRUE)) { // sink is stdin + setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno)); return false; + } source->pipe[1] = sink->pipe[1]; sink->pipe[1] = INVALID_Q_PIPE; @@ -315,23 +341,31 @@ void QProcessPrivate::destroyPipe(Q_PIPE pipe[2]) } } -template <class T> -void deleteWorker(T *&worker) +void QProcessPrivate::closeChannel(Channel *channel) { - if (!worker) - return; - worker->stop(); - worker->deleteLater(); - worker = nullptr; + if (channel == &stdinChannel) { + delete channel->writer; + channel->writer = nullptr; + } else { + delete channel->reader; + channel->reader = nullptr; + } + destroyPipe(channel->pipe); } -void QProcessPrivate::closeChannel(Channel *channel) +void QProcessPrivate::cleanup() { - if (channel == &stdinChannel) - deleteWorker(channel->writer); - else - deleteWorker(channel->reader); - destroyPipe(channel->pipe); + q_func()->setProcessState(QProcess::NotRunning); + + closeChannels(); + delete processFinishedNotifier; + processFinishedNotifier = nullptr; + if (pid) { + CloseHandle(pid->hThread); + CloseHandle(pid->hProcess); + delete pid; + pid = nullptr; + } } static QString qt_create_commandline(const QString &program, const QStringList &arguments, @@ -340,44 +374,44 @@ static QString qt_create_commandline(const QString &program, const QStringList & 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('\\')); + if (!programName.startsWith(u'\"') && !programName.endsWith(u'\"') && programName.contains(u' ')) + programName = u'\"' + programName + u'\"'; + programName.replace(u'/', u'\\'); - // add the prgram as the first arg ... it works better - args = programName + QLatin1Char(' '); + // add the program as the first arg ... it works better + args = programName + u' '; } - for (int i=0; i<arguments.size(); ++i) { + for (qsizetype i = 0; i < arguments.size(); ++i) { QString tmp = arguments.at(i); // Quotes are escaped and their preceding backslashes are doubled. - int index = tmp.indexOf(QLatin1Char('"')); + qsizetype index = tmp.indexOf(u'"'); while (index >= 0) { // Escape quote - tmp.insert(index++, QLatin1Char('\\')); + tmp.insert(index++, u'\\'); // Double preceding backslashes (ignoring the one we just inserted) - for (int i = index - 2 ; i >= 0 && tmp.at(i) == QLatin1Char('\\') ; --i) { - tmp.insert(i, QLatin1Char('\\')); + for (qsizetype i = index - 2 ; i >= 0 && tmp.at(i) == u'\\' ; --i) { + tmp.insert(i, u'\\'); index++; } - index = tmp.indexOf(QLatin1Char('"'), index + 1); + index = tmp.indexOf(u'"', index + 1); } - if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { + if (tmp.isEmpty() || tmp.contains(u' ') || tmp.contains(u'\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\" - int i = tmp.length(); - while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) + qsizetype i = tmp.length(); + while (i > 0 && tmp.at(i - 1) == u'\\') --i; - tmp.insert(i, QLatin1Char('"')); - tmp.prepend(QLatin1Char('"')); + tmp.insert(i, u'"'); + tmp.prepend(u'"'); } - args += QLatin1Char(' ') + tmp; + args += u' ' + tmp; } if (!nativeArguments.isEmpty()) { if (!args.isEmpty()) - args += QLatin1Char(' '); + args += u' '; args += nativeArguments; } @@ -387,60 +421,59 @@ static QString qt_create_commandline(const QString &program, const QStringList & static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &environment) { QByteArray envlist; - if (!environment.isEmpty()) { - QProcessEnvironmentPrivate::Map copy = environment; - - // add PATH if necessary (for DLL loading) - QProcessEnvironmentPrivate::Key pathKey(QLatin1String("PATH")); - if (!copy.contains(pathKey)) { - QByteArray path = qgetenv("PATH"); - if (!path.isEmpty()) - copy.insert(pathKey, QString::fromLocal8Bit(path)); - } + QProcessEnvironmentPrivate::Map copy = environment; - // add systemroot if needed - QProcessEnvironmentPrivate::Key rootKey(QLatin1String("SystemRoot")); - if (!copy.contains(rootKey)) { - QByteArray systemRoot = qgetenv("SystemRoot"); - if (!systemRoot.isEmpty()) - copy.insert(rootKey, QString::fromLocal8Bit(systemRoot)); - } + // add PATH if necessary (for DLL loading) + QProcessEnvironmentPrivate::Key pathKey("PATH"_L1); + if (!copy.contains(pathKey)) { + QByteArray path = qgetenv("PATH"); + if (!path.isEmpty()) + copy.insert(pathKey, QString::fromLocal8Bit(path)); + } - qsizetype pos = 0; - auto it = copy.constBegin(); - const auto end = copy.constEnd(); + // add systemroot if needed + QProcessEnvironmentPrivate::Key rootKey("SystemRoot"_L1); + if (!copy.contains(rootKey)) { + QByteArray systemRoot = qgetenv("SystemRoot"); + if (!systemRoot.isEmpty()) + copy.insert(rootKey, QString::fromLocal8Bit(systemRoot)); + } - static const wchar_t equal = L'='; - static const wchar_t nul = L'\0'; + qsizetype pos = 0; + auto it = copy.constBegin(); + const auto end = copy.constEnd(); - for ( ; it != end; ++it) { - qsizetype 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); + static const wchar_t equal = L'='; + static const wchar_t nul = L'\0'; - tmpSize = it.key().length() * sizeof(wchar_t); - memcpy(envlist.data()+pos, it.key().utf16(), tmpSize); - pos += tmpSize; + for (; it != end; ++it) { + qsizetype 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); - memcpy(envlist.data()+pos, &equal, sizeof(wchar_t)); - pos += sizeof(wchar_t); + tmpSize = it.key().length() * sizeof(wchar_t); + memcpy(envlist.data() + pos, it.key().data(), tmpSize); + pos += tmpSize; - tmpSize = it.value().length() * sizeof(wchar_t); - memcpy(envlist.data()+pos, it.value().utf16(), tmpSize); - pos += tmpSize; + memcpy(envlist.data() + pos, &equal, sizeof(wchar_t)); + pos += sizeof(wchar_t); - 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; + tmpSize = it.value().length() * sizeof(wchar_t); + memcpy(envlist.data() + pos, it.value().data(), 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; } @@ -499,30 +532,21 @@ 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 (!openChannels()) { - QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string()); + // openChannel sets the error string + Q_ASSERT(!errorString.isEmpty()); cleanup(); - setErrorAndEmit(QProcess::FailedToStart, errorString); - q->setProcessState(QProcess::NotRunning); return; } const QString args = qt_create_commandline(program, arguments, nativeArguments); QByteArray envlist; - if (environment.d.constData()) + if (!environment.inheritsFromParent()) envlist = qt_create_environment(environment.d.constData()->vars); #if defined QPROCESS_DEBUG @@ -544,26 +568,26 @@ void QProcessPrivate::startProcess() QProcess::CreateProcessArguments cpargs = { nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())), nullptr, nullptr, true, dwCreationFlags, - environment.isEmpty() ? nullptr : envlist.data(), + environment.inheritsFromParent() ? nullptr : envlist.data(), nativeWorkingDirectory.isEmpty() ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()), &startupInfo, pid }; - success = callCreateProcess(&cpargs); - QString errorString; - if (!success) { + if (!callCreateProcess(&cpargs)) { // Capture the error string before we do CloseHandle below - errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string()); - } - - if (!success) { + QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string()); cleanup(); setErrorAndEmit(QProcess::FailedToStart, errorString); - q->setProcessState(QProcess::NotRunning); return; } + // The pipe writer may have already been created before we had + // the pipe handle, specifically if the user wrote data from the + // stateChanged() slot. + if (stdinChannel.writer) + stdinChannel.writer->setHandle(stdinChannel.pipe[1]); + q->setProcessState(QProcess::Running); // User can call kill()/terminate() from the stateChanged() slot // so check before proceeding @@ -625,7 +649,7 @@ void QProcessPrivate::terminateProcess() void QProcessPrivate::killProcess() { if (pid) - TerminateProcess(pid->hProcess, 0xf291); + TerminateProcess(pid->hProcess, KillProcessExitCode); } bool QProcessPrivate::waitForStarted(const QDeadlineTimer &) @@ -658,30 +682,30 @@ bool QProcessPrivate::drainOutputPipes() bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline) { - QIncrementalSleepTimer timer(deadline.remainingTime()); - forever { - if (!writeBuffer.isEmpty() && !_q_canWrite()) + QProcessPoller poller(*this); + int ret = poller.poll(deadline); + if (ret < 0) return false; - if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0)) - timer.resetIncrements(); + if (ret == 0) + break; + + if (stdinChannel.writer) + stdinChannel.writer->checkForWrite(); - if ((stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0)) - || (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))) + if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead()) + || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead())) return true; if (!pid) return false; - if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) { + + if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { bool readyReadEmitted = drainOutputPipes(); if (pid) processFinished(); return readyReadEmitted; } - - Sleep(timer.nextSleepTime()); - if (timer.hasTimedOut()) - break; } setError(QProcess::Timedout); @@ -690,58 +714,43 @@ bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline) bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline) { - QIncrementalSleepTimer timer(deadline.remainingTime()); - forever { - bool pendingDataInPipe = stdinChannel.writer && stdinChannel.writer->bytesToWrite(); - - // If we don't have pending data, and our write buffer is - // empty, we fail. - if (!pendingDataInPipe && writeBuffer.isEmpty()) + // At entry into the loop the pipe writer's buffer can be empty to + // start with, in which case we fail immediately. Also, if the input + // pipe goes down somewhere in the code below, we avoid waiting for + // a full timeout. + if (!stdinChannel.writer || !stdinChannel.writer->isWriteOperationActive()) 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; - } + QProcessPoller poller(*this); + int ret = poller.poll(deadline); + if (ret < 0) + return false; + if (ret == 0) + break; - // 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 stdinChannel.writer is now dead, that means _q_canWrite() - // destroyed the writer after it successfully wrote the last - // batch. - if (!stdinChannel.writer || stdinChannel.writer->waitForWrite(0)) + if (stdinChannel.writer->checkForWrite()) return true; // If we wouldn't write anything, check if we can read stdout. - if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0)) - timer.resetIncrements(); + if (stdoutChannel.reader) + stdoutChannel.reader->checkForReadyRead(); // Check if we can read stderr. - if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0)) - timer.resetIncrements(); + if (stderrChannel.reader) + stderrChannel.reader->checkForReadyRead(); // 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 (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) { + // Check if the process is signaling completion. + if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { drainOutputPipes(); if (pid) processFinished(); return false; } - - // Only wait for as long as we've been asked. - if (timer.hasTimedOut()) - break; } setError(QProcess::Timedout); @@ -754,52 +763,83 @@ bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline) qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime()); #endif - QIncrementalSleepTimer timer(deadline.remainingTime()); - forever { - if (!writeBuffer.isEmpty() && !_q_canWrite()) + QProcessPoller poller(*this); + int ret = poller.poll(deadline); + if (ret < 0) return false; - if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0)) - timer.resetIncrements(); - if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0)) - timer.resetIncrements(); - if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0)) - timer.resetIncrements(); + if (ret == 0) + break; + + if (stdinChannel.writer) + stdinChannel.writer->checkForWrite(); + if (stdoutChannel.reader) + stdoutChannel.reader->checkForReadyRead(); + if (stderrChannel.reader) + stderrChannel.reader->checkForReadyRead(); if (!pid) return true; - if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { + if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { drainOutputPipes(); if (pid) processFinished(); return true; } - - if (timer.hasTimedOut()) - break; } setError(QProcess::Timedout); return false; } - void QProcessPrivate::findExitCode() { DWORD theExitCode; Q_ASSERT(pid); if (GetExitCodeProcess(pid->hProcess, &theExitCode)) { exitCode = theExitCode; - crashed = (exitCode == 0xf291 // our magic number, see killProcess - || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000)); + if (exitCode == KillProcessExitCode + || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000)) + exitStatus = QProcess::CrashExit; + else + exitStatus = QProcess::NormalExit; } } -void QProcessPrivate::flushPipeWriter() +/*! \reimp + \internal +*/ +qint64 QProcess::writeData(const char *data, qint64 len) { - if (stdinChannel.writer && stdinChannel.writer->bytesToWrite() > 0) - stdinChannel.writer->waitForWrite(ULONG_MAX); + Q_D(QProcess); + + if (d->stdinChannel.closed) { +#if defined QPROCESS_DEBUG + qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)", + data, QtDebugUtils::toPrintable(data, len, 16).constData(), len); +#endif + return 0; + } + + if (!d->stdinChannel.writer) { + d->stdinChannel.writer = new QWindowsPipeWriter(d->stdinChannel.pipe[1], this); + QObjectPrivate::connect(d->stdinChannel.writer, &QWindowsPipeWriter::bytesWritten, + d, &QProcessPrivate::_q_bytesWritten); + QObjectPrivate::connect(d->stdinChannel.writer, &QWindowsPipeWriter::writeFailed, + d, &QProcessPrivate::_q_writeFailed); + } + + if (d->isWriteChunkCached(data, len)) + d->stdinChannel.writer->write(*(d->currentWriteChunk)); + else + d->stdinChannel.writer->write(data, len); + +#if defined QPROCESS_DEBUG + qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)", + data, QtDebugUtils::toPrintable(data, len, 16).constData(), len, len); +#endif + return len; } qint64 QProcessPrivate::pipeWriterBytesToWrite() const @@ -807,23 +847,22 @@ qint64 QProcessPrivate::pipeWriterBytesToWrite() const return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0); } -bool QProcessPrivate::writeToStdin() +void QProcessPrivate::_q_bytesWritten(qint64 bytes) { Q_Q(QProcess); - if (!stdinChannel.writer) { - stdinChannel.writer = new QWindowsPipeWriter(stdinChannel.pipe[1], q); - QObject::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten, - q, &QProcess::bytesWritten); - QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::canWrite, - this, &QProcessPrivate::_q_canWrite); - } else { - if (stdinChannel.writer->isWriteOperationActive()) - return true; + if (!emittedBytesWritten) { + QScopedValueRollback<bool> guard(emittedBytesWritten, true); + emit q->bytesWritten(bytes); } + if (stdinChannel.closed && pipeWriterBytesToWrite() == 0) + closeWriteChannel(); +} - stdinChannel.writer->write(writeBuffer.read()); - return true; +void QProcessPrivate::_q_writeFailed() +{ + closeWriteChannel(); + setErrorAndEmit(QProcess::WriteError); } // Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails @@ -832,14 +871,6 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList & const QString &nativeArguments, const QString &workingDir, qint64 *pid) { - typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *); - - static const ShellExecuteExType shellExecuteEx = // XP ServicePack 1 onwards. - reinterpret_cast<ShellExecuteExType>(QSystemLibrary::resolve(QLatin1String("shell32"), - "ShellExecuteExW")); - if (!shellExecuteEx) - return false; - const QString args = qt_create_commandline(QString(), // needs arguments only arguments, nativeArguments); SHELLEXECUTEINFOW shellExecuteExInfo; @@ -856,7 +887,7 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList & shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16()); shellExecuteExInfo.nShow = SW_SHOWNORMAL; - if (!shellExecuteEx(&shellExecuteExInfo)) + if (!ShellExecuteExW(&shellExecuteExInfo)) return false; if (pid) *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess)); @@ -870,9 +901,7 @@ bool QProcessPrivate::startDetached(qint64 *pid) if (!openChannelsForDetached()) { // openChannel sets the error string - closeChannel(&stdinChannel); - closeChannel(&stdoutChannel); - closeChannel(&stderrChannel); + closeChannels(); return false; } @@ -882,7 +911,7 @@ bool QProcessPrivate::startDetached(qint64 *pid) void *envPtr = nullptr; QByteArray envlist; - if (environment.d.constData()) { + if (!environment.inheritsFromParent()) { envlist = qt_create_environment(environment.d.constData()->vars); envPtr = envlist.data(); } @@ -920,9 +949,7 @@ bool QProcessPrivate::startDetached(qint64 *pid) setErrorAndEmit(QProcess::FailedToStart); } - closeChannel(&stdinChannel); - closeChannel(&stdoutChannel); - closeChannel(&stderrChannel); + closeChannels(); return success; } |