Qt 4.8
qprocess_win.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qprocess.h"
43 #include "qprocess_p.h"
44 #include "qwindowspipewriter_p.h"
45 
46 #include <qdatetime.h>
47 #include <qdir.h>
48 #include <qfileinfo.h>
49 #include <qregexp.h>
50 #include <qtimer.h>
51 #include <qthread.h>
52 #include <qmutex.h>
53 #include <qwaitcondition.h>
54 #include <private/qwineventnotifier_p.h>
55 #include <private/qthread_p.h>
56 #include <qdebug.h>
57 
58 #include "private/qfsfileengine_p.h" // for longFileName
59 
60 
61 #ifndef QT_NO_PROCESS
62 
64 
65 //#define QPROCESS_DEBUG
66 
67 #define NOTIFYTIMEOUT 100
68 
69 static void qt_create_pipe(Q_PIPE *pipe, bool in)
70 {
71  // Open the pipes. Make non-inheritable copies of input write and output
72  // read handles to avoid non-closable handles (this is done by the
73  // DuplicateHandle() call).
74 
75 #if !defined(Q_OS_WINCE)
76  SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
77 
78  HANDLE tmpHandle;
79  if (in) { // stdin
80  if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024))
81  return;
82  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
83  &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS))
84  return;
85  } else { // stdout or stderr
86  if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024))
87  return;
88  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
89  &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS))
90  return;
91  }
92 
93  CloseHandle(tmpHandle);
94 #else
95  Q_UNUSED(pipe);
96  Q_UNUSED(in);
97 #endif
98 }
99 
100 /*
101  Create the pipes to a QProcessPrivate::Channel.
102 
103  This function must be called in order: stdin, stdout, stderr
104 */
105 bool QProcessPrivate::createChannel(Channel &channel)
106 {
107  Q_Q(QProcess);
108 
110  return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
111  &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
112  }
113 
114  if (channel.type == Channel::Normal) {
115  // we're piping this channel to our own process
116  qt_create_pipe(channel.pipe, &channel == &stdinChannel);
117 
118  return true;
119  } else if (channel.type == Channel::Redirect) {
120  // we're redirecting the channel to/from a file
121  SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
122 
123  if (&channel == &stdinChannel) {
124  // try to open in read-only mode
125  channel.pipe[1] = INVALID_Q_PIPE;
126  channel.pipe[0] =
127  CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
128  GENERIC_READ,
129  FILE_SHARE_READ | FILE_SHARE_WRITE,
130  &secAtt,
131  OPEN_EXISTING,
132  FILE_ATTRIBUTE_NORMAL,
133  NULL);
134 
135  if (channel.pipe[0] != INVALID_Q_PIPE)
136  return true;
137 
138  q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
139  } else {
140  // open in write mode
141  channel.pipe[0] = INVALID_Q_PIPE;
142  channel.pipe[1] =
143  CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
144  GENERIC_WRITE,
145  FILE_SHARE_READ | FILE_SHARE_WRITE,
146  &secAtt,
147  channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
148  FILE_ATTRIBUTE_NORMAL,
149  NULL);
150 
151  if (channel.pipe[1] != INVALID_Q_PIPE) {
152  if (channel.append) {
153  SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
154  }
155  return true;
156  }
157 
158  q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
159  }
160 
161  // could not open file
163  emit q->error(processError);
164  cleanup();
165  return false;
166  } else {
167  Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
168 
169  Channel *source;
170  Channel *sink;
171 
172  if (channel.type == Channel::PipeSource) {
173  // we are the source
174  source = &channel;
175  sink = &channel.process->stdinChannel;
176 
177  if (source->pipe[1] != INVALID_Q_PIPE) {
178  // already constructed by the sink
179  // make it inheritable
180  HANDLE tmpHandle = source->pipe[1];
181  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
182  GetCurrentProcess(), &source->pipe[1],
183  0, TRUE, DUPLICATE_SAME_ACCESS))
184  return false;
185 
186  CloseHandle(tmpHandle);
187  return true;
188  }
189 
190  Q_ASSERT(source == &stdoutChannel);
191  Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
192 
193  qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
194  sink->pipe[0] = source->pipe[0];
195  source->pipe[0] = INVALID_Q_PIPE;
196 
197  return true;
198  } else {
199  // we are the sink;
200  source = &channel.process->stdoutChannel;
201  sink = &channel;
202 
203  if (sink->pipe[0] != INVALID_Q_PIPE) {
204  // already constructed by the source
205  // make it inheritable
206  HANDLE tmpHandle = sink->pipe[0];
207  if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
208  GetCurrentProcess(), &sink->pipe[0],
209  0, TRUE, DUPLICATE_SAME_ACCESS))
210  return false;
211 
212  CloseHandle(tmpHandle);
213  return true;
214  }
215  Q_ASSERT(sink == &stdinChannel);
216  Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
217 
218  qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
219  source->pipe[1] = sink->pipe[1];
220  sink->pipe[1] = INVALID_Q_PIPE;
221 
222  return true;
223  }
224  }
225 }
226 
228 {
229  if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) {
230  delete pipeWriter;
231  pipeWriter = 0;
232  }
233 
234  if (pipe[0] != INVALID_Q_PIPE) {
235  CloseHandle(pipe[0]);
236  pipe[0] = INVALID_Q_PIPE;
237  }
238  if (pipe[1] != INVALID_Q_PIPE) {
239  CloseHandle(pipe[1]);
240  pipe[1] = INVALID_Q_PIPE;
241  }
242 }
243 
244 
246 {
247  QString args;
248  if (!program.isEmpty()) {
249  QString programName = program;
250  if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
251  programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
252  programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
253 
254  // add the prgram as the first arg ... it works better
255  args = programName + QLatin1Char(' ');
256  }
257 
258  for (int i=0; i<arguments.size(); ++i) {
259  QString tmp = arguments.at(i);
260  // Quotes are escaped and their preceding backslashes are doubled.
261  tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
262  if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
263  // The argument must not end with a \ since this would be interpreted
264  // as escaping the quote -- rather put the \ behind the quote: e.g.
265  // rather use "foo"\ than "foo\"
266  int i = tmp.length();
267  while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
268  --i;
269  tmp.insert(i, QLatin1Char('"'));
270  tmp.prepend(QLatin1Char('"'));
271  }
272  args += QLatin1Char(' ') + tmp;
273  }
274  return args;
275 }
276 
278 {
280 #if !defined(Q_OS_WINCE)
281  // Calls to setenv() affect the low-level environment as well.
282  // This is not the case the other way round.
283  if (wchar_t *envStrings = GetEnvironmentStringsW()) {
284  for (const wchar_t *entry = envStrings; *entry; ) {
285  int entryLen = wcslen(entry);
286  // + 1 to permit magic cmd variable names starting with =
287  if (const wchar_t *equal = wcschr(entry + 1, L'=')) {
288  int nameLen = equal - entry;
289  QString name = QString::fromWCharArray(entry, nameLen);
290  QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1);
291  env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), value);
292  }
293  entry += entryLen + 1;
294  }
295  FreeEnvironmentStringsW(envStrings);
296  }
297 #endif
298  return env;
299 }
300 
301 #if !defined(Q_OS_WINCE)
302 static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Hash &environment)
303 {
304  QByteArray envlist;
305  if (!environment.isEmpty()) {
307 
308  // add PATH if necessary (for DLL loading)
310  if (!copy.contains(pathKey)) {
311  QByteArray path = qgetenv("PATH");
312  if (!path.isEmpty())
313  copy.insert(pathKey, QString::fromLocal8Bit(path));
314  }
315 
316  // add systemroot if needed
317  QProcessEnvironmentPrivate::Key rootKey(QLatin1String("SystemRoot"));
318  if (!copy.contains(rootKey)) {
319  QByteArray systemRoot = qgetenv("SystemRoot");
320  if (!systemRoot.isEmpty())
321  copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
322  }
323 
324  int pos = 0;
326  end = copy.constEnd();
327 
328  static const wchar_t equal = L'=';
329  static const wchar_t nul = L'\0';
330 
331  for ( ; it != end; ++it) {
332  uint tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
333  // ignore empty strings
334  if (tmpSize == sizeof(wchar_t) * 2)
335  continue;
336  envlist.resize(envlist.size() + tmpSize);
337 
338  tmpSize = it.key().length() * sizeof(wchar_t);
339  memcpy(envlist.data()+pos, it.key().utf16(), tmpSize);
340  pos += tmpSize;
341 
342  memcpy(envlist.data()+pos, &equal, sizeof(wchar_t));
343  pos += sizeof(wchar_t);
344 
345  tmpSize = it.value().length() * sizeof(wchar_t);
346  memcpy(envlist.data()+pos, it.value().utf16(), tmpSize);
347  pos += tmpSize;
348 
349  memcpy(envlist.data()+pos, &nul, sizeof(wchar_t));
350  pos += sizeof(wchar_t);
351  }
352  // add the 2 terminating 0 (actually 4, just to be on the safe side)
353  envlist.resize( envlist.size()+4 );
354  envlist[pos++] = 0;
355  envlist[pos++] = 0;
356  envlist[pos++] = 0;
357  envlist[pos++] = 0;
358  }
359  return envlist;
360 }
361 #endif
362 
364 {
365  Q_Q(QProcess);
366 
367  bool success = false;
368 
369  if (pid) {
370  CloseHandle(pid->hThread);
371  CloseHandle(pid->hProcess);
372  delete pid;
373  pid = 0;
374  }
375  pid = new PROCESS_INFORMATION;
376  memset(pid, 0, sizeof(PROCESS_INFORMATION));
377 
378  q->setProcessState(QProcess::Starting);
379 
380  if (!createChannel(stdinChannel) ||
383  return;
384 
385 #if defined(Q_OS_WINCE)
387 #else
389  QByteArray envlist;
390  if (environment.d.constData())
391  envlist = qt_create_environment(environment.d.constData()->hash);
392 #endif
393  if (!nativeArguments.isEmpty()) {
394  if (!args.isEmpty())
395  args += QLatin1Char(' ');
396  args += nativeArguments;
397  }
398 
399 #if defined QPROCESS_DEBUG
400  qDebug("Creating process");
401  qDebug(" program : [%s]", program.toLatin1().constData());
402  qDebug(" args : %s", args.toLatin1().constData());
403  qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
404 #endif
405 
406 #if defined(Q_OS_WINCE)
407  QString fullPathProgram = program;
408  if (!QDir::isAbsolutePath(fullPathProgram))
409  fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
410  fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
411  success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
412  (wchar_t*)args.utf16(),
413  0, 0, false, 0, 0, 0, 0, pid);
414 #else
415  DWORD dwCreationFlags = CREATE_NO_WINDOW;
416  dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
417  STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
418  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
419  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
420  0, 0, 0,
421  STARTF_USESTDHANDLES,
422  0, 0, 0,
424  };
425  success = CreateProcess(0, (wchar_t*)args.utf16(),
426  0, 0, TRUE, dwCreationFlags,
427  environment.isEmpty() ? 0 : envlist.data(),
429  &startupInfo, pid);
430  if (!success) {
431  // Capture the error string before we do CloseHandle below
432  q->setErrorString(QProcess::tr("Process failed to start: %1").arg(qt_error_string()));
433  }
434 
435  if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
436  CloseHandle(stdinChannel.pipe[0]);
438  }
439  if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
440  CloseHandle(stdoutChannel.pipe[1]);
442  }
443  if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
444  CloseHandle(stderrChannel.pipe[1]);
446  }
447 #endif // Q_OS_WINCE
448 
449  if (!success) {
450  cleanup();
452  emit q->error(processError);
453  q->setProcessState(QProcess::NotRunning);
454  return;
455  }
456 
457  q->setProcessState(QProcess::Running);
458  // User can call kill()/terminate() from the stateChanged() slot
459  // so check before proceeding
460  if (!pid)
461  return;
462 
464  processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
467  notifier = new QTimer(q);
468  QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
470  }
471 
472  // give the process a chance to start ...
473  Sleep(SLEEPMIN * 2);
475 }
476 
478 {
480 }
481 
483 {
485  return 0;
486 
487  DWORD bytesAvail = 0;
488 #if !defined(Q_OS_WINCE)
489  PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
490 #if defined QPROCESS_DEBUG
491  qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
492 #endif
493  if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
494  QByteArray buf(bytesAvail, 0);
495  DWORD bytesRead = 0;
496  if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
497  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
498  if (hStdout) {
499  DWORD bytesWritten = 0;
500  WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0);
501  }
502  }
503  bytesAvail = 0;
504  }
505 #endif
506  return bytesAvail;
507 }
508 
510 {
512  return 0;
513 
514  DWORD bytesAvail = 0;
515 #if !defined(Q_OS_WINCE)
516  PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
517 #if defined QPROCESS_DEBUG
518  qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
519 #endif
520  if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
521  QByteArray buf(bytesAvail, 0);
522  DWORD bytesRead = 0;
523  if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
524  HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
525  if (hStderr) {
526  DWORD bytesWritten = 0;
527  WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0);
528  }
529  }
530  bytesAvail = 0;
531  }
532 #endif
533  return bytesAvail;
534 }
535 
537 {
538  DWORD read = qMin(maxlen, bytesAvailableFromStdout());
539  DWORD bytesRead = 0;
540 
541  if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0))
542  return -1;
543  return bytesRead;
544 }
545 
547 {
548  DWORD read = qMin(maxlen, bytesAvailableFromStderr());
549  DWORD bytesRead = 0;
550 
551  if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0))
552  return -1;
553  return bytesRead;
554 }
555 
556 
557 static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
558 {
559  DWORD currentProcId = 0;
560  GetWindowThreadProcessId(hwnd, &currentProcId);
561  if (currentProcId == (DWORD)procId)
562  PostMessage(hwnd, WM_CLOSE, 0, 0);
563 
564  return TRUE;
565 }
566 
568 {
569  if (pid) {
570  EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
571  PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
572  }
573 }
574 
576 {
577  if (pid)
578  TerminateProcess(pid->hProcess, 0xf291);
579 }
580 
582 {
583  Q_Q(QProcess);
584 
585  if (processStarted())
586  return true;
587 
589  return false;
590 
592  q->setErrorString(QProcess::tr("Process operation timed out"));
593  return false;
594 }
595 
596 bool QProcessPrivate::waitForReadyRead(int msecs)
597 {
598  Q_Q(QProcess);
599 
600 #if defined(Q_OS_WINCE)
602  q->setErrorString(QProcess::tr("Error reading from process"));
603  emit q->error(processError);
604  return false;
605 #endif
606 
608 
609  forever {
610  if (!writeBuffer.isEmpty() && !_q_canWrite())
611  return false;
613  timer.resetIncrements();
614  bool readyReadEmitted = false;
615  if (bytesAvailableFromStdout() != 0) {
616  readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted;
617  timer.resetIncrements();
618  }
619 
620  if (bytesAvailableFromStderr() != 0) {
621  readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted;
622  timer.resetIncrements();
623  }
624 
625  if (readyReadEmitted)
626  return true;
627 
628  if (!pid)
629  return false;
630  if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
631  // find the return value if there is noew data to read
632  _q_processDied();
633  return false;
634  }
635 
636  Sleep(timer.nextSleepTime());
637  if (timer.hasTimedOut())
638  break;
639  }
640 
642  q->setErrorString(QProcess::tr("Process operation timed out"));
643  return false;
644 }
645 
647 {
648  Q_Q(QProcess);
649 
650 #if defined(Q_OS_WINCE)
652  q->setErrorString(QProcess::tr("Error reading from process"));
653  emit q->error(processError);
654  return false;
655 #endif
656 
658 
659  forever {
660  // Check if we have any data pending: the pipe writer has
661  // bytes waiting to written, or it has written data since the
662  // last time we called pipeWriter->waitForWrite().
663  bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
664 
665  // If we don't have pending data, and our write buffer is
666  // empty, we fail.
667  if (!pendingDataInPipe && writeBuffer.isEmpty())
668  return false;
669 
670  // If we don't have pending data and we do have data in our
671  // write buffer, try to flush that data over to the pipe
672  // writer. Fail on error.
673  if (!pendingDataInPipe) {
674  if (!_q_canWrite())
675  return false;
676  }
677 
678  // Wait for the pipe writer to acknowledge that it has
679  // written. This will succeed if either the pipe writer has
680  // already written the data, or if it manages to write data
681  // within the given timeout. If the write buffer was non-empty
682  // and the pipeWriter is now dead, that means _q_canWrite()
683  // destroyed the writer after it successfully wrote the last
684  // batch.
685  if (!pipeWriter || pipeWriter->waitForWrite(0))
686  return true;
687 
688  // If we wouldn't write anything, check if we can read stdout.
689  if (bytesAvailableFromStdout() != 0) {
691  timer.resetIncrements();
692  }
693 
694  // Check if we can read stderr.
695  if (bytesAvailableFromStderr() != 0) {
697  timer.resetIncrements();
698  }
699 
700  // Check if the process died while reading.
701  if (!pid)
702  return false;
703 
704  // Wait for the process to signal any change in its state,
705  // such as incoming data, or if the process died.
706  if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
707  _q_processDied();
708  return false;
709  }
710 
711  // Only wait for as long as we've been asked.
712  if (timer.hasTimedOut())
713  break;
714  }
715 
717  q->setErrorString(QProcess::tr("Process operation timed out"));
718  return false;
719 }
720 
721 
722 bool QProcessPrivate::waitForFinished(int msecs)
723 {
724  Q_Q(QProcess);
725 #if defined QPROCESS_DEBUG
726  qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
727 #endif
728 
730 
731  forever {
732  if (!writeBuffer.isEmpty() && !_q_canWrite())
733  return false;
735  timer.resetIncrements();
736 
737  if (bytesAvailableFromStdout() != 0) {
739  timer.resetIncrements();
740  }
741 
742  if (bytesAvailableFromStderr() != 0) {
744  timer.resetIncrements();
745  }
746 
747  if (!pid)
748  return true;
749 
750  if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
751  _q_processDied();
752  return true;
753  }
754 
755  if (timer.hasTimedOut())
756  break;
757  }
759  q->setErrorString(QProcess::tr("Process operation timed out"));
760  return false;
761 }
762 
763 
765 {
766  DWORD theExitCode;
767  if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
768  exitCode = theExitCode;
769  //### for now we assume a crash if exit code is less than -1 or the magic number
770  crashed = (exitCode == 0xf291 || (int)exitCode < 0);
771  }
772 }
773 
775 {
776  if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
777  pipeWriter->waitForWrite(ULONG_MAX);
778  }
779 }
780 
782 {
783  return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
784 }
785 
786 qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
787 {
788  Q_Q(QProcess);
789 
790 #if defined(Q_OS_WINCE)
792  q->setErrorString(QProcess::tr("Error writing to process"));
793  emit q->error(processError);
794  return -1;
795 #endif
796 
797  if (!pipeWriter) {
799  pipeWriter->start();
800  }
801 
802  return pipeWriter->write(data, maxlen);
803 }
804 
805 bool QProcessPrivate::waitForWrite(int msecs)
806 {
807  Q_Q(QProcess);
808 
809  if (!pipeWriter || pipeWriter->waitForWrite(msecs))
810  return true;
811 
813  q->setErrorString(QProcess::tr("Process operation timed out"));
814  return false;
815 }
816 
818 {
819  notifier->stop();
820 
822  _q_canWrite();
823 
826 
829 
832 }
833 
834 bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
835 {
836 #if defined(Q_OS_WINCE)
837  Q_UNUSED(workingDir);
838  QString args = qt_create_commandline(QString(), arguments);
839 #else
840  QString args = qt_create_commandline(program, arguments);
841 #endif
842 
843  bool success = false;
844 
845  PROCESS_INFORMATION pinfo;
846 
847 #if defined(Q_OS_WINCE)
848  QString fullPathProgram = program;
849  if (!QDir::isAbsolutePath(fullPathProgram))
850  fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/')));
851  fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
852  success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
853  (wchar_t*)args.utf16(),
854  0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
855 #else
856  STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
857  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
858  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
859  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
860  };
861  success = CreateProcess(0, (wchar_t*)args.utf16(),
862  0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
863  workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
864  &startupInfo, &pinfo);
865 #endif // Q_OS_WINCE
866 
867  if (success) {
868  CloseHandle(pinfo.hThread);
869  CloseHandle(pinfo.hProcess);
870  if (pid)
871  *pid = pinfo.dwProcessId;
872  }
873 
874  return success;
875 }
876 
878 
879 #endif // QT_NO_PROCESS
static QString fromWCharArray(const wchar_t *, int size=-1)
Returns a copy of the string, where the encoding of string depends on the size of wchar...
Definition: qstring.cpp:1019
The QProcessEnvironment class holds the environment variables that can be passed to a program...
Definition: qprocess.h:68
bool _q_canReadStandardOutput()
Definition: qprocess.cpp:937
QBool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.h:904
qint64 write(const char *data, qint64 maxlen)
QString qt_error_string(int errorCode)
Definition: qglobal.cpp:2600
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
static QString fromLocal8Bit(const char *, int size=-1)
Returns a QString initialized with the first size characters of the 8-bit string str.
Definition: qstring.cpp:4245
#define NOTIFYTIMEOUT
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
char * data()
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:429
const QChar at(int i) const
Returns the character at the given index position in the string.
Definition: qstring.h:698
The QRegExp class provides pattern matching using regular expressions.
Definition: qregexp.h:61
static bool isAbsolutePath(const QString &path)
Returns true if path is absolute; returns false if it is relative.
Definition: qdir.h:192
#define it(className, varName)
QString & replace(int i, int len, QChar after)
Definition: qstring.cpp:2005
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
qint64 bytesAvailableFromStdout() const
int length() const
Returns the number of characters in this string.
Definition: qstring.h:696
QString & prepend(QChar c)
Definition: qstring.h:261
#define SLOT(a)
Definition: qobjectdefs.h:226
QProcess::ProcessState processState
Definition: qprocess_p.h:308
const_iterator ConstIterator
Qt-style synonym for QHash::const_iterator.
Definition: qhash.h:474
Channel stdinChannel
Definition: qprocess_p.h:317
static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory=QString(), qint64 *pid=0)
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:3734
QHashData * d
Definition: qhash.h:264
QString absoluteFilePath() const
Returns an absolute path including the file name.
Definition: qfileinfo.cpp:534
static QString tr(const char *sourceText, const char *comment=0, int n=-1)
QString nativeArguments
Definition: qprocess_p.h:326
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
EventLoopTimerRef timer
QSharedDataPointer< QProcessEnvironmentPrivate > d
Definition: qprocess.h:99
bool isEmpty() const
The QString class provides a Unicode character string.
Definition: qstring.h:83
static QString currentPath()
Returns the absolute path of the application&#39;s current directory.
Definition: qdir.cpp:1875
QProcessEnvironment environment
Definition: qprocess_p.h:328
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
#define WAIT_OBJECT_0
qint64 bytesAvailableFromStderr() const
#define INVALID_Q_PIPE
Definition: qprocess_p.h:69
qint64 writeToStdin(const char *data, qint64 maxlen)
bool contains(const Key &key) const
Returns true if the hash contains an item with the key; otherwise returns false.
Definition: qhash.h:872
QThreadData * threadData
Definition: qobject_p.h:195
static QString longFileName(const QString &path)
bool waitForReadyRead(int msecs=30000)
bool waitForWrite(int msecs=30000)
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:753
#define Q_Q(Class)
Definition: qglobal.h:2483
QStringList arguments
Definition: qprocess_p.h:324
bool _q_canWrite()
Definition: qprocess.cpp:1036
Q_CORE_EXPORT void qDebug(const char *,...)
#define SIGNAL(a)
Definition: qobjectdefs.h:227
QWindowsPipeWriter * pipeWriter
Definition: qprocess_p.h:343
qint64 bytesToWrite() const
#define QT_WIN_CALLBACK
Definition: qglobal.h:1178
qint64 readFromStdout(char *data, qint64 maxlen)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
bool waitForStarted(int msecs=30000)
QProcess::ProcessError processError
Definition: qprocess_p.h:307
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
const char * name
Channel stdoutChannel
Definition: qprocess_p.h:318
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
#define emit
Definition: qobjectdefs.h:76
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
bool isEmpty() const
Returns true if the hash contains no items; otherwise returns false.
Definition: qhash.h:297
#define SLEEPMIN
static const char * data(const QByteArray &arr)
unsigned int uint
Definition: qglobal.h:996
bool _q_startupNotification()
Definition: qprocess.cpp:1149
#define FALSE
Synonym for false.
Definition: qglobal.h:1019
static QProcessEnvironment systemEnvironment()
The systemEnvironment function returns the environment of the calling process.
QProcess::ProcessChannelMode processChannelMode
Definition: qprocess_p.h:306
QByteArray toLatin1() const Q_REQUIRED_RESULT
Returns a Latin-1 representation of the string as a QByteArray.
Definition: qstring.cpp:3993
__int64 qint64
Definition: qglobal.h:942
void * HANDLE
Definition: qnamespace.h:1671
unsigned long ulong
Definition: qglobal.h:997
qint64 readFromStderr(char *data, qint64 maxlen)
static void qt_create_pipe(Q_PIPE *pipe, bool in)
#define TRUE
Synonym for true.
Definition: qglobal.h:1018
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
const_iterator constBegin() const
Returns a const STL-style iterator pointing to the first item in the hash.
Definition: qhash.h:466
const_iterator constEnd() const
Returns a const STL-style iterator pointing to the imaginary item after the last item in the hash...
Definition: qhash.h:469
#define Q_ASSERT_X(cond, where, what)
Definition: qglobal.h:1837
bool waitForBytesWritten(int msecs=30000)
Channel stderrChannel
Definition: qprocess_p.h:319
void start(Priority=InheritPriority)
Begins execution of the thread by calling run().
HANDLE Q_PIPE
Definition: qprocess_p.h:68
QString workingDirectory
Definition: qprocess_p.h:309
bool waitForFinished(int msecs=30000)
void resize(int size)
Sets the size of the byte array to size bytes.
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
qint64 pipeWriterBytesToWrite() const
int size() const
Returns the number of bytes in this byte array.
Definition: qbytearray.h:402
bool waitForWrite(int msecs)
bool _q_canReadStandardError()
Definition: qprocess.cpp:992
bool createChannel(Channel &channel)
bool isEmpty() const
Returns true if the byte array has size 0; otherwise returns false.
Definition: qbytearray.h:421
bool _q_processDied()
Definition: qprocess.cpp:1080
QWinEventNotifier * processFinishedNotifier
Definition: qprocess_p.h:344
The QTimer class provides repetitive and single-shot timers.
Definition: qtimer.h:56
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:3796
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:60
void destroyPipe(Q_PIPE pipe[2])
static const KeyPair *const end
static QString toNativeSeparators(const QString &pathName)
Returns pathName with the &#39;/&#39; separators converted to separators that are appropriate for the underly...
Definition: qdir.cpp:812
void stop()
Stops the timer.
Definition: qtimer.cpp:284
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition: qtimer.cpp:249
QString program
Definition: qprocess_p.h:323
QRingBuffer writeBuffer
Definition: qprocess_p.h:332
QString & insert(int i, QChar c)
Definition: qstring.cpp:1671
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
QAbstractEventDispatcher * eventDispatcher
Definition: qthread_p.h:264
void setEnabled(bool enable)
#define CREATE_NO_WINDOW
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
The QProcess class is used to start external programs and to communicate with them.
Definition: qprocess.h:102
QTimer * notifier
Definition: qprocess_p.h:342
static bool equal(const QChar *a, int l, const char *b)
Definition: qurl.cpp:3270
const ushort * utf16() const
Returns the QString as a &#39;\0\&#39;-terminated array of unsigned shorts.
Definition: qstring.cpp:5290
static QString qt_create_commandline(const QString &program, const QStringList &arguments)
#define forever
This macro is provided for convenience for writing infinite loops.
Definition: qglobal.h:2452