1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtCore module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 #include "qprocess_p.h"
44 #include "qwindowspipereader_p.h"
45 #include "qwindowspipewriter_p.h"
47 #include <qdatetime.h>
49 #include <qelapsedtimer.h>
50 #include <qfileinfo.h>
53 #include <qwineventnotifier.h>
54 #include <private/qthread_p.h>
57 #include "private/qfsfileengine_p.h" // for longFileName
59 #ifndef PIPE_REJECT_REMOTE_CLIENTS
60 #define PIPE_REJECT_REMOTE_CLIENTS 0x08
67 //#define QPROCESS_DEBUG
69 #define NOTIFYTIMEOUT 100
71 static void qt_create_pipe(Q_PIPE *pipe, bool isInputPipe)
73 // Anomymous pipes do not support asynchronous I/O. Thus we
74 // create named pipes for redirecting stdout, stderr and stdin.
76 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), 0, false };
77 secAtt.bInheritHandle = isInputPipe; // The read handle must be non-inheritable for output pipes.
80 wchar_t pipeName[256];
81 unsigned int attempts = 1000;
83 // ### The user must make sure to call qsrand() to make the pipe names less predictable.
84 // ### Replace the call to qrand() with a secure version, once we have it in Qt.
85 swprintf(pipeName, sizeof(pipeName) / sizeof(pipeName[0]),
86 L"\\\\.\\pipe\\qt-%X", qrand());
88 DWORD dwPipeFlags = PIPE_TYPE_BYTE | PIPE_WAIT;
89 if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA)
90 dwPipeFlags |= PIPE_REJECT_REMOTE_CLIENTS;
91 const DWORD dwPipeBufferSize = 1024 * 1024;
92 hRead = CreateNamedPipe(pipeName,
93 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
95 1, // only one pipe instance
96 0, // output buffer size
97 dwPipeBufferSize, // input buffer size
100 if (hRead != INVALID_HANDLE_VALUE)
102 DWORD dwError = GetLastError();
103 if (dwError != ERROR_PIPE_BUSY || !--attempts) {
104 qErrnoWarning(dwError, "QProcess: CreateNamedPipe failed.");
109 // The write handle must be non-inheritable for input pipes.
110 secAtt.bInheritHandle = !isInputPipe;
112 HANDLE hWrite = INVALID_HANDLE_VALUE;
113 hWrite = CreateFile(pipeName,
118 FILE_FLAG_OVERLAPPED,
120 if (hWrite == INVALID_HANDLE_VALUE) {
121 qErrnoWarning("QProcess: CreateFile failed.");
126 // Wait until connection is in place.
127 ConnectNamedPipe(hRead, NULL);
133 static void duplicateStdWriteChannel(Q_PIPE *pipe, DWORD nStdHandle)
135 pipe[0] = INVALID_Q_PIPE;
136 HANDLE hStdWriteChannel = GetStdHandle(nStdHandle);
137 HANDLE hCurrentProcess = GetCurrentProcess();
138 DuplicateHandle(hCurrentProcess, hStdWriteChannel, hCurrentProcess,
139 &pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
143 Create the pipes to a QProcessPrivate::Channel.
145 This function must be called in order: stdin, stdout, stderr
147 bool QProcessPrivate::createChannel(Channel &channel)
151 if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
152 return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
153 &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
156 if (channel.type == Channel::Normal) {
157 // we're piping this channel to our own process
158 const bool isStdInChannel = (&channel == &stdinChannel);
159 if (isStdInChannel || processChannelMode != QProcess::ForwardedChannels)
160 qt_create_pipe(channel.pipe, isStdInChannel);
162 duplicateStdWriteChannel(channel.pipe, (&channel == &stdoutChannel) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
164 if (processChannelMode != QProcess::ForwardedChannels) {
165 QWindowsPipeReader *pipeReader = 0;
166 if (&channel == &stdoutChannel) {
168 stdoutReader = new QWindowsPipeReader(q);
169 q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
171 pipeReader = stdoutReader;
172 } else if (&channel == &stderrChannel) {
174 stderrReader = new QWindowsPipeReader(q);
175 q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
177 pipeReader = stderrReader;
180 pipeReader->setHandle(channel.pipe[0]);
181 pipeReader->startAsyncRead();
186 } else if (channel.type == Channel::Redirect) {
187 // we're redirecting the channel to/from a file
188 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
190 if (&channel == &stdinChannel) {
191 // try to open in read-only mode
192 channel.pipe[1] = INVALID_Q_PIPE;
194 CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
196 FILE_SHARE_READ | FILE_SHARE_WRITE,
199 FILE_ATTRIBUTE_NORMAL,
202 if (channel.pipe[0] != INVALID_Q_PIPE)
205 q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
207 // open in write mode
208 channel.pipe[0] = INVALID_Q_PIPE;
210 CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
212 FILE_SHARE_READ | FILE_SHARE_WRITE,
214 channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
215 FILE_ATTRIBUTE_NORMAL,
218 if (channel.pipe[1] != INVALID_Q_PIPE) {
219 if (channel.append) {
220 SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
225 q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
228 // could not open file
229 processError = QProcess::FailedToStart;
230 emit q->error(processError);
234 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
239 if (channel.type == Channel::PipeSource) {
242 sink = &channel.process->stdinChannel;
244 if (source->pipe[1] != INVALID_Q_PIPE) {
245 // already constructed by the sink
246 // make it inheritable
247 HANDLE tmpHandle = source->pipe[1];
248 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
249 GetCurrentProcess(), &source->pipe[1],
250 0, TRUE, DUPLICATE_SAME_ACCESS))
253 CloseHandle(tmpHandle);
257 Q_ASSERT(source == &stdoutChannel);
258 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
260 qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
261 sink->pipe[0] = source->pipe[0];
262 source->pipe[0] = INVALID_Q_PIPE;
267 source = &channel.process->stdoutChannel;
270 if (sink->pipe[0] != INVALID_Q_PIPE) {
271 // already constructed by the source
272 // make it inheritable
273 HANDLE tmpHandle = sink->pipe[0];
274 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
275 GetCurrentProcess(), &sink->pipe[0],
276 0, TRUE, DUPLICATE_SAME_ACCESS))
279 CloseHandle(tmpHandle);
282 Q_ASSERT(sink == &stdinChannel);
283 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
285 qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
286 source->pipe[1] = sink->pipe[1];
287 sink->pipe[1] = INVALID_Q_PIPE;
294 void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
296 if (pipe[0] != INVALID_Q_PIPE) {
297 CloseHandle(pipe[0]);
298 pipe[0] = INVALID_Q_PIPE;
300 if (pipe[1] != INVALID_Q_PIPE) {
301 CloseHandle(pipe[1]);
302 pipe[1] = INVALID_Q_PIPE;
306 void QProcessPrivate::destroyChannel(Channel *channel)
308 if (channel == &stdinChannel) {
313 } else if (channel == &stdoutChannel) {
315 stdoutReader->deleteLater();
318 } else if (channel == &stderrChannel) {
320 stderrReader->deleteLater();
324 destroyPipe(channel->pipe);
327 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
330 if (!program.isEmpty()) {
331 QString programName = program;
332 if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
333 programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
334 programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
336 // add the prgram as the first arg ... it works better
337 args = programName + QLatin1Char(' ');
340 for (int i=0; i<arguments.size(); ++i) {
341 QString tmp = arguments.at(i);
342 // Quotes are escaped and their preceding backslashes are doubled.
343 tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
344 if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
345 // The argument must not end with a \ since this would be interpreted
346 // as escaping the quote -- rather put the \ behind the quote: e.g.
347 // rather use "foo"\ than "foo\"
348 int i = tmp.length();
349 while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
351 tmp.insert(i, QLatin1Char('"'));
352 tmp.prepend(QLatin1Char('"'));
354 args += QLatin1Char(' ') + tmp;
359 QProcessEnvironment QProcessEnvironment::systemEnvironment()
361 QProcessEnvironment env;
362 // Calls to setenv() affect the low-level environment as well.
363 // This is not the case the other way round.
364 if (wchar_t *envStrings = GetEnvironmentStringsW()) {
365 for (const wchar_t *entry = envStrings; *entry; ) {
366 const int entryLen = int(wcslen(entry));
367 if (const wchar_t *equal = wcschr(entry, L'=')) {
368 int nameLen = equal - entry;
369 QString name = QString::fromWCharArray(entry, nameLen);
370 QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1);
371 env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), value);
373 entry += entryLen + 1;
375 FreeEnvironmentStringsW(envStrings);
380 static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Hash &environment)
383 if (!environment.isEmpty()) {
384 QProcessEnvironmentPrivate::Hash copy = environment;
386 // add PATH if necessary (for DLL loading)
387 QProcessEnvironmentPrivate::Key pathKey(QLatin1String("PATH"));
388 if (!copy.contains(pathKey)) {
389 QByteArray path = qgetenv("PATH");
391 copy.insert(pathKey, QString::fromLocal8Bit(path));
394 // add systemroot if needed
395 QProcessEnvironmentPrivate::Key rootKey(QLatin1String("SystemRoot"));
396 if (!copy.contains(rootKey)) {
397 QByteArray systemRoot = qgetenv("SystemRoot");
398 if (!systemRoot.isEmpty())
399 copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
403 QProcessEnvironmentPrivate::Hash::ConstIterator it = copy.constBegin(),
404 end = copy.constEnd();
406 static const wchar_t equal = L'=';
407 static const wchar_t nul = L'\0';
409 for ( ; it != end; ++it) {
410 uint tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
411 // ignore empty strings
412 if (tmpSize == sizeof(wchar_t) * 2)
414 envlist.resize(envlist.size() + tmpSize);
416 tmpSize = it.key().length() * sizeof(wchar_t);
417 memcpy(envlist.data()+pos, it.key().utf16(), tmpSize);
420 memcpy(envlist.data()+pos, &equal, sizeof(wchar_t));
421 pos += sizeof(wchar_t);
423 tmpSize = it.value().length() * sizeof(wchar_t);
424 memcpy(envlist.data()+pos, it.value().utf16(), tmpSize);
427 memcpy(envlist.data()+pos, &nul, sizeof(wchar_t));
428 pos += sizeof(wchar_t);
430 // add the 2 terminating 0 (actually 4, just to be on the safe side)
431 envlist.resize( envlist.size()+4 );
440 void QProcessPrivate::startProcess()
444 bool success = false;
447 CloseHandle(pid->hThread);
448 CloseHandle(pid->hProcess);
452 pid = new PROCESS_INFORMATION;
453 memset(pid, 0, sizeof(PROCESS_INFORMATION));
455 q->setProcessState(QProcess::Starting);
457 if (!createChannel(stdinChannel) ||
458 !createChannel(stdoutChannel) ||
459 !createChannel(stderrChannel))
462 QString args = qt_create_commandline(program, arguments);
464 if (environment.d.constData())
465 envlist = qt_create_environment(environment.d.constData()->hash);
466 if (!nativeArguments.isEmpty()) {
468 args += QLatin1Char(' ');
469 args += nativeArguments;
472 #if defined QPROCESS_DEBUG
473 qDebug("Creating process");
474 qDebug(" program : [%s]", program.toLatin1().constData());
475 qDebug(" args : %s", args.toLatin1().constData());
476 qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
479 // Forwarded channels must not set the CREATE_NO_WINDOW flag because this
480 // will render the stdout/stderr handles we're passing useless.
481 DWORD dwCreationFlags = (processChannelMode == QProcess::ForwardedChannels ? 0 : CREATE_NO_WINDOW);
482 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
483 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
484 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
485 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
487 STARTF_USESTDHANDLES,
489 stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
491 success = CreateProcess(0, (wchar_t*)args.utf16(),
492 0, 0, TRUE, dwCreationFlags,
493 environment.isEmpty() ? 0 : envlist.data(),
494 workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(),
497 // Capture the error string before we do CloseHandle below
498 q->setErrorString(QProcess::tr("Process failed to start: %1").arg(qt_error_string()));
501 if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
502 CloseHandle(stdinChannel.pipe[0]);
503 stdinChannel.pipe[0] = INVALID_Q_PIPE;
505 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
506 CloseHandle(stdoutChannel.pipe[1]);
507 stdoutChannel.pipe[1] = INVALID_Q_PIPE;
509 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
510 CloseHandle(stderrChannel.pipe[1]);
511 stderrChannel.pipe[1] = INVALID_Q_PIPE;
516 processError = QProcess::FailedToStart;
517 emit q->error(processError);
518 q->setProcessState(QProcess::NotRunning);
522 q->setProcessState(QProcess::Running);
523 // User can call kill()/terminate() from the stateChanged() slot
524 // so check before proceeding
528 if (threadData->eventDispatcher) {
529 processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
530 QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
531 processFinishedNotifier->setEnabled(true);
532 notifier = new QTimer(q);
533 QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
534 notifier->start(NOTIFYTIMEOUT);
537 _q_startupNotification();
540 bool QProcessPrivate::processStarted()
542 return processState == QProcess::Running;
545 qint64 QProcessPrivate::bytesAvailableFromStdout() const
547 if (stdoutChannel.pipe[0] == INVALID_Q_PIPE)
553 DWORD bytesAvail = stdoutReader->bytesAvailable();
554 #if defined QPROCESS_DEBUG
555 qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
560 qint64 QProcessPrivate::bytesAvailableFromStderr() const
562 if (stderrChannel.pipe[0] == INVALID_Q_PIPE)
568 DWORD bytesAvail = stderrReader->bytesAvailable();
569 #if defined QPROCESS_DEBUG
570 qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
575 qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
577 return stdoutReader ? stdoutReader->read(data, maxlen) : 0;
580 qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
582 return stderrReader ? stderrReader->read(data, maxlen) : 0;
586 static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
588 DWORD currentProcId = 0;
589 GetWindowThreadProcessId(hwnd, ¤tProcId);
590 if (currentProcId == (DWORD)procId)
591 PostMessage(hwnd, WM_CLOSE, 0, 0);
596 void QProcessPrivate::terminateProcess()
599 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
600 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
604 void QProcessPrivate::killProcess()
607 TerminateProcess(pid->hProcess, 0xf291);
610 bool QProcessPrivate::waitForStarted(int)
614 if (processStarted())
617 if (processError == QProcess::FailedToStart)
620 processError = QProcess::Timedout;
621 q->setErrorString(QProcess::tr("Process operation timed out"));
625 static bool drainOutputPipes(QProcessPrivate *d)
627 if (!d->stdoutReader && !d->stderrReader)
630 bool readyReadEmitted = false;
632 bool readOperationActive = false;
633 if (d->stdoutReader) {
634 readyReadEmitted |= d->stdoutReader->waitForReadyRead(0);
635 readOperationActive = d->stdoutReader->isReadOperationActive();
637 if (d->stderrReader) {
638 readyReadEmitted |= d->stderrReader->waitForReadyRead(0);
639 readOperationActive |= d->stderrReader->isReadOperationActive();
641 if (!readOperationActive)
646 return readyReadEmitted;
649 bool QProcessPrivate::waitForReadyRead(int msecs)
653 QIncrementalSleepTimer timer(msecs);
656 if (!writeBuffer.isEmpty() && !_q_canWrite())
658 if (pipeWriter && pipeWriter->waitForWrite(0))
659 timer.resetIncrements();
661 if (processChannelMode != QProcess::ForwardedChannels
662 && ((stdoutReader && stdoutReader->waitForReadyRead(0))
663 || (stderrReader && stderrReader->waitForReadyRead(0))))
668 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
669 bool readyReadEmitted = drainOutputPipes(this);
671 return readyReadEmitted;
674 Sleep(timer.nextSleepTime());
675 if (timer.hasTimedOut())
679 processError = QProcess::Timedout;
680 q->setErrorString(QProcess::tr("Process operation timed out"));
684 bool QProcessPrivate::waitForBytesWritten(int msecs)
688 QIncrementalSleepTimer timer(msecs);
691 // Check if we have any data pending: the pipe writer has
692 // bytes waiting to written, or it has written data since the
693 // last time we called pipeWriter->waitForWrite().
694 bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
696 // If we don't have pending data, and our write buffer is
698 if (!pendingDataInPipe && writeBuffer.isEmpty())
701 // If we don't have pending data and we do have data in our
702 // write buffer, try to flush that data over to the pipe
703 // writer. Fail on error.
704 if (!pendingDataInPipe) {
709 // Wait for the pipe writer to acknowledge that it has
710 // written. This will succeed if either the pipe writer has
711 // already written the data, or if it manages to write data
712 // within the given timeout. If the write buffer was non-empty
713 // and the pipeWriter is now dead, that means _q_canWrite()
714 // destroyed the writer after it successfully wrote the last
716 if (!pipeWriter || pipeWriter->waitForWrite(0))
719 // If we wouldn't write anything, check if we can read stdout.
720 if (bytesAvailableFromStdout() != 0) {
721 _q_canReadStandardOutput();
722 timer.resetIncrements();
725 // Check if we can read stderr.
726 if (bytesAvailableFromStderr() != 0) {
727 _q_canReadStandardError();
728 timer.resetIncrements();
731 // Check if the process died while reading.
735 // Wait for the process to signal any change in its state,
736 // such as incoming data, or if the process died.
737 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
742 // Only wait for as long as we've been asked.
743 if (timer.hasTimedOut())
747 processError = QProcess::Timedout;
748 q->setErrorString(QProcess::tr("Process operation timed out"));
752 bool QProcessPrivate::waitForFinished(int msecs)
755 #if defined QPROCESS_DEBUG
756 qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
759 QIncrementalSleepTimer timer(msecs);
762 if (!writeBuffer.isEmpty() && !_q_canWrite())
764 if (pipeWriter && pipeWriter->waitForWrite(0))
765 timer.resetIncrements();
766 if (stdoutReader && stdoutReader->waitForReadyRead(0))
767 timer.resetIncrements();
768 if (stderrReader && stderrReader->waitForReadyRead(0))
769 timer.resetIncrements();
772 drainOutputPipes(this);
776 if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
777 drainOutputPipes(this);
782 if (timer.hasTimedOut())
786 processError = QProcess::Timedout;
787 q->setErrorString(QProcess::tr("Process operation timed out"));
792 void QProcessPrivate::findExitCode()
795 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
796 exitCode = theExitCode;
797 //### for now we assume a crash if exit code is less than -1 or the magic number
798 crashed = (exitCode == 0xf291 || (int)exitCode < 0);
802 void QProcessPrivate::flushPipeWriter()
804 if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
805 pipeWriter->waitForWrite(ULONG_MAX);
809 qint64 QProcessPrivate::pipeWriterBytesToWrite() const
811 return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
814 qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
819 pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
823 return pipeWriter->write(data, maxlen);
826 bool QProcessPrivate::waitForWrite(int msecs)
830 if (!pipeWriter || pipeWriter->waitForWrite(msecs))
833 processError = QProcess::Timedout;
834 q->setErrorString(QProcess::tr("Process operation timed out"));
838 void QProcessPrivate::_q_notified()
842 if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0)))
845 if (processState != QProcess::NotRunning)
846 notifier->start(NOTIFYTIMEOUT);
849 bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
851 QString args = qt_create_commandline(program, arguments);
852 bool success = false;
853 PROCESS_INFORMATION pinfo;
855 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
856 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
857 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
858 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
860 success = CreateProcess(0, (wchar_t*)args.utf16(),
861 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
862 workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
863 &startupInfo, &pinfo);
866 CloseHandle(pinfo.hThread);
867 CloseHandle(pinfo.hProcess);
869 *pid = pinfo.dwProcessId;
877 #endif // QT_NO_PROCESS