Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qprocess_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5//#define QPROCESS_DEBUG
6#include <qdebug.h>
7#include <private/qdebug_p.h>
8
9#include "qprocess.h"
10#include "qprocess_p.h"
13
14#include <qdatetime.h>
15#include <qdir.h>
16#include <qfileinfo.h>
17#include <qrandom.h>
18#include <qwineventnotifier.h>
20#include <private/qsystemlibrary_p.h>
21#include <private/qthread_p.h>
22
23#include "private/qfsfileengine_p.h" // for longFileName
24
25#ifndef PIPE_REJECT_REMOTE_CLIENTS
26#define PIPE_REJECT_REMOTE_CLIENTS 0x08
27#endif
28
30
31constexpr UINT KillProcessExitCode = 0xf291;
32
33using namespace Qt::StringLiterals;
34
36{
38 // Calls to setenv() affect the low-level environment as well.
39 // This is not the case the other way round.
40 if (wchar_t *envStrings = GetEnvironmentStringsW()) {
41 for (const wchar_t *entry = envStrings; *entry; ) {
42 const int entryLen = int(wcslen(entry));
43 // + 1 to permit magic cmd variable names starting with =
44 if (const wchar_t *equal = wcschr(entry + 1, L'=')) {
45 int nameLen = equal - entry;
47 QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1);
49 }
50 entry += entryLen + 1;
51 }
52 FreeEnvironmentStringsW(envStrings);
53 }
54 return env;
55}
56
57#if QT_CONFIG(process)
58
59namespace {
60struct QProcessPoller
61{
62 QProcessPoller(const QProcessPrivate &proc);
63
64 int poll(const QDeadlineTimer &deadline);
65
66 enum { maxHandles = 4 };
67 HANDLE handles[maxHandles];
68 DWORD handleCount = 0;
69};
70
71QProcessPoller::QProcessPoller(const QProcessPrivate &proc)
72{
73 if (proc.stdinChannel.writer)
74 handles[handleCount++] = proc.stdinChannel.writer->syncEvent();
75 if (proc.stdoutChannel.reader)
76 handles[handleCount++] = proc.stdoutChannel.reader->syncEvent();
77 if (proc.stderrChannel.reader)
78 handles[handleCount++] = proc.stderrChannel.reader->syncEvent();
79
80 handles[handleCount++] = proc.pid->hProcess;
81}
82
83int QProcessPoller::poll(const QDeadlineTimer &deadline)
84{
85 DWORD waitRet;
86
87 do {
88 waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
89 deadline.remainingTime(), TRUE);
90 } while (waitRet == WAIT_IO_COMPLETION);
91
92 if (waitRet - WAIT_OBJECT_0 < handleCount)
93 return 1;
94
95 return (waitRet == WAIT_TIMEOUT) ? 0 : -1;
96}
97} // anonymous namespace
98
99static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag)
100{
101 // Anomymous pipes do not support asynchronous I/O. Thus we
102 // create named pipes for redirecting stdout, stderr and stdin.
103
104 // The write handle must be non-inheritable for input pipes.
105 // The read handle must be non-inheritable for output pipes.
106 // When one process pipes to another (setStandardOutputProcess() was called),
107 // both handles must be inheritable (defInheritFlag == TRUE).
108 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), 0, defInheritFlag };
109
110 HANDLE hServer;
111 wchar_t pipeName[256];
112 unsigned int attempts = 1000;
113 forever {
114 _snwprintf(pipeName, sizeof(pipeName) / sizeof(pipeName[0]),
115 L"\\\\.\\pipe\\qt-%lX-%X", long(QCoreApplication::applicationPid()),
116 QRandomGenerator::global()->generate());
117
118 DWORD dwOpenMode = FILE_FLAG_OVERLAPPED;
119 DWORD dwOutputBufferSize = 0;
120 DWORD dwInputBufferSize = 0;
121 const DWORD dwPipeBufferSize = 1024 * 1024;
122 if (isInputPipe) {
123 dwOpenMode |= PIPE_ACCESS_OUTBOUND;
124 dwOutputBufferSize = dwPipeBufferSize;
125 } else {
126 dwOpenMode |= PIPE_ACCESS_INBOUND;
127 dwInputBufferSize = dwPipeBufferSize;
128 }
129 DWORD dwPipeFlags = PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS;
130 hServer = CreateNamedPipe(pipeName,
131 dwOpenMode,
132 dwPipeFlags,
133 1, // only one pipe instance
134 dwOutputBufferSize,
135 dwInputBufferSize,
136 0,
137 &secAtt);
138 if (hServer != INVALID_HANDLE_VALUE)
139 break;
140 DWORD dwError = GetLastError();
141 if (dwError != ERROR_PIPE_BUSY || !--attempts) {
142 qErrnoWarning(dwError, "QProcess: CreateNamedPipe failed.");
143 return false;
144 }
145 }
146
147 secAtt.bInheritHandle = TRUE;
148 const HANDLE hClient = CreateFile(pipeName,
149 (isInputPipe ? (GENERIC_READ | FILE_WRITE_ATTRIBUTES)
150 : GENERIC_WRITE),
151 0,
152 &secAtt,
153 OPEN_EXISTING,
154 FILE_FLAG_OVERLAPPED,
155 NULL);
156 if (hClient == INVALID_HANDLE_VALUE) {
157 qErrnoWarning("QProcess: CreateFile failed.");
158 CloseHandle(hServer);
159 return false;
160 }
161
162 // Wait until connection is in place.
163 OVERLAPPED overlapped;
164 ZeroMemory(&overlapped, sizeof(overlapped));
165 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
166 if (ConnectNamedPipe(hServer, &overlapped) == 0) {
167 DWORD dwError = GetLastError();
168 switch (dwError) {
169 case ERROR_PIPE_CONNECTED:
170 break;
171 case ERROR_IO_PENDING:
172 WaitForSingleObject(overlapped.hEvent, INFINITE);
173 break;
174 default:
175 qErrnoWarning(dwError, "QProcess: ConnectNamedPipe failed.");
176 CloseHandle(overlapped.hEvent);
177 CloseHandle(hClient);
178 CloseHandle(hServer);
179 return false;
180 }
181 }
182 CloseHandle(overlapped.hEvent);
183
184 if (isInputPipe) {
185 pipe[0] = hClient;
186 pipe[1] = hServer;
187 } else {
188 pipe[0] = hServer;
189 pipe[1] = hClient;
190 }
191 return true;
192}
193
194/*
195 Create the pipes to a QProcessPrivate::Channel.
196*/
197bool QProcessPrivate::openChannel(Channel &channel)
198{
199 Q_Q(QProcess);
200
201 switch (channel.type) {
202 case Channel::Normal: {
203 // we're piping this channel to our own process
204 if (&channel == &stdinChannel)
205 return qt_create_pipe(channel.pipe, true, FALSE);
206
207 if (&channel == &stdoutChannel) {
208 if (!stdoutChannel.reader) {
209 stdoutChannel.reader = new QWindowsPipeReader(q);
210 q->connect(stdoutChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
211 }
212 } else /* if (&channel == &stderrChannel) */ {
213 if (!stderrChannel.reader) {
214 stderrChannel.reader = new QWindowsPipeReader(q);
215 q->connect(stderrChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
216 }
217 }
218 if (!qt_create_pipe(channel.pipe, false, FALSE))
219 return false;
220
221 channel.reader->setHandle(channel.pipe[0]);
222 channel.reader->startAsyncRead();
223 return true;
224 }
225 case Channel::Redirect: {
226 // we're redirecting the channel to/from a file
227 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
228
229 if (&channel == &stdinChannel) {
230 // try to open in read-only mode
231 channel.pipe[1] = INVALID_Q_PIPE;
232 channel.pipe[0] =
233 CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
234 GENERIC_READ,
235 FILE_SHARE_READ | FILE_SHARE_WRITE,
236 &secAtt,
237 OPEN_EXISTING,
238 FILE_ATTRIBUTE_NORMAL,
239 NULL);
240
241 if (channel.pipe[0] != INVALID_Q_PIPE)
242 return true;
243
244 setErrorAndEmit(QProcess::FailedToStart,
245 QProcess::tr("Could not open input redirection for reading"));
246 } else {
247 // open in write mode
248 channel.pipe[0] = INVALID_Q_PIPE;
249 channel.pipe[1] =
250 CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
251 GENERIC_WRITE,
252 FILE_SHARE_READ | FILE_SHARE_WRITE,
253 &secAtt,
254 channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
255 FILE_ATTRIBUTE_NORMAL,
256 NULL);
257
258 if (channel.pipe[1] != INVALID_Q_PIPE) {
259 if (channel.append) {
260 SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
261 }
262 return true;
263 }
264
265 setErrorAndEmit(QProcess::FailedToStart,
266 QProcess::tr("Could not open output redirection for writing"));
267 }
268 cleanup();
269 return false;
270 }
271 case Channel::PipeSource: {
272 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
273 // we are the source
274 Channel *source = &channel;
275 Channel *sink = &channel.process->stdinChannel;
276
277 if (source->pipe[1] != INVALID_Q_PIPE) {
278 // already constructed by the sink
279 return true;
280 }
281
282 Q_ASSERT(source == &stdoutChannel);
283 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
284
285 if (!qt_create_pipe(source->pipe, /* in = */ false, TRUE)) // source is stdout
286 return false;
287
288 sink->pipe[0] = source->pipe[0];
289 source->pipe[0] = INVALID_Q_PIPE;
290
291 return true;
292 }
293 case Channel::PipeSink: { // we are the sink;
294 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
295 Channel *source = &channel.process->stdoutChannel;
296 Channel *sink = &channel;
297
298 if (sink->pipe[0] != INVALID_Q_PIPE) {
299 // already constructed by the source
300 return true;
301 }
302 Q_ASSERT(sink == &stdinChannel);
303 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
304
305 if (!qt_create_pipe(sink->pipe, /* in = */ true, TRUE)) // sink is stdin
306 return false;
307
308 source->pipe[1] = sink->pipe[1];
309 sink->pipe[1] = INVALID_Q_PIPE;
310
311 return true;
312 }
313 } // switch (channel.type)
314 return false;
315}
316
317void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
318{
319 if (pipe[0] != INVALID_Q_PIPE) {
320 CloseHandle(pipe[0]);
321 pipe[0] = INVALID_Q_PIPE;
322 }
323 if (pipe[1] != INVALID_Q_PIPE) {
324 CloseHandle(pipe[1]);
325 pipe[1] = INVALID_Q_PIPE;
326 }
327}
328
329void QProcessPrivate::closeChannel(Channel *channel)
330{
331 if (channel == &stdinChannel) {
332 delete channel->writer;
333 channel->writer = nullptr;
334 } else {
335 delete channel->reader;
336 channel->reader = nullptr;
337 }
338 destroyPipe(channel->pipe);
339}
340
341void QProcessPrivate::cleanup()
342{
343 q_func()->setProcessState(QProcess::NotRunning);
344
345 closeChannels();
346 delete processFinishedNotifier;
347 processFinishedNotifier = nullptr;
348 if (pid) {
349 CloseHandle(pid->hThread);
350 CloseHandle(pid->hProcess);
351 delete pid;
352 pid = nullptr;
353 }
354}
355
356static QString qt_create_commandline(const QString &program, const QStringList &arguments,
357 const QString &nativeArguments)
358{
360 if (!program.isEmpty()) {
361 QString programName = program;
362 if (!programName.startsWith(u'\"') && !programName.endsWith(u'\"') && programName.contains(u' '))
363 programName = u'\"' + programName + u'\"';
364 programName.replace(u'/', u'\\');
365
366 // add the program as the first arg ... it works better
367 args = programName + u' ';
368 }
369
370 for (qsizetype i = 0; i < arguments.size(); ++i) {
371 QString tmp = arguments.at(i);
372 // Quotes are escaped and their preceding backslashes are doubled.
373 qsizetype index = tmp.indexOf(u'"');
374 while (index >= 0) {
375 // Escape quote
376 tmp.insert(index++, u'\\');
377 // Double preceding backslashes (ignoring the one we just inserted)
378 for (qsizetype i = index - 2 ; i >= 0 && tmp.at(i) == u'\\' ; --i) {
379 tmp.insert(i, u'\\');
380 index++;
381 }
382 index = tmp.indexOf(u'"', index + 1);
383 }
384 if (tmp.isEmpty() || tmp.contains(u' ') || tmp.contains(u'\t')) {
385 // The argument must not end with a \ since this would be interpreted
386 // as escaping the quote -- rather put the \ behind the quote: e.g.
387 // rather use "foo"\ than "foo\"
388 qsizetype i = tmp.length();
389 while (i > 0 && tmp.at(i - 1) == u'\\')
390 --i;
391 tmp.insert(i, u'"');
392 tmp.prepend(u'"');
393 }
394 args += u' ' + tmp;
395 }
396
397 if (!nativeArguments.isEmpty()) {
398 if (!args.isEmpty())
399 args += u' ';
400 args += nativeArguments;
401 }
402
403 return args;
404}
405
406static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &environment)
407{
408 QByteArray envlist;
410
411 // add PATH if necessary (for DLL loading)
412 QProcessEnvironmentPrivate::Key pathKey("PATH"_L1);
413 if (!copy.contains(pathKey)) {
414 QByteArray path = qgetenv("PATH");
415 if (!path.isEmpty())
416 copy.insert(pathKey, QString::fromLocal8Bit(path));
417 }
418
419 // add systemroot if needed
420 QProcessEnvironmentPrivate::Key rootKey("SystemRoot"_L1);
421 if (!copy.contains(rootKey)) {
422 QByteArray systemRoot = qgetenv("SystemRoot");
423 if (!systemRoot.isEmpty())
424 copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
425 }
426
427 qsizetype pos = 0;
428 auto it = copy.constBegin();
429 const auto end = copy.constEnd();
430
431 static const wchar_t equal = L'=';
432 static const wchar_t nul = L'\0';
433
434 for (; it != end; ++it) {
435 qsizetype tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
436 // ignore empty strings
437 if (tmpSize == sizeof(wchar_t) * 2)
438 continue;
439 envlist.resize(envlist.size() + tmpSize);
440
441 tmpSize = it.key().length() * sizeof(wchar_t);
442 memcpy(envlist.data() + pos, it.key().data(), tmpSize);
443 pos += tmpSize;
444
445 memcpy(envlist.data() + pos, &equal, sizeof(wchar_t));
446 pos += sizeof(wchar_t);
447
448 tmpSize = it.value().length() * sizeof(wchar_t);
449 memcpy(envlist.data() + pos, it.value().data(), tmpSize);
450 pos += tmpSize;
451
452 memcpy(envlist.data() + pos, &nul, sizeof(wchar_t));
453 pos += sizeof(wchar_t);
454 }
455 // add the 2 terminating 0 (actually 4, just to be on the safe side)
456 envlist.resize(envlist.size() + 4);
457 envlist[pos++] = 0;
458 envlist[pos++] = 0;
459 envlist[pos++] = 0;
460 envlist[pos++] = 0;
461
462 return envlist;
463}
464
465static Q_PIPE pipeOrStdHandle(Q_PIPE pipe, DWORD handleNumber)
466{
467 return pipe != INVALID_Q_PIPE ? pipe : GetStdHandle(handleNumber);
468}
469
470STARTUPINFOW QProcessPrivate::createStartupInfo()
471{
472 Q_PIPE stdinPipe = pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE);
473 Q_PIPE stdoutPipe = pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE);
474 Q_PIPE stderrPipe = stderrChannel.pipe[1];
475 if (stderrPipe == INVALID_Q_PIPE) {
476 stderrPipe = (processChannelMode == QProcess::MergedChannels)
477 ? stdoutPipe
478 : GetStdHandle(STD_ERROR_HANDLE);
479 }
480
481 return STARTUPINFOW{
482 sizeof(STARTUPINFOW), 0, 0, 0,
483 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
484 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
485 0, 0, 0,
486 STARTF_USESTDHANDLES,
487 0, 0, 0,
488 stdinPipe, stdoutPipe, stderrPipe
489 };
490}
491
492bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
493{
494 if (modifyCreateProcessArgs)
495 modifyCreateProcessArgs(cpargs);
496 bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
497 cpargs->processAttributes, cpargs->threadAttributes,
498 cpargs->inheritHandles, cpargs->flags, cpargs->environment,
499 cpargs->currentDirectory, cpargs->startupInfo,
500 cpargs->processInformation);
501 if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
502 CloseHandle(stdinChannel.pipe[0]);
503 stdinChannel.pipe[0] = INVALID_Q_PIPE;
504 }
505 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
506 CloseHandle(stdoutChannel.pipe[1]);
507 stdoutChannel.pipe[1] = INVALID_Q_PIPE;
508 }
509 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
510 CloseHandle(stderrChannel.pipe[1]);
511 stderrChannel.pipe[1] = INVALID_Q_PIPE;
512 }
513 return success;
514}
515
516void QProcessPrivate::startProcess()
517{
518 Q_Q(QProcess);
519
520 pid = new PROCESS_INFORMATION;
521 memset(pid, 0, sizeof(PROCESS_INFORMATION));
522
523 q->setProcessState(QProcess::Starting);
524
525 if (!openChannels()) {
526 QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
527 cleanup();
528 setErrorAndEmit(QProcess::FailedToStart, errorString);
529 return;
530 }
531
532 const QString args = qt_create_commandline(program, arguments, nativeArguments);
533 QByteArray envlist;
534 if (!environment.inheritsFromParent())
535 envlist = qt_create_environment(environment.d.constData()->vars);
536
537#if defined QPROCESS_DEBUG
538 qDebug("Creating process");
539 qDebug(" program : [%s]", program.toLatin1().constData());
540 qDebug(" args : %s", args.toLatin1().constData());
541 qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
542#endif
543
544 // We cannot unconditionally set the CREATE_NO_WINDOW flag, because this
545 // will render the stdout/stderr handles connected to a console useless
546 // (this typically affects ForwardedChannels mode).
547 // However, we also do not want console tools launched from a GUI app to
548 // create new console windows (behavior consistent with UNIX).
549 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
550 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
551 STARTUPINFOW startupInfo = createStartupInfo();
552 const QString nativeWorkingDirectory = QDir::toNativeSeparators(workingDirectory);
553 QProcess::CreateProcessArguments cpargs = {
554 nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
555 nullptr, nullptr, true, dwCreationFlags,
556 environment.inheritsFromParent() ? nullptr : envlist.data(),
557 nativeWorkingDirectory.isEmpty()
558 ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()),
559 &startupInfo, pid
560 };
561
562 if (!callCreateProcess(&cpargs)) {
563 // Capture the error string before we do CloseHandle below
564 QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
565 cleanup();
566 setErrorAndEmit(QProcess::FailedToStart, errorString);
567 return;
568 }
569
570 // The pipe writer may have already been created before we had
571 // the pipe handle, specifically if the user wrote data from the
572 // stateChanged() slot.
573 if (stdinChannel.writer)
574 stdinChannel.writer->setHandle(stdinChannel.pipe[1]);
575
576 q->setProcessState(QProcess::Running);
577 // User can call kill()/terminate() from the stateChanged() slot
578 // so check before proceeding
579 if (!pid)
580 return;
581
582 if (threadData.loadRelaxed()->hasEventDispatcher()) {
583 processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
584 QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
585 processFinishedNotifier->setEnabled(true);
586 }
587
588 _q_startupNotification();
589}
590
591bool QProcessPrivate::processStarted(QString * /*errorMessage*/)
592{
593 return processState == QProcess::Running;
594}
595
596qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel) const
597{
598 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
599 Q_ASSERT(channel->reader);
600
601 DWORD bytesAvail = channel->reader->bytesAvailable();
602#if defined QPROCESS_DEBUG
603 qDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld",
604 int(channel - &stdinChannel), qint64(bytesAvail));
605#endif
606 return bytesAvail;
607}
608
609qint64 QProcessPrivate::readFromChannel(const Channel *channel, char *data, qint64 maxlen)
610{
611 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
612 Q_ASSERT(channel->reader);
613 return channel->reader->read(data, maxlen);
614}
615
616static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
617{
618 DWORD currentProcId = 0;
619 GetWindowThreadProcessId(hwnd, &currentProcId);
620 if (currentProcId == (DWORD)procId)
621 PostMessage(hwnd, WM_CLOSE, 0, 0);
622
623 return TRUE;
624}
625
626void QProcessPrivate::terminateProcess()
627{
628 if (pid) {
629 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
630 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
631 }
632}
633
634void QProcessPrivate::killProcess()
635{
636 if (pid)
637 TerminateProcess(pid->hProcess, KillProcessExitCode);
638}
639
640bool QProcessPrivate::waitForStarted(const QDeadlineTimer &)
641{
642 if (processStarted())
643 return true;
644
645 if (processError == QProcess::FailedToStart)
646 return false;
647
648 setError(QProcess::Timedout);
649 return false;
650}
651
652bool QProcessPrivate::drainOutputPipes()
653{
654 bool readyReadEmitted = false;
655
656 if (stdoutChannel.reader) {
657 stdoutChannel.reader->drainAndStop();
658 readyReadEmitted = _q_canReadStandardOutput();
659 }
660 if (stderrChannel.reader) {
661 stderrChannel.reader->drainAndStop();
662 readyReadEmitted |= _q_canReadStandardError();
663 }
664
665 return readyReadEmitted;
666}
667
668bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
669{
670 forever {
671 QProcessPoller poller(*this);
672 int ret = poller.poll(deadline);
673 if (ret < 0)
674 return false;
675 if (ret == 0)
676 break;
677
678 if (stdinChannel.writer)
679 stdinChannel.writer->checkForWrite();
680
681 if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())
682 || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))
683 return true;
684
685 if (!pid)
686 return false;
687
688 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
689 bool readyReadEmitted = drainOutputPipes();
690 if (pid)
691 processFinished();
692 return readyReadEmitted;
693 }
694 }
695
696 setError(QProcess::Timedout);
697 return false;
698}
699
700bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
701{
702 forever {
703 // At entry into the loop the pipe writer's buffer can be empty to
704 // start with, in which case we fail immediately. Also, if the input
705 // pipe goes down somewhere in the code below, we avoid waiting for
706 // a full timeout.
707 if (!stdinChannel.writer || !stdinChannel.writer->isWriteOperationActive())
708 return false;
709
710 QProcessPoller poller(*this);
711 int ret = poller.poll(deadline);
712 if (ret < 0)
713 return false;
714 if (ret == 0)
715 break;
716
717 if (stdinChannel.writer->checkForWrite())
718 return true;
719
720 // If we wouldn't write anything, check if we can read stdout.
721 if (stdoutChannel.reader)
722 stdoutChannel.reader->checkForReadyRead();
723
724 // Check if we can read stderr.
725 if (stderrChannel.reader)
726 stderrChannel.reader->checkForReadyRead();
727
728 // Check if the process died while reading.
729 if (!pid)
730 return false;
731
732 // Check if the process is signaling completion.
733 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
734 drainOutputPipes();
735 if (pid)
736 processFinished();
737 return false;
738 }
739 }
740
741 setError(QProcess::Timedout);
742 return false;
743}
744
745bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
746{
747#if defined QPROCESS_DEBUG
748 qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
749#endif
750
751 forever {
752 QProcessPoller poller(*this);
753 int ret = poller.poll(deadline);
754 if (ret < 0)
755 return false;
756 if (ret == 0)
757 break;
758
759 if (stdinChannel.writer)
760 stdinChannel.writer->checkForWrite();
761 if (stdoutChannel.reader)
762 stdoutChannel.reader->checkForReadyRead();
763 if (stderrChannel.reader)
764 stderrChannel.reader->checkForReadyRead();
765
766 if (!pid)
767 return true;
768
769 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
770 drainOutputPipes();
771 if (pid)
772 processFinished();
773 return true;
774 }
775 }
776
777 setError(QProcess::Timedout);
778 return false;
779}
780
781void QProcessPrivate::findExitCode()
782{
783 DWORD theExitCode;
784 Q_ASSERT(pid);
785 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
786 exitCode = theExitCode;
787 if (exitCode == KillProcessExitCode
788 || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000))
789 exitStatus = QProcess::CrashExit;
790 else
791 exitStatus = QProcess::NormalExit;
792 }
793}
794
798qint64 QProcess::writeData(const char *data, qint64 len)
799{
800 Q_D(QProcess);
801
802 if (d->stdinChannel.closed) {
803#if defined QPROCESS_DEBUG
804 qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
805 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len);
806#endif
807 return 0;
808 }
809
810 if (!d->stdinChannel.writer) {
811 d->stdinChannel.writer = new QWindowsPipeWriter(d->stdinChannel.pipe[1], this);
813 d, &QProcessPrivate::_q_bytesWritten);
815 d, &QProcessPrivate::_q_writeFailed);
816 }
817
818 if (d->isWriteChunkCached(data, len))
819 d->stdinChannel.writer->write(*(d->currentWriteChunk));
820 else
821 d->stdinChannel.writer->write(data, len);
822
823#if defined QPROCESS_DEBUG
824 qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
825 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len, len);
826#endif
827 return len;
828}
829
830qint64 QProcessPrivate::pipeWriterBytesToWrite() const
831{
832 return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
833}
834
835void QProcessPrivate::_q_bytesWritten(qint64 bytes)
836{
837 Q_Q(QProcess);
838
839 if (!emittedBytesWritten) {
840 QScopedValueRollback<bool> guard(emittedBytesWritten, true);
841 emit q->bytesWritten(bytes);
842 }
843 if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
844 closeWriteChannel();
845}
846
847void QProcessPrivate::_q_writeFailed()
848{
849 closeWriteChannel();
850 setErrorAndEmit(QProcess::WriteError);
851}
852
853// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
854// with ERROR_ELEVATION_REQUIRED.
855static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
856 const QString &nativeArguments,
857 const QString &workingDir, qint64 *pid)
858{
859 const QString args = qt_create_commandline(QString(), // needs arguments only
860 arguments, nativeArguments);
861 SHELLEXECUTEINFOW shellExecuteExInfo;
862 memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
863 shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
864 shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME;
865 shellExecuteExInfo.lpClass = L"exefile";
866 shellExecuteExInfo.lpVerb = L"runas";
867 const QString program = QDir::toNativeSeparators(programIn);
868 shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16());
869 if (!args.isEmpty())
870 shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16());
871 if (!workingDir.isEmpty())
872 shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16());
873 shellExecuteExInfo.nShow = SW_SHOWNORMAL;
874
875 if (!ShellExecuteExW(&shellExecuteExInfo))
876 return false;
877 if (pid)
878 *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess));
879 CloseHandle(shellExecuteExInfo.hProcess);
880 return true;
881}
882
883bool QProcessPrivate::startDetached(qint64 *pid)
884{
885 static const DWORD errorElevationRequired = 740;
886
887 if (!openChannelsForDetached()) {
888 // openChannel sets the error string
889 closeChannels();
890 return false;
891 }
892
893 QString args = qt_create_commandline(program, arguments, nativeArguments);
894 bool success = false;
895 PROCESS_INFORMATION pinfo;
896
897 void *envPtr = nullptr;
898 QByteArray envlist;
899 if (!environment.inheritsFromParent()) {
900 envlist = qt_create_environment(environment.d.constData()->vars);
901 envPtr = envlist.data();
902 }
903
904 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
905 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
906 STARTUPINFOW startupInfo = createStartupInfo();
907 QProcess::CreateProcessArguments cpargs = {
908 nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
909 nullptr, nullptr, true, dwCreationFlags, envPtr,
910 workingDirectory.isEmpty()
911 ? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
912 &startupInfo, &pinfo
913 };
914 success = callCreateProcess(&cpargs);
915
916 if (success) {
917 CloseHandle(pinfo.hThread);
918 CloseHandle(pinfo.hProcess);
919 if (pid)
920 *pid = pinfo.dwProcessId;
921 } else if (GetLastError() == errorElevationRequired) {
922 if (envPtr)
923 qWarning("QProcess: custom environment will be ignored for detached elevated process.");
924 if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
925 || !stderrChannel.file.isEmpty()) {
926 qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
927 }
928 success = startDetachedUacPrompt(program, arguments, nativeArguments,
929 workingDirectory, pid);
930 }
931 if (!success) {
932 if (pid)
933 *pid = -1;
934 setErrorAndEmit(QProcess::FailedToStart);
935 }
936
937 closeChannels();
938 return success;
939}
940
941#endif // QT_CONFIG(process)
942
IOBluetoothL2CAPChannel * channel
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static qint64 applicationPid() Q_DECL_CONST_FUNCTION
\inmodule QtCore
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
bool isEmpty() const noexcept
Definition qlist.h:390
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
bool isEmpty() const
Definition qmap.h:268
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:298
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
\inmodule QtCore
Definition qprocess.h:31
static QProcessEnvironment systemEnvironment()
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6737
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString & prepend(QChar c)
Definition qstring.h:411
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore
void bytesWritten(qint64 bytes)
QSet< QString >::iterator it
QList< QVariant > arguments
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr)
Q_CORE_EXPORT QByteArray toPrintable(const char *data, qint64 len, qsizetype maxSize)
Definition qdebug.cpp:29
void * HANDLE
static jboolean copy(JNIEnv *, jobject)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define forever
Definition qforeach.h:78
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
return ret
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLuint index
[2]
GLuint GLuint end
GLuint program
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLsizei GLsizei GLchar * source
GLuint entry
GLenum GLsizei len
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
int Q_PIPE
Definition qprocess_p.h:38
#define INVALID_Q_PIPE
Definition qprocess_p.h:39
QT_BEGIN_NAMESPACE constexpr UINT KillProcessExitCode
#define PIPE_REJECT_REMOTE_CLIENTS
static void setError(QJsonObject *response, const QString &msg)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define emit
unsigned long ulong
Definition qtypes.h:30
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
unsigned short ushort
Definition qtypes.h:28
static bool equal(const QChar *a, int l, const char *b)
Definition qurlidna.cpp:338
QDeadlineTimer deadline(30s)
QObject::connect nullptr
QJSValueList args