Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 SetLastError(dwError);
144 return false;
145 }
146 }
147
148 secAtt.bInheritHandle = TRUE;
149 const HANDLE hClient = CreateFile(pipeName,
150 (isInputPipe ? (GENERIC_READ | FILE_WRITE_ATTRIBUTES)
151 : GENERIC_WRITE),
152 0,
153 &secAtt,
154 OPEN_EXISTING,
155 FILE_FLAG_OVERLAPPED,
156 NULL);
157 if (hClient == INVALID_HANDLE_VALUE) {
158 DWORD dwError = GetLastError();
159 qErrnoWarning("QProcess: CreateFile failed.");
160 CloseHandle(hServer);
161 SetLastError(dwError);
162 return false;
163 }
164
165 // Wait until connection is in place.
166 OVERLAPPED overlapped;
167 ZeroMemory(&overlapped, sizeof(overlapped));
168 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
169 if (ConnectNamedPipe(hServer, &overlapped) == 0) {
170 DWORD dwError = GetLastError();
171 switch (dwError) {
172 case ERROR_PIPE_CONNECTED:
173 break;
174 case ERROR_IO_PENDING:
175 WaitForSingleObject(overlapped.hEvent, INFINITE);
176 break;
177 default:
178 dwError = GetLastError();
179 qErrnoWarning(dwError, "QProcess: ConnectNamedPipe failed.");
180 CloseHandle(overlapped.hEvent);
181 CloseHandle(hClient);
182 CloseHandle(hServer);
183 SetLastError(dwError);
184 return false;
185 }
186 }
187 CloseHandle(overlapped.hEvent);
188
189 if (isInputPipe) {
190 pipe[0] = hClient;
191 pipe[1] = hServer;
192 } else {
193 pipe[0] = hServer;
194 pipe[1] = hClient;
195 }
196 return true;
197}
198
199/*
200 Create the pipes to a QProcessPrivate::Channel.
201*/
202bool QProcessPrivate::openChannel(Channel &channel)
203{
204 Q_Q(QProcess);
205
206 switch (channel.type) {
207 case Channel::Normal: {
208 // we're piping this channel to our own process
209 if (&channel == &stdinChannel) {
210 if (!qt_create_pipe(channel.pipe, true, FALSE)) {
211 setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
212 return false;
213 }
214 return true;
215 }
216
217 if (&channel == &stdoutChannel) {
218 if (!stdoutChannel.reader) {
219 stdoutChannel.reader = new QWindowsPipeReader(q);
220 q->connect(stdoutChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
221 }
222 } else /* if (&channel == &stderrChannel) */ {
223 if (!stderrChannel.reader) {
224 stderrChannel.reader = new QWindowsPipeReader(q);
225 q->connect(stderrChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
226 }
227 }
228 if (!qt_create_pipe(channel.pipe, false, FALSE)) {
229 setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
230 return false;
231 }
232
233 channel.reader->setHandle(channel.pipe[0]);
234 channel.reader->startAsyncRead();
235 return true;
236 }
237 case Channel::Redirect: {
238 // we're redirecting the channel to/from a file
239 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
240
241 if (&channel == &stdinChannel) {
242 // try to open in read-only mode
243 channel.pipe[1] = INVALID_Q_PIPE;
244 channel.pipe[0] =
245 CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
246 GENERIC_READ,
247 FILE_SHARE_READ | FILE_SHARE_WRITE,
248 &secAtt,
249 OPEN_EXISTING,
250 FILE_ATTRIBUTE_NORMAL,
251 NULL);
252
253 if (channel.pipe[0] != INVALID_Q_PIPE)
254 return true;
255
256 setErrorAndEmit(QProcess::FailedToStart,
257 QProcess::tr("Could not open input redirection for reading"));
258 } else {
259 // open in write mode
260 channel.pipe[0] = INVALID_Q_PIPE;
261 channel.pipe[1] =
262 CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
263 GENERIC_WRITE,
264 FILE_SHARE_READ | FILE_SHARE_WRITE,
265 &secAtt,
266 channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
267 FILE_ATTRIBUTE_NORMAL,
268 NULL);
269
270 if (channel.pipe[1] != INVALID_Q_PIPE) {
271 if (channel.append) {
272 SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
273 }
274 return true;
275 }
276
277 setErrorAndEmit(QProcess::FailedToStart,
278 QProcess::tr("Could not open output redirection for writing"));
279 }
280 return false;
281 }
282 case Channel::PipeSource: {
283 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
284 // we are the source
285 Channel *source = &channel;
286 Channel *sink = &channel.process->stdinChannel;
287
288 if (source->pipe[1] != INVALID_Q_PIPE) {
289 // already constructed by the sink
290 return true;
291 }
292
293 Q_ASSERT(source == &stdoutChannel);
294 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
295
296 if (!qt_create_pipe(source->pipe, /* in = */ false, TRUE)) { // source is stdout
297 setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
298 return false;
299 }
300
301 sink->pipe[0] = source->pipe[0];
302 source->pipe[0] = INVALID_Q_PIPE;
303
304 return true;
305 }
306 case Channel::PipeSink: { // we are the sink;
307 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
308 Channel *source = &channel.process->stdoutChannel;
309 Channel *sink = &channel;
310
311 if (sink->pipe[0] != INVALID_Q_PIPE) {
312 // already constructed by the source
313 return true;
314 }
315 Q_ASSERT(sink == &stdinChannel);
316 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
317
318 if (!qt_create_pipe(sink->pipe, /* in = */ true, TRUE)) { // sink is stdin
319 setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
320 return false;
321 }
322
323 source->pipe[1] = sink->pipe[1];
324 sink->pipe[1] = INVALID_Q_PIPE;
325
326 return true;
327 }
328 } // switch (channel.type)
329 return false;
330}
331
332void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
333{
334 if (pipe[0] != INVALID_Q_PIPE) {
335 CloseHandle(pipe[0]);
336 pipe[0] = INVALID_Q_PIPE;
337 }
338 if (pipe[1] != INVALID_Q_PIPE) {
339 CloseHandle(pipe[1]);
340 pipe[1] = INVALID_Q_PIPE;
341 }
342}
343
344void QProcessPrivate::closeChannel(Channel *channel)
345{
346 if (channel == &stdinChannel) {
347 delete channel->writer;
348 channel->writer = nullptr;
349 } else {
350 delete channel->reader;
351 channel->reader = nullptr;
352 }
353 destroyPipe(channel->pipe);
354}
355
356void QProcessPrivate::cleanup()
357{
358 q_func()->setProcessState(QProcess::NotRunning);
359
360 closeChannels();
361 delete processFinishedNotifier;
362 processFinishedNotifier = nullptr;
363 if (pid) {
364 CloseHandle(pid->hThread);
365 CloseHandle(pid->hProcess);
366 delete pid;
367 pid = nullptr;
368 }
369}
370
371static QString qt_create_commandline(const QString &program, const QStringList &arguments,
372 const QString &nativeArguments)
373{
375 if (!program.isEmpty()) {
376 QString programName = program;
377 if (!programName.startsWith(u'\"') && !programName.endsWith(u'\"') && programName.contains(u' '))
378 programName = u'\"' + programName + u'\"';
379 programName.replace(u'/', u'\\');
380
381 // add the program as the first arg ... it works better
382 args = programName + u' ';
383 }
384
385 for (qsizetype i = 0; i < arguments.size(); ++i) {
386 QString tmp = arguments.at(i);
387 // Quotes are escaped and their preceding backslashes are doubled.
388 qsizetype index = tmp.indexOf(u'"');
389 while (index >= 0) {
390 // Escape quote
391 tmp.insert(index++, u'\\');
392 // Double preceding backslashes (ignoring the one we just inserted)
393 for (qsizetype i = index - 2 ; i >= 0 && tmp.at(i) == u'\\' ; --i) {
394 tmp.insert(i, u'\\');
395 index++;
396 }
397 index = tmp.indexOf(u'"', index + 1);
398 }
399 if (tmp.isEmpty() || tmp.contains(u' ') || tmp.contains(u'\t')) {
400 // The argument must not end with a \ since this would be interpreted
401 // as escaping the quote -- rather put the \ behind the quote: e.g.
402 // rather use "foo"\ than "foo\"
403 qsizetype i = tmp.length();
404 while (i > 0 && tmp.at(i - 1) == u'\\')
405 --i;
406 tmp.insert(i, u'"');
407 tmp.prepend(u'"');
408 }
409 args += u' ' + tmp;
410 }
411
412 if (!nativeArguments.isEmpty()) {
413 if (!args.isEmpty())
414 args += u' ';
415 args += nativeArguments;
416 }
417
418 return args;
419}
420
421static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &environment)
422{
423 QByteArray envlist;
425
426 // add PATH if necessary (for DLL loading)
427 QProcessEnvironmentPrivate::Key pathKey("PATH"_L1);
428 if (!copy.contains(pathKey)) {
429 QByteArray path = qgetenv("PATH");
430 if (!path.isEmpty())
431 copy.insert(pathKey, QString::fromLocal8Bit(path));
432 }
433
434 // add systemroot if needed
435 QProcessEnvironmentPrivate::Key rootKey("SystemRoot"_L1);
436 if (!copy.contains(rootKey)) {
437 QByteArray systemRoot = qgetenv("SystemRoot");
438 if (!systemRoot.isEmpty())
439 copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
440 }
441
442 qsizetype pos = 0;
443 auto it = copy.constBegin();
444 const auto end = copy.constEnd();
445
446 static const wchar_t equal = L'=';
447 static const wchar_t nul = L'\0';
448
449 for (; it != end; ++it) {
450 qsizetype tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
451 // ignore empty strings
452 if (tmpSize == sizeof(wchar_t) * 2)
453 continue;
454 envlist.resize(envlist.size() + tmpSize);
455
456 tmpSize = it.key().length() * sizeof(wchar_t);
457 memcpy(envlist.data() + pos, it.key().data(), tmpSize);
458 pos += tmpSize;
459
460 memcpy(envlist.data() + pos, &equal, sizeof(wchar_t));
461 pos += sizeof(wchar_t);
462
463 tmpSize = it.value().length() * sizeof(wchar_t);
464 memcpy(envlist.data() + pos, it.value().data(), tmpSize);
465 pos += tmpSize;
466
467 memcpy(envlist.data() + pos, &nul, sizeof(wchar_t));
468 pos += sizeof(wchar_t);
469 }
470 // add the 2 terminating 0 (actually 4, just to be on the safe side)
471 envlist.resize(envlist.size() + 4);
472 envlist[pos++] = 0;
473 envlist[pos++] = 0;
474 envlist[pos++] = 0;
475 envlist[pos++] = 0;
476
477 return envlist;
478}
479
480static Q_PIPE pipeOrStdHandle(Q_PIPE pipe, DWORD handleNumber)
481{
482 return pipe != INVALID_Q_PIPE ? pipe : GetStdHandle(handleNumber);
483}
484
485STARTUPINFOW QProcessPrivate::createStartupInfo()
486{
487 Q_PIPE stdinPipe = pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE);
488 Q_PIPE stdoutPipe = pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE);
489 Q_PIPE stderrPipe = stderrChannel.pipe[1];
490 if (stderrPipe == INVALID_Q_PIPE) {
491 stderrPipe = (processChannelMode == QProcess::MergedChannels)
492 ? stdoutPipe
493 : GetStdHandle(STD_ERROR_HANDLE);
494 }
495
496 return STARTUPINFOW{
497 sizeof(STARTUPINFOW), 0, 0, 0,
498 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
499 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
500 0, 0, 0,
501 STARTF_USESTDHANDLES,
502 0, 0, 0,
503 stdinPipe, stdoutPipe, stderrPipe
504 };
505}
506
507bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
508{
509 if (modifyCreateProcessArgs)
510 modifyCreateProcessArgs(cpargs);
511 bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
512 cpargs->processAttributes, cpargs->threadAttributes,
513 cpargs->inheritHandles, cpargs->flags, cpargs->environment,
514 cpargs->currentDirectory, cpargs->startupInfo,
515 cpargs->processInformation);
516 if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
517 CloseHandle(stdinChannel.pipe[0]);
518 stdinChannel.pipe[0] = INVALID_Q_PIPE;
519 }
520 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
521 CloseHandle(stdoutChannel.pipe[1]);
522 stdoutChannel.pipe[1] = INVALID_Q_PIPE;
523 }
524 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
525 CloseHandle(stderrChannel.pipe[1]);
526 stderrChannel.pipe[1] = INVALID_Q_PIPE;
527 }
528 return success;
529}
530
531void QProcessPrivate::startProcess()
532{
533 Q_Q(QProcess);
534
535 pid = new PROCESS_INFORMATION;
536 memset(pid, 0, sizeof(PROCESS_INFORMATION));
537
538 q->setProcessState(QProcess::Starting);
539
540 if (!openChannels()) {
541 // openChannel sets the error string
542 Q_ASSERT(!errorString.isEmpty());
543 cleanup();
544 return;
545 }
546
547 const QString args = qt_create_commandline(program, arguments, nativeArguments);
548 QByteArray envlist;
549 if (!environment.inheritsFromParent())
550 envlist = qt_create_environment(environment.d.constData()->vars);
551
552#if defined QPROCESS_DEBUG
553 qDebug("Creating process");
554 qDebug(" program : [%s]", program.toLatin1().constData());
555 qDebug(" args : %s", args.toLatin1().constData());
556 qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
557#endif
558
559 // We cannot unconditionally set the CREATE_NO_WINDOW flag, because this
560 // will render the stdout/stderr handles connected to a console useless
561 // (this typically affects ForwardedChannels mode).
562 // However, we also do not want console tools launched from a GUI app to
563 // create new console windows (behavior consistent with UNIX).
564 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
565 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
566 STARTUPINFOW startupInfo = createStartupInfo();
567 const QString nativeWorkingDirectory = QDir::toNativeSeparators(workingDirectory);
568 QProcess::CreateProcessArguments cpargs = {
569 nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
570 nullptr, nullptr, true, dwCreationFlags,
571 environment.inheritsFromParent() ? nullptr : envlist.data(),
572 nativeWorkingDirectory.isEmpty()
573 ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()),
574 &startupInfo, pid
575 };
576
577 if (!callCreateProcess(&cpargs)) {
578 // Capture the error string before we do CloseHandle below
579 QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
580 cleanup();
581 setErrorAndEmit(QProcess::FailedToStart, errorString);
582 return;
583 }
584
585 // The pipe writer may have already been created before we had
586 // the pipe handle, specifically if the user wrote data from the
587 // stateChanged() slot.
588 if (stdinChannel.writer)
589 stdinChannel.writer->setHandle(stdinChannel.pipe[1]);
590
591 q->setProcessState(QProcess::Running);
592 // User can call kill()/terminate() from the stateChanged() slot
593 // so check before proceeding
594 if (!pid)
595 return;
596
597 if (threadData.loadRelaxed()->hasEventDispatcher()) {
598 processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
599 QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
600 processFinishedNotifier->setEnabled(true);
601 }
602
603 _q_startupNotification();
604}
605
606bool QProcessPrivate::processStarted(QString * /*errorMessage*/)
607{
608 return processState == QProcess::Running;
609}
610
611qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel) const
612{
613 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
614 Q_ASSERT(channel->reader);
615
616 DWORD bytesAvail = channel->reader->bytesAvailable();
617#if defined QPROCESS_DEBUG
618 qDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld",
619 int(channel - &stdinChannel), qint64(bytesAvail));
620#endif
621 return bytesAvail;
622}
623
624qint64 QProcessPrivate::readFromChannel(const Channel *channel, char *data, qint64 maxlen)
625{
626 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
627 Q_ASSERT(channel->reader);
628 return channel->reader->read(data, maxlen);
629}
630
631static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
632{
633 DWORD currentProcId = 0;
634 GetWindowThreadProcessId(hwnd, &currentProcId);
635 if (currentProcId == (DWORD)procId)
636 PostMessage(hwnd, WM_CLOSE, 0, 0);
637
638 return TRUE;
639}
640
641void QProcessPrivate::terminateProcess()
642{
643 if (pid) {
644 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
645 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
646 }
647}
648
649void QProcessPrivate::killProcess()
650{
651 if (pid)
652 TerminateProcess(pid->hProcess, KillProcessExitCode);
653}
654
655bool QProcessPrivate::waitForStarted(const QDeadlineTimer &)
656{
657 if (processStarted())
658 return true;
659
660 if (processError == QProcess::FailedToStart)
661 return false;
662
663 setError(QProcess::Timedout);
664 return false;
665}
666
667bool QProcessPrivate::drainOutputPipes()
668{
669 bool readyReadEmitted = false;
670
671 if (stdoutChannel.reader) {
672 stdoutChannel.reader->drainAndStop();
673 readyReadEmitted = _q_canReadStandardOutput();
674 }
675 if (stderrChannel.reader) {
676 stderrChannel.reader->drainAndStop();
677 readyReadEmitted |= _q_canReadStandardError();
678 }
679
680 return readyReadEmitted;
681}
682
683bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
684{
685 forever {
686 QProcessPoller poller(*this);
687 int ret = poller.poll(deadline);
688 if (ret < 0)
689 return false;
690 if (ret == 0)
691 break;
692
693 if (stdinChannel.writer)
694 stdinChannel.writer->checkForWrite();
695
696 if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())
697 || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))
698 return true;
699
700 if (!pid)
701 return false;
702
703 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
704 bool readyReadEmitted = drainOutputPipes();
705 if (pid)
706 processFinished();
707 return readyReadEmitted;
708 }
709 }
710
711 setError(QProcess::Timedout);
712 return false;
713}
714
715bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
716{
717 forever {
718 // At entry into the loop the pipe writer's buffer can be empty to
719 // start with, in which case we fail immediately. Also, if the input
720 // pipe goes down somewhere in the code below, we avoid waiting for
721 // a full timeout.
722 if (!stdinChannel.writer || !stdinChannel.writer->isWriteOperationActive())
723 return false;
724
725 QProcessPoller poller(*this);
726 int ret = poller.poll(deadline);
727 if (ret < 0)
728 return false;
729 if (ret == 0)
730 break;
731
732 if (stdinChannel.writer->checkForWrite())
733 return true;
734
735 // If we wouldn't write anything, check if we can read stdout.
736 if (stdoutChannel.reader)
737 stdoutChannel.reader->checkForReadyRead();
738
739 // Check if we can read stderr.
740 if (stderrChannel.reader)
741 stderrChannel.reader->checkForReadyRead();
742
743 // Check if the process died while reading.
744 if (!pid)
745 return false;
746
747 // Check if the process is signaling completion.
748 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
749 drainOutputPipes();
750 if (pid)
751 processFinished();
752 return false;
753 }
754 }
755
756 setError(QProcess::Timedout);
757 return false;
758}
759
760bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
761{
762#if defined QPROCESS_DEBUG
763 qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
764#endif
765
766 forever {
767 QProcessPoller poller(*this);
768 int ret = poller.poll(deadline);
769 if (ret < 0)
770 return false;
771 if (ret == 0)
772 break;
773
774 if (stdinChannel.writer)
775 stdinChannel.writer->checkForWrite();
776 if (stdoutChannel.reader)
777 stdoutChannel.reader->checkForReadyRead();
778 if (stderrChannel.reader)
779 stderrChannel.reader->checkForReadyRead();
780
781 if (!pid)
782 return true;
783
784 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
785 drainOutputPipes();
786 if (pid)
787 processFinished();
788 return true;
789 }
790 }
791
792 setError(QProcess::Timedout);
793 return false;
794}
795
796void QProcessPrivate::findExitCode()
797{
798 DWORD theExitCode;
799 Q_ASSERT(pid);
800 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
801 exitCode = theExitCode;
802 if (exitCode == KillProcessExitCode
803 || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000))
804 exitStatus = QProcess::CrashExit;
805 else
806 exitStatus = QProcess::NormalExit;
807 }
808}
809
813qint64 QProcess::writeData(const char *data, qint64 len)
814{
815 Q_D(QProcess);
816
817 if (d->stdinChannel.closed) {
818#if defined QPROCESS_DEBUG
819 qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
820 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len);
821#endif
822 return 0;
823 }
824
825 if (!d->stdinChannel.writer) {
826 d->stdinChannel.writer = new QWindowsPipeWriter(d->stdinChannel.pipe[1], this);
828 d, &QProcessPrivate::_q_bytesWritten);
830 d, &QProcessPrivate::_q_writeFailed);
831 }
832
833 if (d->isWriteChunkCached(data, len))
834 d->stdinChannel.writer->write(*(d->currentWriteChunk));
835 else
836 d->stdinChannel.writer->write(data, len);
837
838#if defined QPROCESS_DEBUG
839 qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
840 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len, len);
841#endif
842 return len;
843}
844
845qint64 QProcessPrivate::pipeWriterBytesToWrite() const
846{
847 return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
848}
849
850void QProcessPrivate::_q_bytesWritten(qint64 bytes)
851{
852 Q_Q(QProcess);
853
854 if (!emittedBytesWritten) {
855 QScopedValueRollback<bool> guard(emittedBytesWritten, true);
856 emit q->bytesWritten(bytes);
857 }
858 if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
859 closeWriteChannel();
860}
861
862void QProcessPrivate::_q_writeFailed()
863{
864 closeWriteChannel();
865 setErrorAndEmit(QProcess::WriteError);
866}
867
868// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
869// with ERROR_ELEVATION_REQUIRED.
870static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
871 const QString &nativeArguments,
872 const QString &workingDir, qint64 *pid)
873{
874 const QString args = qt_create_commandline(QString(), // needs arguments only
875 arguments, nativeArguments);
876 SHELLEXECUTEINFOW shellExecuteExInfo;
877 memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
878 shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
879 shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME;
880 shellExecuteExInfo.lpClass = L"exefile";
881 shellExecuteExInfo.lpVerb = L"runas";
882 const QString program = QDir::toNativeSeparators(programIn);
883 shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16());
884 if (!args.isEmpty())
885 shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16());
886 if (!workingDir.isEmpty())
887 shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16());
888 shellExecuteExInfo.nShow = SW_SHOWNORMAL;
889
890 if (!ShellExecuteExW(&shellExecuteExInfo))
891 return false;
892 if (pid)
893 *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess));
894 CloseHandle(shellExecuteExInfo.hProcess);
895 return true;
896}
897
898bool QProcessPrivate::startDetached(qint64 *pid)
899{
900 static const DWORD errorElevationRequired = 740;
901
902 if (!openChannelsForDetached()) {
903 // openChannel sets the error string
904 closeChannels();
905 return false;
906 }
907
908 QString args = qt_create_commandline(program, arguments, nativeArguments);
909 bool success = false;
910 PROCESS_INFORMATION pinfo;
911
912 void *envPtr = nullptr;
913 QByteArray envlist;
914 if (!environment.inheritsFromParent()) {
915 envlist = qt_create_environment(environment.d.constData()->vars);
916 envPtr = envlist.data();
917 }
918
919 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
920 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
921 STARTUPINFOW startupInfo = createStartupInfo();
922 QProcess::CreateProcessArguments cpargs = {
923 nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
924 nullptr, nullptr, true, dwCreationFlags, envPtr,
925 workingDirectory.isEmpty()
926 ? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
927 &startupInfo, &pinfo
928 };
929 success = callCreateProcess(&cpargs);
930
931 if (success) {
932 CloseHandle(pinfo.hThread);
933 CloseHandle(pinfo.hProcess);
934 if (pid)
935 *pid = pinfo.dwProcessId;
936 } else if (GetLastError() == errorElevationRequired) {
937 if (envPtr)
938 qWarning("QProcess: custom environment will be ignored for detached elevated process.");
939 if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
940 || !stderrChannel.file.isEmpty()) {
941 qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
942 }
943 success = startDetachedUacPrompt(program, arguments, nativeArguments,
944 workingDirectory, pid);
945 }
946 if (!success) {
947 if (pid)
948 *pid = -1;
949 setErrorAndEmit(QProcess::FailedToStart);
950 }
951
952 closeChannels();
953 return success;
954}
955
956#endif // QT_CONFIG(process)
957
IOBluetoothL2CAPChannel * channel
\inmodule QtCore
Definition qbytearray.h:57
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:397
const_pointer constData() const noexcept
Definition qlist.h:433
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool isEmpty() const
Definition qmap.h:269
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:299
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
Definition qprocess.h:32
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:129
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
QString & prepend(QChar c)
Definition qstring.h:478
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
\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:24
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:164
#define qWarning
Definition qlogging.h:166
return ret
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint index
[2]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint program
GLuint name
GLsizei GLsizei GLchar * source
GLuint entry
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
GLenum GLsizei len
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:35
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
static bool equal(const QChar *a, int l, const char *b)
Definition qurlidna.cpp:338
QDeadlineTimer deadline(30s)
QObject::connect nullptr
QJSValueList args