|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qplatformdefs.h" |
|
43 #include "q3process.h" |
|
44 |
|
45 #ifndef QT_NO_PROCESS |
|
46 |
|
47 #include "qapplication.h" |
|
48 #include "q3cstring.h" |
|
49 #include "q3ptrqueue.h" |
|
50 #include "qtimer.h" |
|
51 #include "qregexp.h" |
|
52 #include "private/q3membuf_p.h" |
|
53 #include "qt_windows.h" |
|
54 |
|
55 #ifdef Q_OS_WINCE |
|
56 #define STARTF_USESTDHANDLES 1 |
|
57 #endif |
|
58 |
|
59 QT_BEGIN_NAMESPACE |
|
60 |
|
61 //#define QT_Q3PROCESS_DEBUG |
|
62 |
|
63 /*********************************************************************** |
|
64 * |
|
65 * Q3ProcessPrivate |
|
66 * |
|
67 **********************************************************************/ |
|
68 class Q3ProcessPrivate |
|
69 { |
|
70 public: |
|
71 Q3ProcessPrivate( Q3Process *proc ) |
|
72 { |
|
73 stdinBufRead = 0; |
|
74 pipeStdin[0] = 0; |
|
75 pipeStdin[1] = 0; |
|
76 pipeStdout[0] = 0; |
|
77 pipeStdout[1] = 0; |
|
78 pipeStderr[0] = 0; |
|
79 pipeStderr[1] = 0; |
|
80 exitValuesCalculated = false; |
|
81 |
|
82 lookup = new QTimer( proc ); |
|
83 qApp->connect( lookup, SIGNAL(timeout()), |
|
84 proc, SLOT(timeout()) ); |
|
85 |
|
86 pid = 0; |
|
87 } |
|
88 |
|
89 ~Q3ProcessPrivate() |
|
90 { |
|
91 reset(); |
|
92 } |
|
93 |
|
94 void reset() |
|
95 { |
|
96 while ( !stdinBuf.isEmpty() ) { |
|
97 delete stdinBuf.dequeue(); |
|
98 } |
|
99 closeHandles(); |
|
100 stdinBufRead = 0; |
|
101 pipeStdin[0] = 0; |
|
102 pipeStdin[1] = 0; |
|
103 pipeStdout[0] = 0; |
|
104 pipeStdout[1] = 0; |
|
105 pipeStderr[0] = 0; |
|
106 pipeStderr[1] = 0; |
|
107 exitValuesCalculated = false; |
|
108 |
|
109 deletePid(); |
|
110 } |
|
111 |
|
112 void closeHandles() |
|
113 { |
|
114 if( pipeStdin[1] != 0 ) { |
|
115 CloseHandle( pipeStdin[1] ); |
|
116 pipeStdin[1] = 0; |
|
117 } |
|
118 if( pipeStdout[0] != 0 ) { |
|
119 CloseHandle( pipeStdout[0] ); |
|
120 pipeStdout[0] = 0; |
|
121 } |
|
122 if( pipeStderr[0] != 0 ) { |
|
123 CloseHandle( pipeStderr[0] ); |
|
124 pipeStderr[0] = 0; |
|
125 } |
|
126 } |
|
127 |
|
128 void deletePid() |
|
129 { |
|
130 if ( pid ) { |
|
131 CloseHandle( pid->hProcess ); |
|
132 CloseHandle( pid->hThread ); |
|
133 delete pid; |
|
134 pid = 0; |
|
135 } |
|
136 } |
|
137 |
|
138 void newPid() |
|
139 { |
|
140 deletePid(); |
|
141 pid = new PROCESS_INFORMATION; |
|
142 memset( pid, 0, sizeof(PROCESS_INFORMATION) ); |
|
143 } |
|
144 |
|
145 Q3Membuf bufStdout; |
|
146 Q3Membuf bufStderr; |
|
147 |
|
148 Q3PtrQueue<QByteArray> stdinBuf; |
|
149 |
|
150 HANDLE pipeStdin[2]; |
|
151 HANDLE pipeStdout[2]; |
|
152 HANDLE pipeStderr[2]; |
|
153 QTimer *lookup; |
|
154 |
|
155 PROCESS_INFORMATION *pid; |
|
156 uint stdinBufRead; |
|
157 |
|
158 bool exitValuesCalculated; |
|
159 }; |
|
160 |
|
161 |
|
162 /*********************************************************************** |
|
163 * |
|
164 * Q3Process |
|
165 * |
|
166 **********************************************************************/ |
|
167 void Q3Process::init() |
|
168 { |
|
169 d = new Q3ProcessPrivate( this ); |
|
170 exitStat = 0; |
|
171 exitNormal = false; |
|
172 } |
|
173 |
|
174 void Q3Process::reset() |
|
175 { |
|
176 d->reset(); |
|
177 exitStat = 0; |
|
178 exitNormal = false; |
|
179 d->bufStdout.clear(); |
|
180 d->bufStderr.clear(); |
|
181 } |
|
182 |
|
183 Q3Membuf* Q3Process::membufStdout() |
|
184 { |
|
185 if( d->pipeStdout[0] != 0 ) |
|
186 socketRead( 1 ); |
|
187 return &d->bufStdout; |
|
188 } |
|
189 |
|
190 Q3Membuf* Q3Process::membufStderr() |
|
191 { |
|
192 if( d->pipeStderr[0] != 0 ) |
|
193 socketRead( 2 ); |
|
194 return &d->bufStderr; |
|
195 } |
|
196 |
|
197 Q3Process::~Q3Process() |
|
198 { |
|
199 delete d; |
|
200 } |
|
201 |
|
202 bool Q3Process::start( QStringList *env ) |
|
203 { |
|
204 #if defined(QT_Q3PROCESS_DEBUG) |
|
205 qDebug( "Q3Process::start()" ); |
|
206 #endif |
|
207 reset(); |
|
208 |
|
209 if ( _arguments.isEmpty() ) |
|
210 return false; |
|
211 |
|
212 // Open the pipes. Make non-inheritable copies of input write and output |
|
213 // read handles to avoid non-closable handles (this is done by the |
|
214 // DuplicateHandle() call). |
|
215 SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE }; |
|
216 #ifndef Q_OS_WINCE |
|
217 // I guess there is no stdin stdout and stderr on Q_OS_WINCE to dup |
|
218 // CreatePipe and DupilcateHandle aren't available for Q_OS_WINCE |
|
219 HANDLE tmpStdin, tmpStdout, tmpStderr; |
|
220 if ( comms & Stdin ) { |
|
221 if ( !CreatePipe( &d->pipeStdin[0], &tmpStdin, &secAtt, 0 ) ) { |
|
222 d->closeHandles(); |
|
223 return false; |
|
224 } |
|
225 if ( !DuplicateHandle( GetCurrentProcess(), tmpStdin, GetCurrentProcess(), &d->pipeStdin[1], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { |
|
226 d->closeHandles(); |
|
227 return false; |
|
228 } |
|
229 if ( !CloseHandle( tmpStdin ) ) { |
|
230 d->closeHandles(); |
|
231 return false; |
|
232 } |
|
233 } |
|
234 if ( comms & Stdout ) { |
|
235 if ( !CreatePipe( &tmpStdout, &d->pipeStdout[1], &secAtt, 0 ) ) { |
|
236 d->closeHandles(); |
|
237 return false; |
|
238 } |
|
239 if ( !DuplicateHandle( GetCurrentProcess(), tmpStdout, GetCurrentProcess(), &d->pipeStdout[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { |
|
240 d->closeHandles(); |
|
241 return false; |
|
242 } |
|
243 if ( !CloseHandle( tmpStdout ) ) { |
|
244 d->closeHandles(); |
|
245 return false; |
|
246 } |
|
247 } |
|
248 if ( comms & Stderr ) { |
|
249 if ( !CreatePipe( &tmpStderr, &d->pipeStderr[1], &secAtt, 0 ) ) { |
|
250 d->closeHandles(); |
|
251 return false; |
|
252 } |
|
253 if ( !DuplicateHandle( GetCurrentProcess(), tmpStderr, GetCurrentProcess(), &d->pipeStderr[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { |
|
254 d->closeHandles(); |
|
255 return false; |
|
256 } |
|
257 if ( !CloseHandle( tmpStderr ) ) { |
|
258 d->closeHandles(); |
|
259 return false; |
|
260 } |
|
261 } |
|
262 if ( comms & DupStderr ) { |
|
263 CloseHandle( d->pipeStderr[1] ); |
|
264 d->pipeStderr[1] = d->pipeStdout[1]; |
|
265 } |
|
266 #endif |
|
267 |
|
268 // construct the arguments for CreateProcess() |
|
269 QString args; |
|
270 QString appName; |
|
271 QStringList::Iterator it = _arguments.begin(); |
|
272 args = *it; |
|
273 ++it; |
|
274 if ( args.endsWith( QLatin1String(".bat") ) && args.contains( QLatin1Char(' ') ) ) { |
|
275 // CreateProcess() seems to have a strange semantics (see also |
|
276 // http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_11138647.html): |
|
277 // If you start a batch file with spaces in the filename, the first |
|
278 // argument to CreateProcess() must be the name of the batchfile |
|
279 // without quotes, but the second argument must start with the same |
|
280 // argument with quotes included. But if the same approach is used for |
|
281 // .exe files, it doesn't work. |
|
282 appName = args; |
|
283 args = QLatin1Char('"') + args + QLatin1Char('"'); |
|
284 } |
|
285 for ( ; it != _arguments.end(); ++it ) { |
|
286 QString tmp = *it; |
|
287 // escape a single " because the arguments will be parsed |
|
288 tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") ); |
|
289 if ( tmp.isEmpty() || tmp.contains( QLatin1Char(' ') ) || tmp.contains( QLatin1Char('\t') ) ) { |
|
290 // The argument must not end with a \ since this would be interpreted |
|
291 // as escaping the quote -- rather put the \ behind the quote: e.g. |
|
292 // rather use "foo"\ than "foo\" |
|
293 QString endQuote( QLatin1String("\"") ); |
|
294 int i = tmp.length(); |
|
295 while ( i>0 && tmp.at( i-1 ) == QLatin1Char('\\') ) { |
|
296 --i; |
|
297 endQuote += QLatin1Char('\\'); |
|
298 } |
|
299 args += QLatin1String(" \"") + tmp.left( i ) + endQuote; |
|
300 } else { |
|
301 args += QLatin1Char(' ') + tmp; |
|
302 } |
|
303 } |
|
304 #if defined(QT_Q3PROCESS_DEBUG) |
|
305 qDebug( "Q3Process::start(): args [%s]", args.latin1() ); |
|
306 #endif |
|
307 |
|
308 // CreateProcess() |
|
309 bool success; |
|
310 d->newPid(); |
|
311 |
|
312 STARTUPINFOW startupInfo = { |
|
313 sizeof( STARTUPINFO ), 0, 0, 0, |
|
314 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, |
|
315 0, 0, 0, |
|
316 STARTF_USESTDHANDLES, |
|
317 0, 0, 0, |
|
318 d->pipeStdin[0], d->pipeStdout[1], d->pipeStderr[1] |
|
319 }; |
|
320 wchar_t *applicationName; |
|
321 if ( appName.isNull() ) |
|
322 applicationName = 0; |
|
323 else |
|
324 applicationName = _wcsdup( (wchar_t*)appName.utf16() ); |
|
325 wchar_t *commandLine = _wcsdup( (wchar_t*)args.utf16() ); |
|
326 QByteArray envlist; |
|
327 if ( env != 0 ) { |
|
328 int pos = 0; |
|
329 // add PATH if necessary (for DLL loading) |
|
330 QByteArray path = qgetenv( "PATH" ); |
|
331 if ( env->grep( QRegExp(QLatin1String("^PATH="),FALSE) ).empty() && !path.isNull() ) { |
|
332 QString tmp = QString::fromLatin1("PATH=%1").arg(QLatin1String(path.constData())); |
|
333 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1); |
|
334 envlist.resize( envlist.size() + tmpSize ); |
|
335 memcpy( envlist.data() + pos, tmp.utf16(), tmpSize ); |
|
336 pos += tmpSize; |
|
337 } |
|
338 // add the user environment |
|
339 for ( QStringList::Iterator it = env->begin(); it != env->end(); it++ ) { |
|
340 QString tmp = *it; |
|
341 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1); |
|
342 envlist.resize( envlist.size() + tmpSize ); |
|
343 memcpy( envlist.data() + pos, tmp.utf16(), tmpSize ); |
|
344 pos += tmpSize; |
|
345 } |
|
346 // add the 2 terminating 0 (actually 4, just to be on the safe side) |
|
347 envlist.resize( envlist.size()+4 ); |
|
348 envlist[pos++] = 0; |
|
349 envlist[pos++] = 0; |
|
350 envlist[pos++] = 0; |
|
351 envlist[pos++] = 0; |
|
352 } |
|
353 success = CreateProcess( applicationName, commandLine, |
|
354 0, 0, TRUE, ( comms == 0 ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW ) |
|
355 #ifndef Q_OS_WINCE |
|
356 | CREATE_UNICODE_ENVIRONMENT |
|
357 #endif |
|
358 , env == 0 ? 0 : envlist.data(), |
|
359 (wchar_t*)QDir::toNativeSeparators(workingDir.absPath()).utf16(), |
|
360 &startupInfo, d->pid ); |
|
361 |
|
362 free( applicationName ); |
|
363 free( commandLine ); |
|
364 |
|
365 if ( !success ) { |
|
366 d->deletePid(); |
|
367 return false; |
|
368 } |
|
369 |
|
370 #ifndef Q_OS_WINCE |
|
371 if ( comms & Stdin ) |
|
372 CloseHandle( d->pipeStdin[0] ); |
|
373 if ( comms & Stdout ) |
|
374 CloseHandle( d->pipeStdout[1] ); |
|
375 if ( (comms & Stderr) && !(comms & DupStderr) ) |
|
376 CloseHandle( d->pipeStderr[1] ); |
|
377 #endif |
|
378 |
|
379 if ( ioRedirection || notifyOnExit ) { |
|
380 d->lookup->start( 100 ); |
|
381 } |
|
382 |
|
383 // cleanup and return |
|
384 return true; |
|
385 } |
|
386 |
|
387 static BOOL CALLBACK qt_terminateApp( HWND hwnd, LPARAM procId ) |
|
388 { |
|
389 DWORD procId_win; |
|
390 GetWindowThreadProcessId( hwnd, &procId_win ); |
|
391 if( procId_win == (DWORD)procId ) |
|
392 PostMessage( hwnd, WM_CLOSE, 0, 0 ); |
|
393 |
|
394 return TRUE; |
|
395 } |
|
396 |
|
397 void Q3Process::tryTerminate() const |
|
398 { |
|
399 if ( d->pid ) |
|
400 EnumWindows( qt_terminateApp, (LPARAM)d->pid->dwProcessId ); |
|
401 } |
|
402 |
|
403 void Q3Process::kill() const |
|
404 { |
|
405 if ( d->pid ) |
|
406 TerminateProcess( d->pid->hProcess, 0xf291 ); |
|
407 } |
|
408 |
|
409 bool Q3Process::isRunning() const |
|
410 { |
|
411 if ( !d->pid ) |
|
412 return false; |
|
413 |
|
414 if ( WaitForSingleObject( d->pid->hProcess, 0) == WAIT_OBJECT_0 ) { |
|
415 // there might be data to read |
|
416 Q3Process *that = (Q3Process*)this; |
|
417 that->socketRead( 1 ); // try stdout |
|
418 that->socketRead( 2 ); // try stderr |
|
419 // compute the exit values |
|
420 if ( !d->exitValuesCalculated ) { |
|
421 DWORD exitCode; |
|
422 if ( GetExitCodeProcess( d->pid->hProcess, &exitCode ) ) { |
|
423 if ( exitCode != STILL_ACTIVE ) { // this should ever be true? |
|
424 that->exitNormal = exitCode != 0xf291; |
|
425 that->exitStat = exitCode; |
|
426 } |
|
427 } |
|
428 d->exitValuesCalculated = true; |
|
429 } |
|
430 d->deletePid(); |
|
431 d->closeHandles(); |
|
432 return false; |
|
433 } else { |
|
434 return true; |
|
435 } |
|
436 } |
|
437 |
|
438 bool Q3Process::canReadLineStdout() const |
|
439 { |
|
440 if( !d->pipeStdout[0] ) |
|
441 return d->bufStdout.size() != 0; |
|
442 |
|
443 Q3Process *that = (Q3Process*)this; |
|
444 return that->membufStdout()->scanNewline( 0 ); |
|
445 } |
|
446 |
|
447 bool Q3Process::canReadLineStderr() const |
|
448 { |
|
449 if( !d->pipeStderr[0] ) |
|
450 return d->bufStderr.size() != 0; |
|
451 |
|
452 Q3Process *that = (Q3Process*)this; |
|
453 return that->membufStderr()->scanNewline( 0 ); |
|
454 } |
|
455 |
|
456 void Q3Process::writeToStdin( const QByteArray& buf ) |
|
457 { |
|
458 d->stdinBuf.enqueue( new QByteArray(buf) ); |
|
459 socketWrite( 0 ); |
|
460 } |
|
461 |
|
462 void Q3Process::closeStdin( ) |
|
463 { |
|
464 if ( d->pipeStdin[1] != 0 ) { |
|
465 CloseHandle( d->pipeStdin[1] ); |
|
466 d->pipeStdin[1] = 0; |
|
467 } |
|
468 } |
|
469 |
|
470 void Q3Process::socketRead( int fd ) |
|
471 { |
|
472 // fd == 1: stdout, fd == 2: stderr |
|
473 HANDLE dev; |
|
474 if ( fd == 1 ) { |
|
475 dev = d->pipeStdout[0]; |
|
476 } else if ( fd == 2 ) { |
|
477 dev = d->pipeStderr[0]; |
|
478 } else { |
|
479 return; |
|
480 } |
|
481 #ifndef Q_OS_WINCE |
|
482 // get the number of bytes that are waiting to be read |
|
483 unsigned long i, r; |
|
484 char dummy; |
|
485 if ( !PeekNamedPipe( dev, &dummy, 1, &r, &i, 0 ) ) { |
|
486 return; // ### is it worth to dig for the reason of the error? |
|
487 } |
|
488 #else |
|
489 unsigned long i = 1000; |
|
490 #endif |
|
491 if ( i > 0 ) { |
|
492 Q3Membuf *buffer; |
|
493 if ( fd == 1 ) |
|
494 buffer = &d->bufStdout; |
|
495 else |
|
496 buffer = &d->bufStderr; |
|
497 |
|
498 QByteArray *ba = new QByteArray( i ); |
|
499 uint sz = readStddev( dev, ba->data(), i ); |
|
500 if ( sz != i ) |
|
501 ba->resize( i ); |
|
502 |
|
503 if ( sz == 0 ) { |
|
504 delete ba; |
|
505 return; |
|
506 } |
|
507 buffer->append( ba ); |
|
508 if ( fd == 1 ) |
|
509 emit readyReadStdout(); |
|
510 else |
|
511 emit readyReadStderr(); |
|
512 } |
|
513 } |
|
514 |
|
515 void Q3Process::socketWrite( int ) |
|
516 { |
|
517 DWORD written; |
|
518 while ( !d->stdinBuf.isEmpty() && isRunning() ) { |
|
519 if ( !WriteFile( d->pipeStdin[1], |
|
520 d->stdinBuf.head()->data() + d->stdinBufRead, |
|
521 qMin( 8192, int(d->stdinBuf.head()->size() - d->stdinBufRead) ), |
|
522 &written, 0 ) ) { |
|
523 d->lookup->start( 100 ); |
|
524 return; |
|
525 } |
|
526 d->stdinBufRead += written; |
|
527 if ( d->stdinBufRead == (DWORD)d->stdinBuf.head()->size() ) { |
|
528 d->stdinBufRead = 0; |
|
529 delete d->stdinBuf.dequeue(); |
|
530 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() ) |
|
531 emit wroteToStdin(); |
|
532 } |
|
533 } |
|
534 } |
|
535 |
|
536 void Q3Process::flushStdin() |
|
537 { |
|
538 socketWrite( 0 ); |
|
539 } |
|
540 |
|
541 /* |
|
542 Use a timer for polling misc. stuff. |
|
543 */ |
|
544 void Q3Process::timeout() |
|
545 { |
|
546 // Disable the timer temporary since one of the slots that are connected to |
|
547 // the readyRead...(), etc. signals might trigger recursion if |
|
548 // processEvents() is called. |
|
549 d->lookup->stop(); |
|
550 |
|
551 // try to write pending data to stdin |
|
552 if ( !d->stdinBuf.isEmpty() ) |
|
553 socketWrite( 0 ); |
|
554 |
|
555 if ( ioRedirection ) { |
|
556 socketRead( 1 ); // try stdout |
|
557 socketRead( 2 ); // try stderr |
|
558 } |
|
559 |
|
560 if ( isRunning() ) { |
|
561 // enable timer again, if needed |
|
562 if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit ) |
|
563 d->lookup->start( 100 ); |
|
564 } else if ( notifyOnExit ) { |
|
565 emit processExited(); |
|
566 } |
|
567 } |
|
568 |
|
569 /* |
|
570 read on the pipe |
|
571 */ |
|
572 uint Q3Process::readStddev( HANDLE dev, char *buf, uint bytes ) |
|
573 { |
|
574 if ( bytes > 0 ) { |
|
575 ulong r; |
|
576 if ( ReadFile( dev, buf, bytes, &r, 0 ) ) |
|
577 return r; |
|
578 } |
|
579 return 0; |
|
580 } |
|
581 |
|
582 /* |
|
583 Used by connectNotify() and disconnectNotify() to change the value of |
|
584 ioRedirection (and related behaviour) |
|
585 */ |
|
586 void Q3Process::setIoRedirection( bool value ) |
|
587 { |
|
588 ioRedirection = value; |
|
589 if ( !ioRedirection && !notifyOnExit ) |
|
590 d->lookup->stop(); |
|
591 if ( ioRedirection ) { |
|
592 if ( isRunning() ) |
|
593 d->lookup->start( 100 ); |
|
594 } |
|
595 } |
|
596 |
|
597 /* |
|
598 Used by connectNotify() and disconnectNotify() to change the value of |
|
599 notifyOnExit (and related behaviour) |
|
600 */ |
|
601 void Q3Process::setNotifyOnExit( bool value ) |
|
602 { |
|
603 notifyOnExit = value; |
|
604 if ( !ioRedirection && !notifyOnExit ) |
|
605 d->lookup->stop(); |
|
606 if ( notifyOnExit ) { |
|
607 if ( isRunning() ) |
|
608 d->lookup->start( 100 ); |
|
609 } |
|
610 } |
|
611 |
|
612 /* |
|
613 Used by connectNotify() and disconnectNotify() to change the value of |
|
614 wroteToStdinConnected (and related behaviour) |
|
615 */ |
|
616 void Q3Process::setWroteStdinConnected( bool value ) |
|
617 { |
|
618 wroteToStdinConnected = value; |
|
619 } |
|
620 |
|
621 Q3Process::PID Q3Process::processIdentifier() |
|
622 { |
|
623 return d->pid; |
|
624 } |
|
625 |
|
626 QT_END_NAMESPACE |
|
627 |
|
628 #endif // QT_NO_PROCESS |