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 "qwindowspipewriter_p.h"
47
#include <qfileinfo.h>
50
#include <qwineventnotifier.h>
52
#include <private/qthread_p.h>
58
//#define QPROCESS_DEBUG
60
void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
65
void QProcessPrivate::destroyChannel(Channel *channel)
70
static QString qt_create_commandline(const QString &program, const QStringList &arguments)
73
if (!program.isEmpty()) {
74
QString programName = program;
75
if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
76
programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
77
programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
79
// add the prgram as the first arg ... it works better
80
args = programName + QLatin1Char(' ');
83
for (int i=0; i<arguments.size(); ++i) {
84
QString tmp = arguments.at(i);
85
// Quotes are escaped and their preceding backslashes are doubled.
86
tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
87
if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
88
// The argument must not end with a \ since this would be interpreted
89
// as escaping the quote -- rather put the \ behind the quote: e.g.
90
// rather use "foo"\ than "foo\"
92
while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
94
tmp.insert(i, QLatin1Char('"'));
95
tmp.prepend(QLatin1Char('"'));
97
args += QLatin1Char(' ') + tmp;
102
QProcessEnvironment QProcessEnvironment::systemEnvironment()
104
QProcessEnvironment env;
108
void QProcessPrivate::startProcess()
112
bool success = false;
115
CloseHandle(pid->hThread);
116
CloseHandle(pid->hProcess);
120
pid = new PROCESS_INFORMATION;
121
memset(pid, 0, sizeof(PROCESS_INFORMATION));
123
q->setProcessState(QProcess::Starting);
125
QString args = qt_create_commandline(QString(), arguments);
126
if (!nativeArguments.isEmpty()) {
128
args += QLatin1Char(' ');
129
args += nativeArguments;
132
#if defined QPROCESS_DEBUG
133
qDebug("Creating process");
134
qDebug(" program : [%s]", program.toLatin1().constData());
135
qDebug(" args : %s", args.toLatin1().constData());
136
qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
139
QString fullPathProgram = program;
140
if (!QDir::isAbsolutePath(fullPathProgram))
141
fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
142
fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
143
success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
144
(wchar_t*)args.utf16(),
145
0, 0, false, 0, 0, 0, 0, pid);
149
processError = QProcess::FailedToStart;
150
emit q->error(processError);
151
q->setProcessState(QProcess::NotRunning);
155
q->setProcessState(QProcess::Running);
156
// User can call kill()/terminate() from the stateChanged() slot
157
// so check before proceeding
161
if (threadData->eventDispatcher) {
162
processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
163
QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
164
processFinishedNotifier->setEnabled(true);
167
// give the process a chance to start ...
169
_q_startupNotification();
172
bool QProcessPrivate::processStarted()
174
return processState == QProcess::Running;
177
qint64 QProcessPrivate::bytesAvailableFromStdout() const
182
qint64 QProcessPrivate::bytesAvailableFromStderr() const
187
qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
192
qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
197
static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
199
DWORD currentProcId = 0;
200
GetWindowThreadProcessId(hwnd, ¤tProcId);
201
if (currentProcId == (DWORD)procId)
202
PostMessage(hwnd, WM_CLOSE, 0, 0);
207
void QProcessPrivate::terminateProcess()
210
EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
211
PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
215
void QProcessPrivate::killProcess()
218
TerminateProcess(pid->hProcess, 0xf291);
221
bool QProcessPrivate::waitForStarted(int)
225
if (processStarted())
228
if (processError == QProcess::FailedToStart)
231
processError = QProcess::Timedout;
232
q->setErrorString(QProcess::tr("Process operation timed out"));
236
bool QProcessPrivate::waitForReadyRead(int msecs)
241
bool QProcessPrivate::waitForBytesWritten(int msecs)
246
bool QProcessPrivate::waitForFinished(int msecs)
249
#if defined QPROCESS_DEBUG
250
qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
253
QIncrementalSleepTimer timer(msecs);
259
if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
264
if (timer.hasTimedOut())
267
processError = QProcess::Timedout;
268
q->setErrorString(QProcess::tr("Process operation timed out"));
272
void QProcessPrivate::findExitCode()
275
if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
276
exitCode = theExitCode;
277
//### for now we assume a crash if exit code is less than -1 or the magic number
278
crashed = (exitCode == 0xf291 || (int)exitCode < 0);
282
void QProcessPrivate::flushPipeWriter()
286
qint64 QProcessPrivate::pipeWriterBytesToWrite() const
291
qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
298
bool QProcessPrivate::waitForWrite(int msecs)
304
void QProcessPrivate::_q_notified()
308
bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
310
Q_UNUSED(workingDir);
311
QString args = qt_create_commandline(QString(), arguments);
313
bool success = false;
315
PROCESS_INFORMATION pinfo;
317
QString fullPathProgram = program;
318
if (!QDir::isAbsolutePath(fullPathProgram))
319
fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/')));
320
fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
321
success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
322
(wchar_t*)args.utf16(),
323
0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
326
CloseHandle(pinfo.hThread);
327
CloseHandle(pinfo.hProcess);
329
*pid = pinfo.dwProcessId;
337
#endif // QT_NO_PROCESS