summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qprocess_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qprocess_win.cpp')
-rw-r--r--src/corelib/io/qprocess_win.cpp505
1 files changed, 269 insertions, 236 deletions
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index dc8c50127d..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));
@@ -869,9 +900,8 @@ bool QProcessPrivate::startDetached(qint64 *pid)
static const DWORD errorElevationRequired = 740;
if (!openChannelsForDetached()) {
- closeChannel(&stdinChannel);
- closeChannel(&stdoutChannel);
- closeChannel(&stderrChannel);
+ // openChannel sets the error string
+ closeChannels();
return false;
}
@@ -881,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();
}
@@ -913,10 +943,13 @@ bool QProcessPrivate::startDetached(qint64 *pid)
success = startDetachedUacPrompt(program, arguments, nativeArguments,
workingDirectory, pid);
}
+ if (!success) {
+ if (pid)
+ *pid = -1;
+ setErrorAndEmit(QProcess::FailedToStart);
+ }
- closeChannel(&stdinChannel);
- closeChannel(&stdoutChannel);
- closeChannel(&stderrChannel);
+ closeChannels();
return success;
}