File indexing completed on 2024-05-12 04:51:20
0001 /* 0002 SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies). 0003 Contact: Qt Software Information (qt-info@nokia.com) 0004 0005 This file is part of the QtCore module of the Qt Toolkit. 0006 0007 $QT_BEGIN_LICENSE:LGPL$ 0008 Commercial Usage 0009 Licensees holding valid Qt Commercial licenses may use this file in 0010 accordance with the Qt Commercial License Agreement provided with the 0011 Software or, alternatively, in accordance with the terms contained in 0012 a written agreement between you and Nokia. 0013 0014 GNU Lesser General Public License Usage 0015 Alternatively, this file may be used under the terms of the GNU Lesser 0016 General Public License version 2.1 as published by the Free Software 0017 Foundation and appearing in the file LICENSE.LGPL included in the 0018 packaging of this file. Please review the following information to 0019 ensure the GNU Lesser General Public License version 2.1 requirements 0020 will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 0021 0022 In addition, as a special exception, Nokia gives you certain 0023 additional rights. These rights are described in the Nokia Qt LGPL 0024 Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 0025 package. 0026 0027 GNU General Public License Usage 0028 Alternatively, this file may be used under the terms of the GNU 0029 General Public License version 3.0 as published by the Free Software 0030 Foundation and appearing in the file LICENSE.GPL included in the 0031 packaging of this file. Please review the following information to 0032 ensure the GNU General Public License version 3.0 requirements will be 0033 met: https://www.gnu.org/licenses/gpl-3.0.html. 0034 0035 If you are unsure which license is appropriate for your use, please 0036 contact the sales department at qt-sales@nokia.com. 0037 $QT_END_LICENSE$ 0038 0039 */ 0040 0041 #include "k3bqprocess.h" 0042 #include "k3bqprocess_p.h" 0043 #include "qwindowspipewriter_p.h" 0044 0045 #include <QDateTime> 0046 #include <QDir> 0047 #include <QFileInfo> 0048 #include <QTimer> 0049 #include <QThread> 0050 #include <QMutex> 0051 #include <QWaitCondition> 0052 #include <private/qwineventnotifier_p.h> 0053 #include <private/qthread_p.h> 0054 #include <qdebug.h> 0055 0056 #include "private/qfsfileengine.h" // for longFileName and win95FileName 0057 #include "private/qfsfileengine_p.h" // for longFileName and win95FileName 0058 0059 0060 #ifndef QT_NO_PROCESS 0061 0062 QT_BEGIN_NAMESPACE 0063 0064 //#define QPROCESS_DEBUG 0065 0066 #define NOTIFYTIMEOUT 100 0067 0068 static void qt_create_pipe(Q_PIPE *pipe, bool in) 0069 { 0070 // Open the pipes. Make non-inheritable copies of input write and output 0071 // read handles to avoid non-closable handles (this is done by the 0072 // DuplicateHandle() call). 0073 0074 #if !defined(Q_OS_WINCE) 0075 SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE }; 0076 0077 HANDLE tmpHandle; 0078 if (in) { // stdin 0079 if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) 0080 return; 0081 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), 0082 &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) 0083 return; 0084 } else { // stdout or stderr 0085 if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) 0086 return; 0087 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), 0088 &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) 0089 return; 0090 } 0091 0092 CloseHandle(tmpHandle); 0093 #else 0094 Q_UNUSED(pipe); 0095 Q_UNUSED(in); 0096 #endif 0097 } 0098 0099 /* 0100 Create the pipes to a K3bQProcessPrivate::Channel. 0101 0102 This function must be called in order: stdin, stdout, stderr 0103 */ 0104 bool K3bQProcessPrivate::createChannel(Channel &channel) 0105 { 0106 Q_Q(K3bQProcess); 0107 0108 if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) { 0109 return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(), 0110 &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS); 0111 } 0112 0113 if (channel.type == Channel::Normal) { 0114 // we're piping this channel to our own process 0115 qt_create_pipe(channel.pipe, &channel == &stdinChannel); 0116 0117 return true; 0118 } else if (channel.type == Channel::Redirect) { 0119 // we're redirecting the channel to/from a file 0120 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; 0121 0122 if (&channel == &stdinChannel) { 0123 // try to open in read-only mode 0124 channel.pipe[1] = INVALID_Q_PIPE; 0125 QT_WA({ 0126 channel.pipe[0] = 0127 CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), 0128 GENERIC_READ, 0129 FILE_SHARE_READ | FILE_SHARE_WRITE, 0130 &secAtt, 0131 OPEN_EXISTING, 0132 FILE_ATTRIBUTE_NORMAL, 0133 NULL); 0134 }, { 0135 channel.pipe[0] = 0136 CreateFileA(QFSFileEnginePrivate::win95Name(channel.file), 0137 GENERIC_READ, 0138 FILE_SHARE_READ | FILE_SHARE_WRITE, 0139 &secAtt, 0140 OPEN_EXISTING, 0141 FILE_ATTRIBUTE_NORMAL, 0142 NULL); 0143 }); 0144 if (channel.pipe[0] != INVALID_Q_PIPE) 0145 return true; 0146 0147 q->setErrorString(K3bQProcess::tr("Could not open input redirection for reading")); 0148 } else { 0149 // open in write mode 0150 channel.pipe[0] = INVALID_Q_PIPE; 0151 DWORD creation; 0152 if (channel.append) 0153 creation = OPEN_ALWAYS; 0154 else 0155 creation = CREATE_ALWAYS; 0156 0157 QT_WA({ 0158 channel.pipe[1] = 0159 CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), 0160 GENERIC_WRITE, 0161 FILE_SHARE_READ | FILE_SHARE_WRITE, 0162 &secAtt, 0163 creation, 0164 FILE_ATTRIBUTE_NORMAL, 0165 NULL); 0166 }, { 0167 channel.pipe[1] = 0168 CreateFileA(QFSFileEnginePrivate::win95Name(channel.file), 0169 GENERIC_WRITE, 0170 FILE_SHARE_READ | FILE_SHARE_WRITE, 0171 &secAtt, 0172 creation, 0173 FILE_ATTRIBUTE_NORMAL, 0174 NULL); 0175 }); 0176 if (channel.pipe[1] != INVALID_Q_PIPE) { 0177 if (channel.append) { 0178 SetFilePointer(channel.pipe[1], 0, NULL, FILE_END); 0179 } 0180 return true; 0181 } 0182 0183 q->setErrorString(K3bQProcess::tr("Could not open output redirection for writing")); 0184 } 0185 0186 // could not open file 0187 processError = QProcess::FailedToStart; 0188 emit q->error(processError); 0189 cleanup(); 0190 return false; 0191 } else { 0192 Q_ASSERT_X(channel.process, "K3bQProcess::start", "Internal error"); 0193 0194 Channel *source; 0195 Channel *sink; 0196 0197 if (channel.type == Channel::PipeSource) { 0198 // we are the source 0199 source = &channel; 0200 sink = &channel.process->stdinChannel; 0201 0202 if (source->pipe[1] != INVALID_Q_PIPE) { 0203 // already constructed by the sink 0204 // make it inheritable 0205 HANDLE tmpHandle = source->pipe[1]; 0206 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, 0207 GetCurrentProcess(), &source->pipe[1], 0208 0, TRUE, DUPLICATE_SAME_ACCESS)) 0209 return false; 0210 0211 CloseHandle(tmpHandle); 0212 return true; 0213 } 0214 0215 Q_ASSERT(source == &stdoutChannel); 0216 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); 0217 0218 qt_create_pipe(source->pipe, /* in = */ false); // source is stdout 0219 sink->pipe[0] = source->pipe[0]; 0220 source->pipe[0] = INVALID_Q_PIPE; 0221 0222 return true; 0223 } else { 0224 // we are the sink; 0225 source = &channel.process->stdoutChannel; 0226 sink = &channel; 0227 0228 if (sink->pipe[0] != INVALID_Q_PIPE) { 0229 // already constructed by the source 0230 // make it inheritable 0231 HANDLE tmpHandle = sink->pipe[0]; 0232 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, 0233 GetCurrentProcess(), &sink->pipe[0], 0234 0, TRUE, DUPLICATE_SAME_ACCESS)) 0235 return false; 0236 0237 CloseHandle(tmpHandle); 0238 return true; 0239 } 0240 Q_ASSERT(sink == &stdinChannel); 0241 Q_ASSERT(source->process == this && source->type == Channel::PipeSource); 0242 0243 qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin 0244 source->pipe[1] = sink->pipe[1]; 0245 sink->pipe[1] = INVALID_Q_PIPE; 0246 0247 return true; 0248 } 0249 } 0250 } 0251 0252 void K3bQProcessPrivate::destroyPipe(Q_PIPE pipe[2]) 0253 { 0254 if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) { 0255 delete pipeWriter; 0256 pipeWriter = 0; 0257 } 0258 0259 if (pipe[0] != INVALID_Q_PIPE) { 0260 CloseHandle(pipe[0]); 0261 pipe[0] = INVALID_Q_PIPE; 0262 } 0263 if (pipe[1] != INVALID_Q_PIPE) { 0264 CloseHandle(pipe[1]); 0265 pipe[1] = INVALID_Q_PIPE; 0266 } 0267 } 0268 0269 0270 static QString qt_create_commandline(const QString &program, const QStringList &arguments) 0271 { 0272 QString args; 0273 if (!program.isEmpty()) { 0274 QString programName = program; 0275 if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" "))) 0276 programName = QLatin1String("\"") + programName + QLatin1String("\""); 0277 programName.replace(QLatin1String("/"), QLatin1String("\\")); 0278 0279 // add the prgram as the first arg ... it works better 0280 args = programName + QLatin1String(" "); 0281 } 0282 0283 for (int i=0; i<arguments.size(); ++i) { 0284 QString tmp = arguments.at(i); 0285 // in the case of \" already being in the string the \ must also be escaped 0286 tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") ); 0287 // escape a single " because the arguments will be parsed 0288 tmp.replace( QLatin1String("\""), QLatin1String("\\\"") ); 0289 if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { 0290 // The argument must not end with a \ since this would be interpreted 0291 // as escaping the quote -- rather put the \ behind the quote: e.g. 0292 // rather use "foo"\ than "foo\" 0293 QString endQuote(QLatin1String("\"")); 0294 int i = tmp.length(); 0295 while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) { 0296 --i; 0297 endQuote += QLatin1String("\\"); 0298 } 0299 args += QLatin1String(" \"") + tmp.left(i) + endQuote; 0300 } else { 0301 args += QLatin1Char(' ') + tmp; 0302 } 0303 } 0304 return args; 0305 } 0306 0307 static QByteArray qt_create_environment(const QStringList &environment) 0308 { 0309 QByteArray envlist; 0310 if (!environment.isEmpty()) { 0311 QStringList envStrings = environment; 0312 int pos = 0; 0313 // add PATH if necessary (for DLL loading) 0314 if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) { 0315 QByteArray path = qgetenv("PATH"); 0316 if (!path.isEmpty()) 0317 envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); 0318 } 0319 // add systemroot if needed 0320 if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) { 0321 QByteArray systemRoot = qgetenv("SystemRoot"); 0322 if (!systemRoot.isEmpty()) 0323 envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); 0324 } 0325 #ifdef UNICODE 0326 if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { 0327 for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { 0328 QString tmp = *it; 0329 uint tmpSize = sizeof(TCHAR) * (tmp.length()+1); 0330 envlist.resize(envlist.size() + tmpSize); 0331 memcpy(envlist.data()+pos, tmp.utf16(), tmpSize); 0332 pos += tmpSize; 0333 } 0334 // add the 2 terminating 0 (actually 4, just to be on the safe side) 0335 envlist.resize( envlist.size()+4 ); 0336 envlist[pos++] = 0; 0337 envlist[pos++] = 0; 0338 envlist[pos++] = 0; 0339 envlist[pos++] = 0; 0340 } else 0341 #endif // UNICODE 0342 { 0343 for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { 0344 QByteArray tmp = (*it).toLocal8Bit(); 0345 uint tmpSize = tmp.length() + 1; 0346 envlist.resize(envlist.size() + tmpSize); 0347 memcpy(envlist.data()+pos, tmp.data(), tmpSize); 0348 pos += tmpSize; 0349 } 0350 // add the terminating 0 (actually 2, just to be on the safe side) 0351 envlist.resize(envlist.size()+2); 0352 envlist[pos++] = 0; 0353 envlist[pos++] = 0; 0354 } 0355 } 0356 return envlist; 0357 } 0358 0359 void K3bQProcessPrivate::startProcess() 0360 { 0361 Q_Q(K3bQProcess); 0362 0363 bool success = false; 0364 0365 if (pid) { 0366 CloseHandle(pid->hThread); 0367 CloseHandle(pid->hProcess); 0368 delete pid; 0369 pid = 0; 0370 } 0371 pid = new PROCESS_INFORMATION; 0372 memset(pid, 0, sizeof(PROCESS_INFORMATION)); 0373 0374 q->setProcessState(QProcess::Starting); 0375 0376 if (!createChannel(stdinChannel) || 0377 !createChannel(stdoutChannel) || 0378 !createChannel(stderrChannel)) 0379 return; 0380 0381 #if defined(Q_OS_WINCE) 0382 QString args = qt_create_commandline(QString(), arguments); 0383 #else 0384 QString args = qt_create_commandline(program, arguments); 0385 QByteArray envlist = qt_create_environment(environment); 0386 #endif 0387 0388 #if defined QPROCESS_DEBUG 0389 qDebug("Creating process"); 0390 qDebug(" program : [%s]", program.toLatin1().constData()); 0391 qDebug(" args : %s", args.toLatin1().constData()); 0392 qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes"); 0393 #endif 0394 0395 DWORD dwCreationFlags = 0; 0396 if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) 0397 dwCreationFlags |= CREATE_NO_WINDOW; 0398 0399 #ifdef UNICODE 0400 if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { 0401 #if defined(Q_OS_WINCE) 0402 QString fullPathProgram = program; 0403 if (!QDir::isAbsolutePath(fullPathProgram)) 0404 fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath(); 0405 fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); 0406 success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), 0407 (WCHAR*)args.utf16(), 0408 0, 0, false, 0, 0, 0, 0, pid); 0409 #else 0410 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; 0411 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, 0412 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0413 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0414 0, 0, 0, 0415 STARTF_USESTDHANDLES, 0416 0, 0, 0, 0417 stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1] 0418 }; 0419 success = CreateProcessW(0, (WCHAR*)args.utf16(), 0420 0, 0, TRUE, dwCreationFlags, 0421 environment.isEmpty() ? 0 : envlist.data(), 0422 workingDirectory.isEmpty() ? 0 0423 : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(), 0424 &startupInfo, pid); 0425 #endif 0426 } else 0427 #endif // UNICODE 0428 { 0429 #ifndef Q_OS_WINCE 0430 STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0, 0431 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0432 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0433 0, 0, 0, 0434 STARTF_USESTDHANDLES, 0435 0, 0, 0, 0436 stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1] 0437 }; 0438 0439 success = CreateProcessA(0, args.toLocal8Bit().data(), 0440 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(), 0441 workingDirectory.isEmpty() ? 0 0442 : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(), 0443 &startupInfo, pid); 0444 #endif // Q_OS_WINCE 0445 } 0446 #ifndef Q_OS_WINCE 0447 if (stdinChannel.pipe[0] != INVALID_Q_PIPE) { 0448 CloseHandle(stdinChannel.pipe[0]); 0449 stdinChannel.pipe[0] = INVALID_Q_PIPE; 0450 } 0451 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) { 0452 CloseHandle(stdoutChannel.pipe[1]); 0453 stdoutChannel.pipe[1] = INVALID_Q_PIPE; 0454 } 0455 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) { 0456 CloseHandle(stderrChannel.pipe[1]); 0457 stderrChannel.pipe[1] = INVALID_Q_PIPE; 0458 } 0459 #endif // Q_OS_WINCE 0460 0461 if (!success) { 0462 cleanup(); 0463 processError = QProcess::FailedToStart; 0464 q->setErrorString(K3bQProcess::tr("Process failed to start")); 0465 emit q->error(processError); 0466 q->setProcessState(QProcess::NotRunning); 0467 return; 0468 } 0469 0470 q->setProcessState(QProcess::Running); 0471 // User can call kill()/terminate() from the stateChanged() slot 0472 // so check before proceeding 0473 if (!pid) 0474 return; 0475 0476 // if (threadData->eventDispatcher) { 0477 processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q); 0478 QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied())); 0479 processFinishedNotifier->setEnabled(true); 0480 notifier = new QTimer(q); 0481 QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified())); 0482 notifier->start(NOTIFYTIMEOUT); 0483 // } 0484 0485 // give the process a chance to start ... 0486 Sleep(SLEEPMIN*2); 0487 _q_startupNotification(); 0488 } 0489 0490 bool K3bQProcessPrivate::processStarted() 0491 { 0492 return processState == QProcess::Running; 0493 } 0494 0495 qint64 K3bQProcessPrivate::bytesAvailableFromStdout() const 0496 { 0497 if (stdoutChannel.pipe[0] == INVALID_Q_PIPE) 0498 return 0; 0499 0500 DWORD bytesAvail = 0; 0501 #if !defined(Q_OS_WINCE) 0502 PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); 0503 #if defined QPROCESS_DEBUG 0504 qDebug("K3bQProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail); 0505 #endif 0506 if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) { 0507 QByteArray buf(bytesAvail, 0); 0508 DWORD bytesRead = 0; 0509 if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) { 0510 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 0511 if (hStdout) { 0512 DWORD bytesWritten = 0; 0513 WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0); 0514 } 0515 } 0516 bytesAvail = 0; 0517 } 0518 #endif 0519 return bytesAvail; 0520 } 0521 0522 qint64 K3bQProcessPrivate::bytesAvailableFromStderr() const 0523 { 0524 if (stderrChannel.pipe[0] == INVALID_Q_PIPE) 0525 return 0; 0526 0527 DWORD bytesAvail = 0; 0528 #if !defined(Q_OS_WINCE) 0529 PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); 0530 #if defined QPROCESS_DEBUG 0531 qDebug("K3bQProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail); 0532 #endif 0533 if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) { 0534 QByteArray buf(bytesAvail, 0); 0535 DWORD bytesRead = 0; 0536 if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) { 0537 HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); 0538 if (hStderr) { 0539 DWORD bytesWritten = 0; 0540 WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0); 0541 } 0542 } 0543 bytesAvail = 0; 0544 } 0545 #endif 0546 return bytesAvail; 0547 } 0548 0549 qint64 K3bQProcessPrivate::readFromStdout(char *data, qint64 maxlen) 0550 { 0551 DWORD read = qMin(maxlen, bytesAvailableFromStdout()); 0552 DWORD bytesRead = 0; 0553 0554 if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0)) 0555 return -1; 0556 return bytesRead; 0557 } 0558 0559 qint64 K3bQProcessPrivate::readFromStderr(char *data, qint64 maxlen) 0560 { 0561 DWORD read = qMin(maxlen, bytesAvailableFromStderr()); 0562 DWORD bytesRead = 0; 0563 0564 if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0)) 0565 return -1; 0566 return bytesRead; 0567 } 0568 0569 0570 static BOOL CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId) 0571 { 0572 DWORD currentProcId = 0; 0573 GetWindowThreadProcessId(hwnd, ¤tProcId); 0574 if (currentProcId == (DWORD)procId) 0575 PostMessage(hwnd, WM_CLOSE, 0, 0); 0576 0577 return TRUE; 0578 } 0579 0580 void K3bQProcessPrivate::terminateProcess() 0581 { 0582 if (pid) { 0583 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId); 0584 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0); 0585 } 0586 } 0587 0588 void K3bQProcessPrivate::killProcess() 0589 { 0590 if (pid) 0591 TerminateProcess(pid->hProcess, 0xf291); 0592 } 0593 0594 bool K3bQProcessPrivate::waitForStarted(int) 0595 { 0596 Q_Q(K3bQProcess); 0597 0598 if (processStarted()) 0599 return true; 0600 0601 if (processError == QProcess::FailedToStart) 0602 return false; 0603 0604 processError = QProcess::Timedout; 0605 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0606 return false; 0607 } 0608 0609 bool K3bQProcessPrivate::waitForReadyRead(int msecs) 0610 { 0611 Q_Q(K3bQProcess); 0612 0613 #if defined(Q_OS_WINCE) 0614 processError = K3bQProcess::ReadError; 0615 q->setErrorString(K3bQProcess::tr("Error reading from process")); 0616 emit q->error(processError); 0617 return false; 0618 #endif 0619 0620 QIncrementalSleepTimer timer(msecs); 0621 0622 forever { 0623 if (!writeBuffer.isEmpty() && !_q_canWrite()) 0624 return false; 0625 if (pipeWriter && pipeWriter->waitForWrite(0)) 0626 timer.resetIncrements(); 0627 bool readyReadEmitted = false; 0628 if (bytesAvailableFromStdout() != 0) { 0629 readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted; 0630 timer.resetIncrements(); 0631 } 0632 0633 if (bytesAvailableFromStderr() != 0) { 0634 readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted; 0635 timer.resetIncrements(); 0636 } 0637 0638 if (readyReadEmitted) 0639 return true; 0640 0641 if (!pid) 0642 return false; 0643 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { 0644 // find the return value if there is noew data to read 0645 _q_processDied(); 0646 return false; 0647 } 0648 0649 Sleep(timer.nextSleepTime()); 0650 if (timer.hasTimedOut()) 0651 break; 0652 } 0653 0654 processError = QProcess::Timedout; 0655 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0656 return false; 0657 } 0658 0659 bool K3bQProcessPrivate::waitForBytesWritten(int msecs) 0660 { 0661 Q_Q(K3bQProcess); 0662 0663 #if defined(Q_OS_WINCE) 0664 processError = K3bQProcess::ReadError; 0665 q->setErrorString(K3bQProcess::tr("Error reading from process")); 0666 emit q->error(processError); 0667 return false; 0668 #endif 0669 0670 QIncrementalSleepTimer timer(msecs); 0671 0672 forever { 0673 // Check if we have any data pending: the pipe writer has 0674 // bytes waiting to written, or it has written data since the 0675 // last time we called pipeWriter->waitForWrite(). 0676 bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten()); 0677 0678 // If we don't have pending data, and our write buffer is 0679 // empty, we fail. 0680 if (!pendingDataInPipe && writeBuffer.isEmpty()) 0681 return false; 0682 0683 // If we don't have pending data and we do have data in our 0684 // write buffer, try to flush that data over to the pipe 0685 // writer. Fail on error. 0686 if (!pendingDataInPipe) { 0687 if (!_q_canWrite()) 0688 return false; 0689 } 0690 0691 // Wait for the pipe writer to acknowledge that it has 0692 // written. This will succeed if either the pipe writer has 0693 // already written the data, or if it manages to write data 0694 // within the given timeout. If the write buffer was non-empty 0695 // and the pipeWriter is now dead, that means _q_canWrite() 0696 // destroyed the writer after it successfully wrote the last 0697 // batch. 0698 if (!pipeWriter || pipeWriter->waitForWrite(0)) 0699 return true; 0700 0701 // If we wouldn't write anything, check if we can read stdout. 0702 if (bytesAvailableFromStdout() != 0) { 0703 _q_canReadStandardOutput(); 0704 timer.resetIncrements(); 0705 } 0706 0707 // Check if we can read stderr. 0708 if (bytesAvailableFromStderr() != 0) { 0709 _q_canReadStandardError(); 0710 timer.resetIncrements(); 0711 } 0712 0713 // Check if the process died while reading. 0714 if (!pid) 0715 return false; 0716 0717 // Wait for the process to signal any change in its state, 0718 // such as incoming data, or if the process died. 0719 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { 0720 _q_processDied(); 0721 return false; 0722 } 0723 0724 // Only wait for as long as we've been asked. 0725 if (timer.hasTimedOut()) 0726 break; 0727 } 0728 0729 processError = QProcess::Timedout; 0730 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0731 return false; 0732 } 0733 0734 0735 bool K3bQProcessPrivate::waitForFinished(int msecs) 0736 { 0737 Q_Q(K3bQProcess); 0738 #if defined QPROCESS_DEBUG 0739 qDebug("K3bQProcessPrivate::waitForFinished(%d)", msecs); 0740 #endif 0741 0742 QIncrementalSleepTimer timer(msecs); 0743 0744 forever { 0745 if (!writeBuffer.isEmpty() && !_q_canWrite()) 0746 return false; 0747 if (pipeWriter && pipeWriter->waitForWrite(0)) 0748 timer.resetIncrements(); 0749 0750 if (bytesAvailableFromStdout() != 0) { 0751 _q_canReadStandardOutput(); 0752 timer.resetIncrements(); 0753 } 0754 0755 if (bytesAvailableFromStderr() != 0) { 0756 _q_canReadStandardError(); 0757 timer.resetIncrements(); 0758 } 0759 0760 if (!pid) 0761 return true; 0762 0763 if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { 0764 _q_processDied(); 0765 return true; 0766 } 0767 0768 if (timer.hasTimedOut()) 0769 break; 0770 } 0771 processError = QProcess::Timedout; 0772 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0773 return false; 0774 } 0775 0776 0777 void K3bQProcessPrivate::findExitCode() 0778 { 0779 DWORD theExitCode; 0780 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) { 0781 exitCode = theExitCode; 0782 //### for now we assume a crash if exit code is less than -1 or the magic number 0783 crashed = (exitCode == 0xf291 || (int)exitCode < 0); 0784 } 0785 } 0786 0787 void K3bQProcessPrivate::flushPipeWriter() 0788 { 0789 if (pipeWriter && pipeWriter->bytesToWrite() > 0) { 0790 pipeWriter->waitForWrite(ULONG_MAX); 0791 } 0792 } 0793 0794 qint64 K3bQProcessPrivate::pipeWriterBytesToWrite() const 0795 { 0796 return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0); 0797 } 0798 0799 qint64 K3bQProcessPrivate::writeToStdin(const char *data, qint64 maxlen) 0800 { 0801 Q_Q(K3bQProcess); 0802 0803 #if defined(Q_OS_WINCE) 0804 processError = K3bQProcess::WriteError; 0805 q->setErrorString(K3bQProcess::tr("Error writing to process")); 0806 emit q->error(processError); 0807 return -1; 0808 #endif 0809 0810 if (!pipeWriter) { 0811 pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q); 0812 pipeWriter->start(); 0813 } 0814 0815 return pipeWriter->write(data, maxlen); 0816 } 0817 0818 bool K3bQProcessPrivate::waitForWrite(int msecs) 0819 { 0820 Q_Q(K3bQProcess); 0821 0822 if (!pipeWriter || pipeWriter->waitForWrite(msecs)) 0823 return true; 0824 0825 processError = QProcess::Timedout; 0826 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0827 return false; 0828 } 0829 0830 void K3bQProcessPrivate::_q_notified() 0831 { 0832 notifier->stop(); 0833 0834 if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0))) 0835 _q_canWrite(); 0836 0837 if (bytesAvailableFromStdout()) 0838 _q_canReadStandardOutput(); 0839 0840 if (bytesAvailableFromStderr()) 0841 _q_canReadStandardError(); 0842 0843 if (processState != QProcess::NotRunning) 0844 notifier->start(NOTIFYTIMEOUT); 0845 } 0846 0847 bool K3bQProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid) 0848 { 0849 #if defined(Q_OS_WINCE) 0850 Q_UNUSED(workingDir); 0851 QString args = qt_create_commandline(QString(), arguments); 0852 #else 0853 QString args = qt_create_commandline(program, arguments); 0854 #endif 0855 0856 bool success = false; 0857 0858 PROCESS_INFORMATION pinfo; 0859 0860 #ifdef UNICODE 0861 if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { 0862 #if defined(Q_OS_WINCE) 0863 QString fullPathProgram = program; 0864 if (!QDir::isAbsolutePath(fullPathProgram)) 0865 fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/"))); 0866 fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); 0867 success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), 0868 (WCHAR*)args.utf16(), 0869 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo); 0870 #else 0871 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, 0872 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0873 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0874 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0875 }; 0876 success = CreateProcessW(0, (WCHAR*)args.utf16(), 0877 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, 0878 workingDir.isEmpty() ? (const WCHAR *)0 : (const WCHAR *)workingDir.utf16(), 0879 &startupInfo, &pinfo); 0880 #endif 0881 } else 0882 #endif // UNICODE 0883 { 0884 #ifndef Q_OS_WINCE 0885 STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0, 0886 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0887 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0888 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0889 }; 0890 success = CreateProcessA(0, args.toLocal8Bit().data(), 0891 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0892 workingDir.isEmpty() ? (LPCSTR)0 : workingDir.toLocal8Bit().constData(), 0893 &startupInfo, &pinfo); 0894 #endif // Q_OS_WINCE 0895 } 0896 0897 if (success) { 0898 CloseHandle(pinfo.hThread); 0899 CloseHandle(pinfo.hProcess); 0900 if (pid) 0901 *pid = pinfo.dwProcessId; 0902 } 0903 0904 return success; 0905 } 0906 0907 QT_END_NAMESPACE 0908 0909 #endif // QT_NO_PROCESS