123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967 | // 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"#include"qwindowspipewriter_p.h"#include <qdatetime.h>#include <qdir.h>#include <qfileinfo.h>#include <qrandom.h>#include <qwineventnotifier.h>#include <qscopedvaluerollback.h>#include <private/qsystemlibrary_p.h>#include <private/qthread_p.h>#include"private/qfsfileengine_p.h"// for longFileName#ifndef PIPE_REJECT_REMOTE_CLIENTS#define PIPE_REJECT_REMOTE_CLIENTS 0x08#endif QT_BEGIN_NAMESPACE constexpr UINT KillProcessExitCode =0xf291;using namespaceQt::StringLiterals; QProcessEnvironment QProcessEnvironment::systemEnvironment(){ QProcessEnvironment env;// Calls to setenv() affect the low-level environment as well.// This is not the case the other way round.if(wchar_t*envStrings =GetEnvironmentStringsW()) {for(const wchar_t*entry = envStrings; *entry; ) {const int entryLen =int(wcslen(entry));// + 1 to permit magic cmd variable names starting with =if(const wchar_t*equal =wcschr(entry +1, L'=')) {int nameLen = equal - entry; QString name =QString::fromWCharArray(entry, nameLen); QString value =QString::fromWCharArray(equal +1, entryLen - nameLen -1); env.d->vars.insert(QProcessEnvironmentPrivate::Key(name), value);} entry += entryLen +1;}FreeEnvironmentStringsW(envStrings);}return env;}#if QT_CONFIG(process)namespace{struct QProcessPoller {QProcessPoller(const QProcessPrivate &proc);intpoll(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;}intQProcessPoller::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)return1;return(waitRet == WAIT_TIMEOUT) ?0: -1;}}// anonymous namespacestatic boolqt_create_pipe(Q_PIPE *pipe,bool isInputPipe, BOOL defInheritFlag){// Anomymous pipes do not support asynchronous I/O. Thus we// create named pipes for redirecting stdout, stderr and stdin.// The write handle must be non-inheritable for input pipes.// The read handle must be non-inheritable for output pipes.// When one process pipes to another (setStandardOutputProcess() was called),// both handles must be inheritable (defInheritFlag == TRUE). SECURITY_ATTRIBUTES secAtt = {sizeof(SECURITY_ATTRIBUTES),0, defInheritFlag }; HANDLE hServer;wchar_t pipeName[256];unsigned int attempts =1000; forever {_snwprintf(pipeName,sizeof(pipeName) /sizeof(pipeName[0]), L"\\\\.\\pipe\\qt-%lX-%X",long(QCoreApplication::applicationPid()),QRandomGenerator::global()->generate()); DWORD dwOpenMode = FILE_FLAG_OVERLAPPED; DWORD dwOutputBufferSize =0; DWORD dwInputBufferSize =0;const DWORD dwPipeBufferSize =1024*1024;if(isInputPipe) { dwOpenMode |= PIPE_ACCESS_OUTBOUND; dwOutputBufferSize = dwPipeBufferSize;}else{ dwOpenMode |= PIPE_ACCESS_INBOUND; dwInputBufferSize = dwPipeBufferSize;} DWORD dwPipeFlags = PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS; hServer =CreateNamedPipe(pipeName, dwOpenMode, dwPipeFlags,1,// only one pipe instance dwOutputBufferSize, dwInputBufferSize,0,&secAtt);if(hServer != INVALID_HANDLE_VALUE)break; DWORD dwError =GetLastError();if(dwError != ERROR_PIPE_BUSY || !--attempts) {qErrnoWarning(dwError,"QProcess: CreateNamedPipe failed.");SetLastError(dwError);return false;}} secAtt.bInheritHandle = TRUE;const HANDLE hClient =CreateFile(pipeName,(isInputPipe ? (GENERIC_READ | FILE_WRITE_ATTRIBUTES): GENERIC_WRITE),0,&secAtt, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);if(hClient == INVALID_HANDLE_VALUE) { DWORD dwError =GetLastError();qErrnoWarning("QProcess: CreateFile failed.");CloseHandle(hServer);SetLastError(dwError);return false;}// Wait until connection is in place. OVERLAPPED overlapped;ZeroMemory(&overlapped,sizeof(overlapped)); overlapped.hEvent =CreateEvent(NULL, TRUE, FALSE, NULL);if(ConnectNamedPipe(hServer, &overlapped) ==0) { DWORD dwError =GetLastError();switch(dwError) {case ERROR_PIPE_CONNECTED:break;case ERROR_IO_PENDING:WaitForSingleObject(overlapped.hEvent, INFINITE);break;default: dwError =GetLastError();qErrnoWarning(dwError,"QProcess: ConnectNamedPipe failed.");CloseHandle(overlapped.hEvent);CloseHandle(hClient);CloseHandle(hServer);SetLastError(dwError);return false;}}CloseHandle(overlapped.hEvent);if(isInputPipe) { pipe[0] = hClient; pipe[1] = hServer;}else{ pipe[0] = hServer; pipe[1] = hClient;}return true;}/* Create the pipes to a QProcessPrivate::Channel.*/boolQProcessPrivate::openChannel(Channel &channel){Q_Q(QProcess);switch(channel.type) {caseChannel::Normal: {// we're piping this channel to our own processif(&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) { stdoutChannel.reader =newQWindowsPipeReader(q); q->connect(stdoutChannel.reader,SIGNAL(readyRead()),SLOT(_q_canReadStandardOutput()));}}else/* if (&channel == &stderrChannel) */{if(!stderrChannel.reader) { stderrChannel.reader =newQWindowsPipeReader(q); q->connect(stderrChannel.reader,SIGNAL(readyRead()),SLOT(_q_canReadStandardError()));}}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();return true;}caseChannel::Redirect: {// we're redirecting the channel to/from a file SECURITY_ATTRIBUTES secAtt = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };if(&channel == &stdinChannel) {// try to open in read-only mode channel.pipe[1] = INVALID_Q_PIPE; channel.pipe[0] =CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,&secAtt, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if(channel.pipe[0] != INVALID_Q_PIPE)return true;setErrorAndEmit(QProcess::FailedToStart,QProcess::tr("Could not open input redirection for reading"));}else{// open in write mode channel.pipe[0] = INVALID_Q_PIPE; channel.pipe[1] =CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,&secAtt, channel.append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(channel.pipe[1] != INVALID_Q_PIPE) {if(channel.append) {SetFilePointer(channel.pipe[1],0, NULL, FILE_END);}return true;}setErrorAndEmit(QProcess::FailedToStart,QProcess::tr("Could not open output redirection for writing"));}return false;}caseChannel::PipeSource: {Q_ASSERT_X(channel.process,"QProcess::start","Internal error");// we are the source Channel *source = &channel; Channel *sink = &channel.process->stdinChannel;if(source->pipe[1] != INVALID_Q_PIPE) {// already constructed by the sinkreturn true;}Q_ASSERT(source == &stdoutChannel);Q_ASSERT(sink->process ==this&& sink->type ==Channel::PipeSink);if(!qt_create_pipe(source->pipe,/* in = */false, TRUE)) {// source is stdoutsetErrorAndEmit(QProcess::FailedToStart,"pipe: "_L1 +qt_error_string(errno));return false;} sink->pipe[0] = source->pipe[0]; source->pipe[0] = INVALID_Q_PIPE;return true;}caseChannel::PipeSink: {// we are the sink;Q_ASSERT_X(channel.process,"QProcess::start","Internal error"); Channel *source = &channel.process->stdoutChannel; Channel *sink = &channel;if(sink->pipe[0] != INVALID_Q_PIPE) {// already constructed by the sourcereturn true;}Q_ASSERT(sink == &stdinChannel);Q_ASSERT(source->process ==this&& source->type ==Channel::PipeSource);if(!qt_create_pipe(sink->pipe,/* in = */true, TRUE)) {// sink is stdinsetErrorAndEmit(QProcess::FailedToStart,"pipe: "_L1 +qt_error_string(errno));return false;} source->pipe[1] = sink->pipe[1]; sink->pipe[1] = INVALID_Q_PIPE;return true;}}// switch (channel.type)return false;}voidQProcessPrivate::destroyPipe(Q_PIPE pipe[2]){if(pipe[0] != INVALID_Q_PIPE) {CloseHandle(pipe[0]); pipe[0] = INVALID_Q_PIPE;}if(pipe[1] != INVALID_Q_PIPE) {CloseHandle(pipe[1]); pipe[1] = INVALID_Q_PIPE;}}voidQProcessPrivate::closeChannel(Channel *channel){if(channel == &stdinChannel) {delete channel->writer; channel->writer =nullptr;}else{delete channel->reader; channel->reader =nullptr;}destroyPipe(channel->pipe);}voidQProcessPrivate::cleanup(){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,const QString &nativeArguments){ QString args;if(!program.isEmpty()) { QString programName = program;if(!programName.startsWith(u'\"') && !programName.endsWith(u'\"') && programName.contains(u' ')) programName = u'\"'+ programName + u'\"'; programName.replace(u'/', u'\\');// add the program as the first arg ... it works better args = programName + u' ';}for(qsizetype i =0; i < arguments.size(); ++i) { QString tmp = arguments.at(i);// Quotes are escaped and their preceding backslashes are doubled. qsizetype index = tmp.indexOf(u'"');while(index >=0) {// Escape quote tmp.insert(index++, u'\\');// Double preceding backslashes (ignoring the one we just inserted)for(qsizetype i = index -2; i >=0&& tmp.at(i) == u'\\'; --i) { tmp.insert(i, u'\\'); index++;} index = tmp.indexOf(u'"', index +1);}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\" qsizetype i = tmp.length();while(i >0&& tmp.at(i -1) == u'\\')--i; tmp.insert(i, u'"'); tmp.prepend(u'"');} args += u' '+ tmp;}if(!nativeArguments.isEmpty()) {if(!args.isEmpty()) args += u' '; args += nativeArguments;}return args;}static QByteArray qt_create_environment(constQProcessEnvironmentPrivate::Map &environment){ QByteArray envlist;QProcessEnvironmentPrivate::Map copy = environment;// 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));}// add systemroot if neededQProcessEnvironmentPrivate::Key rootKey("SystemRoot"_L1);if(!copy.contains(rootKey)) { QByteArray systemRoot =qgetenv("SystemRoot");if(!systemRoot.isEmpty()) copy.insert(rootKey,QString::fromLocal8Bit(systemRoot));} qsizetype pos =0;auto it = copy.constBegin();constauto end = copy.constEnd();static const wchar_t equal = L'=';static const wchar_t nul = L'\0';for(; it != end; ++it) { qsizetype tmpSize =sizeof(wchar_t) * (it.key().length() + it.value().length() +2);// ignore empty stringsif(tmpSize ==sizeof(wchar_t) *2)continue; envlist.resize(envlist.size() + tmpSize); tmpSize = it.key().length() *sizeof(wchar_t);memcpy(envlist.data() + pos, it.key().data(), tmpSize); pos += tmpSize;memcpy(envlist.data() + pos, &equal,sizeof(wchar_t)); pos +=sizeof(wchar_t); 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;}static Q_PIPE pipeOrStdHandle(Q_PIPE pipe, DWORD handleNumber){return pipe != INVALID_Q_PIPE ? pipe :GetStdHandle(handleNumber);} STARTUPINFOW QProcessPrivate::createStartupInfo(){ Q_PIPE stdinPipe =pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE); Q_PIPE stdoutPipe =pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE); Q_PIPE stderrPipe = stderrChannel.pipe[1];if(stderrPipe == INVALID_Q_PIPE) { stderrPipe = (processChannelMode ==QProcess::MergedChannels)? stdoutPipe :GetStdHandle(STD_ERROR_HANDLE);}return STARTUPINFOW{sizeof(STARTUPINFOW),0,0,0,(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,0,0,0, STARTF_USESTDHANDLES,0,0,0, stdinPipe, stdoutPipe, stderrPipe };}boolQProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs){if(modifyCreateProcessArgs)modifyCreateProcessArgs(cpargs);bool success =CreateProcess(cpargs->applicationName, cpargs->arguments, cpargs->processAttributes, cpargs->threadAttributes, cpargs->inheritHandles, cpargs->flags, cpargs->environment, cpargs->currentDirectory, cpargs->startupInfo, cpargs->processInformation);if(!success) {// don't CloseHandle here (we'll do that in cleanup()) so GetLastError()// remains unmodifiedreturn false;}if(stdinChannel.pipe[0] != INVALID_Q_PIPE) {CloseHandle(stdinChannel.pipe[0]); stdinChannel.pipe[0] = INVALID_Q_PIPE;}if(stdoutChannel.pipe[1] != INVALID_Q_PIPE) {CloseHandle(stdoutChannel.pipe[1]); stdoutChannel.pipe[1] = INVALID_Q_PIPE;}if(stderrChannel.pipe[1] != INVALID_Q_PIPE) {CloseHandle(stderrChannel.pipe[1]); stderrChannel.pipe[1] = INVALID_Q_PIPE;}return success;}voidQProcessPrivate::startProcess(){Q_Q(QProcess); pid =new PROCESS_INFORMATION;memset(pid,0,sizeof(PROCESS_INFORMATION)); q->setProcessState(QProcess::Starting);if(!openChannels()) {// openChannel sets the error stringQ_ASSERT(!errorString.isEmpty());cleanup();return;} QString args =qt_create_commandline(program, arguments, nativeArguments); QByteArray envlist;if(!environment.inheritsFromParent()) envlist =qt_create_environment(environment.d.constData()->vars);#if defined QPROCESS_DEBUGqDebug("Creating process");qDebug(" program : [%s]", program.toLatin1().constData());qDebug(" args : %s", args.toLatin1().constData());qDebug(" pass environment : %s", environment.isEmpty() ?"no":"yes");#endif// We cannot unconditionally set the CREATE_NO_WINDOW flag, because this// will render the stdout/stderr handles connected to a console useless// (this typically affects ForwardedChannels mode).// However, we also do not want console tools launched from a GUI app to// create new console windows (behavior consistent with UNIX). DWORD dwCreationFlags = (GetConsoleWindow() ?0: CREATE_NO_WINDOW); dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; STARTUPINFOW startupInfo =createStartupInfo();const QString nativeWorkingDirectory =QDir::toNativeSeparators(workingDirectory);QProcess::CreateProcessArguments cpargs = {nullptr,reinterpret_cast<wchar_t*>(args.data_ptr().data()),nullptr,nullptr,true, dwCreationFlags, environment.inheritsFromParent() ?nullptr: envlist.data(), nativeWorkingDirectory.isEmpty()?nullptr:reinterpret_cast<const wchar_t*>(nativeWorkingDirectory.utf16()),&startupInfo, pid };if(!callCreateProcess(&cpargs)) {// Capture the error string before we do CloseHandle below QString errorString =qt_error_string();cleanup();setErrorAndEmit(QProcess::FailedToStart,QProcess::tr("Process failed to start: %1").arg(errorString));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 proceedingif(!pid)return;if(threadData.loadRelaxed()->hasEventDispatcher()) { processFinishedNotifier =newQWinEventNotifier(pid->hProcess, q);QObject::connect(processFinishedNotifier,SIGNAL(activated(HANDLE)), q,SLOT(_q_processDied())); processFinishedNotifier->setEnabled(true);}_q_startupNotification();}boolQProcessPrivate::processStarted(QString */*errorMessage*/){return processState ==QProcess::Running;} qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel)const{Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);Q_ASSERT(channel->reader); DWORD bytesAvail = channel->reader->bytesAvailable();#if defined QPROCESS_DEBUGqDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld",int(channel - &stdinChannel),qint64(bytesAvail));#endifreturn bytesAvail;} qint64 QProcessPrivate::readFromChannel(const Channel *channel,char*data, qint64 maxlen){Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);Q_ASSERT(channel->reader);return channel->reader->read(data, maxlen);}static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId){ DWORD currentProcId =0;GetWindowThreadProcessId(hwnd, ¤tProcId);if(currentProcId == (DWORD)procId)PostMessage(hwnd, WM_CLOSE,0,0);return TRUE;}voidQProcessPrivate::terminateProcess(){if(pid) {EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);PostThreadMessage(pid->dwThreadId, WM_CLOSE,0,0);}}voidQProcessPrivate::killProcess(){if(pid)TerminateProcess(pid->hProcess, KillProcessExitCode);}boolQProcessPrivate::waitForStarted(const QDeadlineTimer &){if(processStarted())return true;if(processError ==QProcess::FailedToStart)return false;setError(QProcess::Timedout);return false;}boolQProcessPrivate::drainOutputPipes(){bool readyReadEmitted =false;if(stdoutChannel.reader) { stdoutChannel.reader->drainAndStop(); readyReadEmitted =_q_canReadStandardOutput();}if(stderrChannel.reader) { stderrChannel.reader->drainAndStop(); readyReadEmitted |=_q_canReadStandardError();}return readyReadEmitted;}boolQProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline){ forever { QProcessPoller poller(*this);int ret = poller.poll(deadline);if(ret <0)return false;if(ret ==0)break;if(stdinChannel.writer) stdinChannel.writer->checkForWrite();if((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())|| (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))return true;if(!pid)return false;if(WaitForSingleObject(pid->hProcess,0) == WAIT_OBJECT_0) {bool readyReadEmitted =drainOutputPipes();if(pid)processFinished();return readyReadEmitted;}}setError(QProcess::Timedout);return false;}boolQProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline){ forever {// 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; QProcessPoller poller(*this);int ret = poller.poll(deadline);if(ret <0)return false;if(ret ==0)break;if(stdinChannel.writer->checkForWrite())return true;// If we wouldn't write anything, check if we can read stdout.if(stdoutChannel.reader) stdoutChannel.reader->checkForReadyRead();// Check if we can read stderr.if(stderrChannel.reader) stderrChannel.reader->checkForReadyRead();// Check if the process died while reading.if(!pid)return false;// Check if the process is signaling completion.if(WaitForSingleObject(pid->hProcess,0) == WAIT_OBJECT_0) {drainOutputPipes();if(pid)processFinished();return false;}}setError(QProcess::Timedout);return false;}boolQProcessPrivate::waitForFinished(const QDeadlineTimer &deadline){#if defined QPROCESS_DEBUGqDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());#endif forever { QProcessPoller poller(*this);int ret = poller.poll(deadline);if(ret <0)return false;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,0) == WAIT_OBJECT_0) {drainOutputPipes();if(pid)processFinished();return true;}}setError(QProcess::Timedout);return false;}voidQProcessPrivate::findExitCode(){ DWORD theExitCode;Q_ASSERT(pid);if(GetExitCodeProcess(pid->hProcess, &theExitCode)) { exitCode = theExitCode;if(exitCode == KillProcessExitCode || (theExitCode >=0x80000000&& theExitCode <0xD0000000)) exitStatus =QProcess::CrashExit;else exitStatus =QProcess::NormalExit;}}/*! \reimp \internal*/ qint64 QProcess::writeData(const char*data, qint64 len){Q_D(QProcess);if(d->stdinChannel.closed) {#if defined QPROCESS_DEBUGqDebug("QProcess::writeData(%p\"%s\", %lld) == 0 (write channel closing)", data,QtDebugUtils::toPrintable(data, len,16).constData(), len);#endifreturn0;}if(!d->stdinChannel.writer) { d->stdinChannel.writer =newQWindowsPipeWriter(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_DEBUGqDebug("QProcess::writeData(%p\"%s\", %lld) == %lld (written to buffer)", data,QtDebugUtils::toPrintable(data, len,16).constData(), len, len);#endifreturn len;} qint64 QProcessPrivate::pipeWriterBytesToWrite()const{return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() :qint64(0);}voidQProcessPrivate::_q_bytesWritten(qint64 bytes){Q_Q(QProcess);if(!emittedBytesWritten) { QScopedValueRollback<bool>guard(emittedBytesWritten,true); emit q->bytesWritten(bytes);}if(stdinChannel.closed &&pipeWriterBytesToWrite() ==0)closeWriteChannel();}voidQProcessPrivate::_q_writeFailed(){closeWriteChannel();setErrorAndEmit(QProcess::WriteError);}// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails// with ERROR_ELEVATION_REQUIRED.static boolstartDetachedUacPrompt(const QString &programIn,const QStringList &arguments,const QString &nativeArguments,const QString &workingDir, qint64 *pid){const QString args =qt_create_commandline(QString(),// needs arguments only arguments, nativeArguments); SHELLEXECUTEINFOW shellExecuteExInfo;memset(&shellExecuteExInfo,0,sizeof(SHELLEXECUTEINFOW)); shellExecuteExInfo.cbSize =sizeof(SHELLEXECUTEINFOW); shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME; shellExecuteExInfo.lpClass = L"exefile"; shellExecuteExInfo.lpVerb = L"runas";const QString program =QDir::toNativeSeparators(programIn); shellExecuteExInfo.lpFile =reinterpret_cast<LPCWSTR>(program.utf16());if(!args.isEmpty()) shellExecuteExInfo.lpParameters =reinterpret_cast<LPCWSTR>(args.utf16());if(!workingDir.isEmpty()) shellExecuteExInfo.lpDirectory =reinterpret_cast<LPCWSTR>(workingDir.utf16()); shellExecuteExInfo.nShow = SW_SHOWNORMAL;if(!ShellExecuteExW(&shellExecuteExInfo))return false;if(pid)*pid =qint64(GetProcessId(shellExecuteExInfo.hProcess));CloseHandle(shellExecuteExInfo.hProcess);return true;}boolQProcessPrivate::startDetached(qint64 *pid){static const DWORD errorElevationRequired =740;if(!openChannelsForDetached()) {// openChannel sets the error stringcloseChannels();return false;} QString args =qt_create_commandline(program, arguments, nativeArguments);bool success =false; PROCESS_INFORMATION pinfo;void*envPtr =nullptr; QByteArray envlist;if(!environment.inheritsFromParent()) { envlist =qt_create_environment(environment.d.constData()->vars); envPtr = envlist.data();} DWORD dwCreationFlags = (GetConsoleWindow() ?0: CREATE_NO_WINDOW); dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; STARTUPINFOW startupInfo =createStartupInfo();QProcess::CreateProcessArguments cpargs = {nullptr,reinterpret_cast<wchar_t*>(args.data_ptr().data()),nullptr,nullptr,true, dwCreationFlags, envPtr, workingDirectory.isEmpty()?nullptr:reinterpret_cast<const wchar_t*>(workingDirectory.utf16()),&startupInfo, &pinfo }; success =callCreateProcess(&cpargs);if(success) {CloseHandle(pinfo.hThread);CloseHandle(pinfo.hProcess);if(pid)*pid = pinfo.dwProcessId;}else if(GetLastError() == errorElevationRequired) {if(envPtr)qWarning("QProcess: custom environment will be ignored for detached elevated process.");if(!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()|| !stderrChannel.file.isEmpty()) {qWarning("QProcess: file redirection is unsupported for detached elevated processes.");} success =startDetachedUacPrompt(program, arguments, nativeArguments, workingDirectory, pid);}if(!success) {if(pid)*pid = -1; QString errorString =qt_error_string();setErrorAndEmit(QProcess::FailedToStart,QProcess::tr("Process failed to start: %1").arg(errorString));}closeChannels();return success;}#endif// QT_CONFIG(process) QT_END_NAMESPACE
|