]> git.saurik.com Git - wxWidgets.git/blame - src/msw/utilsexc.cpp
Applied patch [ 738821 ] Allow more features to be turned off
[wxWidgets.git] / src / msw / utilsexc.cpp
CommitLineData
32592631 1/////////////////////////////////////////////////////////////////////////////
b568d04f 2// Name: msw/utilsexec.cpp
79066131 3// Purpose: wxExecute implementation for MSW
32592631
GL
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
79066131 8// Copyright: (c) 1998-2002 wxWindows dev team
6c9a19aa 9// Licence: wxWindows licence
32592631
GL
10/////////////////////////////////////////////////////////////////////////////
11
b568d04f
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
32592631 20#ifdef __GNUG__
b568d04f 21 #pragma implementation
32592631
GL
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
b568d04f 28 #pragma hdrstop
32592631
GL
29#endif
30
31#ifndef WX_PRECOMP
b568d04f
VZ
32 #include "wx/utils.h"
33 #include "wx/app.h"
34 #include "wx/intl.h"
068a7cfe 35 #include "wx/log.h"
45d5a0c6
VZ
36 #if wxUSE_GUI // See 'dirty hack' below.
37 #include "wx/frame.h"
38 #endif
2ae8a353 39#endif
32592631 40
1044a386 41#ifdef __WIN32__
8b33ae2d 42 #include "wx/stream.h"
b568d04f 43 #include "wx/process.h"
1044a386 44#endif
dbeac4bd 45
32592631 46#include "wx/msw/private.h"
dbeac4bd 47
32592631
GL
48#include <ctype.h>
49
b4da152e 50#if !defined(__GNUWIN32__) && !defined(__SALFORDC__) && !defined(__WXMICROWIN__)
b568d04f 51 #include <direct.h>
17dff81c 52#ifndef __MWERKS__
b568d04f 53 #include <dos.h>
32592631 54#endif
17dff81c 55#endif
32592631 56
b568d04f
VZ
57#if defined(__GNUWIN32__) && !defined(__TWIN32__)
58 #include <sys/unistd.h>
59 #include <sys/stat.h>
57c208c5 60#endif
32592631 61
b4da152e 62#if defined(__WIN32__) && !defined(__WXMICROWIN__)
2e38557f
JS
63#ifndef __UNIX__
64 #include <io.h>
65#endif
32592631
GL
66
67#ifndef __GNUWIN32__
68#include <shellapi.h>
69#endif
70#endif
71
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#ifndef __WATCOMC__
3f4a0c5b
VZ
76 #if !(defined(_MSC_VER) && (_MSC_VER > 800))
77 #include <errno.h>
78 #endif
32592631
GL
79#endif
80#include <stdarg.h>
81
731dd422
VZ
82#if wxUSE_IPC
83 #include "wx/dde.h" // for WX_DDE hack in wxExecute
84#endif // wxUSE_IPC
5bd3a2da 85
b568d04f
VZ
86// ----------------------------------------------------------------------------
87// constants
88// ----------------------------------------------------------------------------
89
cb6827a8
VZ
90// this message is sent when the process we're waiting for terminates
91#define wxWM_PROC_TERMINATED (WM_USER + 10000)
32592631 92
b568d04f
VZ
93// ----------------------------------------------------------------------------
94// this module globals
95// ----------------------------------------------------------------------------
96
97// we need to create a hidden window to receive the process termination
98// notifications and for this we need a (Win) class name for it which we will
99// register the first time it's needed
100static const wxChar *gs_classForHiddenWindow = NULL;
101
102// ----------------------------------------------------------------------------
103// private types
104// ----------------------------------------------------------------------------
105
cb6827a8
VZ
106// structure describing the process we're being waiting for
107struct wxExecuteData
108{
109public:
110 ~wxExecuteData()
111 {
750b78ba 112#ifndef __WIN16__
cb6827a8
VZ
113 if ( !::CloseHandle(hProcess) )
114 {
f6bcfd97 115 wxLogLastError(wxT("CloseHandle(hProcess)"));
cb6827a8 116 }
750b78ba 117#endif
cb6827a8
VZ
118 }
119
120 HWND hWnd; // window to send wxWM_PROC_TERMINATED to
121 HANDLE hProcess; // handle of the process
122 DWORD dwProcessId; // pid of the process
123 wxProcess *handler;
124 DWORD dwExitCode; // the exit code of the process
125 bool state; // set to FALSE when the process finishes
32592631
GL
126};
127
f6bcfd97 128#if defined(__WIN32__) && wxUSE_STREAMS
8b33ae2d 129
8b33ae2d
GL
130// ----------------------------------------------------------------------------
131// wxPipeStreams
132// ----------------------------------------------------------------------------
133
f6bcfd97
BP
134class wxPipeInputStream: public wxInputStream
135{
8b33ae2d
GL
136public:
137 wxPipeInputStream(HANDLE hInput);
79066131 138 virtual ~wxPipeInputStream();
8b33ae2d 139
79066131
VZ
140 // returns TRUE if the pipe is still opened
141 bool IsOpened() const { return m_hInput != INVALID_HANDLE_VALUE; }
142
143 // returns TRUE if there is any data to be read from the pipe
2b5f62a0 144 virtual bool CanRead() const;
f6bcfd97 145
8b33ae2d
GL
146protected:
147 size_t OnSysRead(void *buffer, size_t len);
148
149protected:
150 HANDLE m_hInput;
22f3361e
VZ
151
152 DECLARE_NO_COPY_CLASS(wxPipeInputStream)
8b33ae2d
GL
153};
154
f6bcfd97
BP
155class wxPipeOutputStream: public wxOutputStream
156{
8b33ae2d
GL
157public:
158 wxPipeOutputStream(HANDLE hOutput);
79066131 159 virtual ~wxPipeOutputStream();
8b33ae2d
GL
160
161protected:
162 size_t OnSysWrite(const void *buffer, size_t len);
163
164protected:
165 HANDLE m_hOutput;
22f3361e
VZ
166
167 DECLARE_NO_COPY_CLASS(wxPipeOutputStream)
8b33ae2d
GL
168};
169
79066131
VZ
170// define this to let wxexec.cpp know that we know what we're doing
171#define _WX_USED_BY_WXEXECUTE_
172#include "../common/execcmn.cpp"
173
174// ----------------------------------------------------------------------------
175// wxPipe represents a Win32 anonymous pipe
176// ----------------------------------------------------------------------------
177
178class wxPipe
179{
180public:
181 // the symbolic names for the pipe ends
182 enum Direction
183 {
184 Read,
185 Write
186 };
187
188 // default ctor doesn't do anything
189 wxPipe() { m_handles[Read] = m_handles[Write] = INVALID_HANDLE_VALUE; }
190
191 // create the pipe, return TRUE if ok, FALSE on error
192 bool Create()
193 {
194 // default secutiry attributes
195 SECURITY_ATTRIBUTES security;
196
197 security.nLength = sizeof(security);
198 security.lpSecurityDescriptor = NULL;
199 security.bInheritHandle = TRUE; // to pass it to the child
200
201 if ( !::CreatePipe(&m_handles[0], &m_handles[1], &security, 0) )
202 {
203 wxLogSysError(_("Failed to create an anonymous pipe"));
204
205 return FALSE;
206 }
207
208 return TRUE;
209 }
210
211 // return TRUE if we were created successfully
212 bool IsOk() const { return m_handles[Read] != INVALID_HANDLE_VALUE; }
213
214 // return the descriptor for one of the pipe ends
215 HANDLE operator[](Direction which) const
216 {
217 wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
218 _T("invalid pipe index") );
219
220 return m_handles[which];
221 }
222
223 // detach a descriptor, meaning that the pipe dtor won't close it, and
224 // return it
225 HANDLE Detach(Direction which)
226 {
227 wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
228 _T("invalid pipe index") );
229
230 HANDLE handle = m_handles[which];
231 m_handles[which] = INVALID_HANDLE_VALUE;
232
233 return handle;
234 }
235
236 // close the pipe descriptors
237 void Close()
238 {
239 for ( size_t n = 0; n < WXSIZEOF(m_handles); n++ )
240 {
241 if ( m_handles[n] != INVALID_HANDLE_VALUE )
242 {
243 ::CloseHandle(m_handles[n]);
244 m_handles[n] = INVALID_HANDLE_VALUE;
245 }
246 }
247 }
248
249 // dtor closes the pipe descriptors
250 ~wxPipe() { Close(); }
251
252private:
253 HANDLE m_handles[2];
254};
255
256#endif // wxUSE_STREAMS
257
258// ============================================================================
259// implementation
260// ============================================================================
261
262#ifdef __WIN32__
263
264// ----------------------------------------------------------------------------
265// process termination detecting support
266// ----------------------------------------------------------------------------
267
268// thread function for the thread monitoring the process termination
269static DWORD __stdcall wxExecuteThread(void *arg)
270{
45d5a0c6 271 wxExecuteData * const data = (wxExecuteData *)arg;
79066131
VZ
272
273 if ( ::WaitForSingleObject(data->hProcess, INFINITE) != WAIT_OBJECT_0 )
274 {
275 wxLogDebug(_T("Waiting for the process termination failed!"));
276 }
277
278 // get the exit code
279 if ( !::GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
280 {
281 wxLogLastError(wxT("GetExitCodeProcess"));
282 }
283
284 wxASSERT_MSG( data->dwExitCode != STILL_ACTIVE,
285 wxT("process should have terminated") );
286
287 // send a message indicating process termination to the window
288 ::SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
289
290 return 0;
291}
292
293// window procedure of a hidden window which is created just to receive
294// the notification message when a process exits
295LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
296 WPARAM wParam, LPARAM lParam)
297{
298 if ( message == wxWM_PROC_TERMINATED )
299 {
300 DestroyWindow(hWnd); // we don't need it any more
301
45d5a0c6 302 wxExecuteData * const data = (wxExecuteData *)lParam;
79066131
VZ
303 if ( data->handler )
304 {
305 data->handler->OnTerminate((int)data->dwProcessId,
306 (int)data->dwExitCode);
307 }
308
309 if ( data->state )
310 {
311 // we're executing synchronously, tell the waiting thread
312 // that the process finished
313 data->state = 0;
314 }
315 else
316 {
317 // asynchronous execution - we should do the clean up
318 delete data;
319 }
320
321 return 0;
322 }
323 else
324 {
45d5a0c6 325 return ::DefWindowProc(hWnd, message, wParam, lParam);
79066131
VZ
326 }
327}
328
329// ============================================================================
330// implementation of IO redirection support classes
331// ============================================================================
332
333#if wxUSE_STREAMS
334
335// ----------------------------------------------------------------------------
336// wxPipeInputStreams
337// ----------------------------------------------------------------------------
8b33ae2d
GL
338
339wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
340{
341 m_hInput = hInput;
cd6ce4a9 342}
8b33ae2d
GL
343
344wxPipeInputStream::~wxPipeInputStream()
345{
79066131
VZ
346 if ( m_hInput != INVALID_HANDLE_VALUE )
347 ::CloseHandle(m_hInput);
8b33ae2d
GL
348}
349
2b5f62a0 350bool wxPipeInputStream::CanRead() const
8b33ae2d 351{
79066131
VZ
352 if ( !IsOpened() )
353 return FALSE;
354
f6bcfd97
BP
355 DWORD nAvailable;
356
357 // function name is misleading, it works with anon pipes as well
358 DWORD rc = ::PeekNamedPipe
359 (
360 m_hInput, // handle
361 NULL, 0, // ptr to buffer and its size
362 NULL, // [out] bytes read
363 &nAvailable, // [out] bytes available
364 NULL // [out] bytes left
365 );
366
367 if ( !rc )
368 {
369 if ( ::GetLastError() != ERROR_BROKEN_PIPE )
370 {
371 // unexpected error
372 wxLogLastError(_T("PeekNamedPipe"));
373 }
8b33ae2d 374
f6bcfd97
BP
375 // don't try to continue reading from a pipe if an error occured or if
376 // it had been closed
79066131
VZ
377 ::CloseHandle(m_hInput);
378
2b5f62a0 379 wxPipeInputStream *self = wxConstCast(this, wxPipeInputStream);
79066131 380
2b5f62a0
VZ
381 self->m_hInput = INVALID_HANDLE_VALUE;
382 self->m_lasterror = wxSTREAM_EOF;
383
384 nAvailable = 0;
f6bcfd97 385 }
79066131
VZ
386
387 return nAvailable != 0;
f6bcfd97
BP
388}
389
390size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
391{
2b5f62a0 392 if ( !IsOpened() )
79066131
VZ
393 {
394 m_lasterror = wxSTREAM_EOF;
395
f6bcfd97 396 return 0;
79066131
VZ
397 }
398
f6bcfd97
BP
399 DWORD bytesRead;
400 if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
401 {
2b5f62a0
VZ
402 m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
403 ? wxSTREAM_EOF
404 : wxSTREAM_READ_ERROR;
8b33ae2d 405 }
f6bcfd97 406
2b5f62a0 407 // bytesRead is set to 0, as desired, if an error occured
8b33ae2d
GL
408 return bytesRead;
409}
410
79066131 411// ----------------------------------------------------------------------------
8b33ae2d 412// wxPipeOutputStream
79066131 413// ----------------------------------------------------------------------------
8b33ae2d
GL
414
415wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput)
416{
417 m_hOutput = hOutput;
cd6ce4a9 418}
8b33ae2d
GL
419
420wxPipeOutputStream::~wxPipeOutputStream()
421{
422 ::CloseHandle(m_hOutput);
423}
424
425size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
426{
2b5f62a0 427 DWORD bytesWritten;
8b33ae2d 428
2b5f62a0
VZ
429 m_lasterror = wxSTREAM_NO_ERROR;
430 if ( !::WriteFile(m_hOutput, buffer, len, &bytesWritten, NULL) )
f6bcfd97 431 {
2b5f62a0
VZ
432 m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
433 ? wxSTREAM_EOF
434 : wxSTREAM_WRITE_ERROR;
8b33ae2d 435 }
f6bcfd97 436
2b5f62a0 437 return bytesWritten;
8b33ae2d
GL
438}
439
79066131
VZ
440#endif // wxUSE_STREAMS
441
442#endif // Win32
8b33ae2d 443
b568d04f 444// ============================================================================
79066131 445// wxExecute functions family
b568d04f 446// ============================================================================
5260b1c5 447
ca289436
VZ
448#if wxUSE_IPC
449
450// connect to the given server via DDE and ask it to execute the command
451static bool wxExecuteDDE(const wxString& ddeServer,
452 const wxString& ddeTopic,
453 const wxString& ddeCommand)
454{
2b5f62a0 455 bool ok = FALSE;
ca289436
VZ
456
457 wxDDEClient client;
458 wxConnectionBase *conn = client.MakeConnection(_T(""),
459 ddeServer,
460 ddeTopic);
461 if ( !conn )
462 {
463 ok = FALSE;
464 }
465 else // connected to DDE server
466 {
467 // the added complication here is that although most
468 // programs use XTYP_EXECUTE for their DDE API, some
469 // important ones - like IE and other MS stuff - use
470 // XTYP_REQUEST!
471 //
2b5f62a0 472 // so we try one first and then the other one if it
ca289436
VZ
473 // failed
474 {
475 wxLogNull noErrors;
2b5f62a0 476 ok = conn->Execute(ddeCommand);
ca289436
VZ
477 }
478
479 if ( !ok )
480 {
2b5f62a0
VZ
481 // now try request - but show the errors
482 ok = conn->Request(ddeCommand) != NULL;
ca289436
VZ
483 }
484 }
485
486 return ok;
487}
488
489#endif // wxUSE_IPC
490
fbf456aa 491long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
32592631 492{
5bd3a2da
VZ
493 wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
494
647b8e37
VZ
495#if wxUSE_THREADS
496 // for many reasons, the code below breaks down if it's called from another
497 // thread -- this could be fixed, but as Unix versions don't support this
498 // neither I don't want to waste time on this now
499 wxASSERT_MSG( wxThread::IsMain(),
500 _T("wxExecute() can be called only from the main thread") );
501#endif // wxUSE_THREADS
502
039f62f4 503 wxString command;
6ba63600 504
731dd422 505#if wxUSE_IPC
5bd3a2da
VZ
506 // DDE hack: this is really not pretty, but we need to allow this for
507 // transparent handling of DDE servers in wxMimeTypesManager. Usually it
508 // returns the command which should be run to view/open/... a file of the
509 // given type. Sometimes, however, this command just launches the server
510 // and an additional DDE request must be made to really open the file. To
511 // keep all this well hidden from the application, we allow a special form
6ba63600 512 // of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which
5bd3a2da 513 // case we execute just <command> and process the rest below
039f62f4 514 wxString ddeServer, ddeTopic, ddeCommand;
5bd3a2da
VZ
515 static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
516 if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
517 {
ca289436
VZ
518 // speed up the concatenations below
519 ddeServer.reserve(256);
520 ddeTopic.reserve(256);
521 ddeCommand.reserve(256);
522
5bd3a2da
VZ
523 const wxChar *p = cmd.c_str() + 7;
524 while ( *p && *p != _T('#') )
525 {
526 command += *p++;
527 }
528
529 if ( *p )
530 {
531 // skip '#'
532 p++;
533 }
534 else
535 {
536 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
537 }
538
539 while ( *p && *p != _T('#') )
540 {
541 ddeServer += *p++;
542 }
543
544 if ( *p )
545 {
546 // skip '#'
547 p++;
548 }
549 else
550 {
551 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
552 }
553
554 while ( *p && *p != _T('#') )
555 {
556 ddeTopic += *p++;
557 }
558
559 if ( *p )
560 {
561 // skip '#'
562 p++;
563 }
564 else
565 {
566 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
567 }
568
569 while ( *p )
570 {
571 ddeCommand += *p++;
572 }
6ba63600 573
ca289436
VZ
574 // if we want to just launch the program and not wait for its
575 // termination, try to execute DDE command right now, it can succeed if
576 // the process is already running - but as it fails if it's not
577 // running, suppress any errors it might generate
fbf456aa 578 if ( !(flags & wxEXEC_SYNC) )
6ba63600 579 {
ca289436
VZ
580 wxLogNull noErrors;
581 if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
582 {
583 // a dummy PID - this is a hack, of course, but it's well worth
584 // it as we don't open a new server each time we're called
585 // which would be quite bad
586 return -1;
587 }
6ba63600 588 }
5bd3a2da
VZ
589 }
590 else
731dd422 591#endif // wxUSE_IPC
5bd3a2da
VZ
592 {
593 // no DDE
594 command = cmd;
595 }
32592631 596
57c208c5 597#if defined(__WIN32__) && !defined(__TWIN32__)
cb6827a8 598
f6bcfd97
BP
599 // the IO redirection is only supported with wxUSE_STREAMS
600 BOOL redirect = FALSE;
79066131 601
f6bcfd97 602#if wxUSE_STREAMS
79066131
VZ
603 wxPipe pipeIn, pipeOut, pipeErr;
604
605 // we'll save here the copy of pipeIn[Write]
33ac7e6f 606 HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
8b33ae2d 607
cd6ce4a9 608 // open the pipes to which child process IO will be redirected if needed
cd6ce4a9
VZ
609 if ( handler && handler->IsRedirected() )
610 {
79066131
VZ
611 // create pipes for redirecting stdin, stdout and stderr
612 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
cd6ce4a9 613 {
79066131 614 wxLogSysError(_("Failed to redirect the child process IO"));
8b33ae2d 615
fbf456aa
VZ
616 // indicate failure: we need to return different error code
617 // depending on the sync flag
618 return flags & wxEXEC_SYNC ? -1 : 0;
8b33ae2d
GL
619 }
620
f6bcfd97 621 redirect = TRUE;
8b33ae2d 622 }
f6bcfd97 623#endif // wxUSE_STREAMS
5bd3a2da 624
cb6827a8
VZ
625 // create the process
626 STARTUPINFO si;
11c7d5b6 627 wxZeroMemory(si);
cb6827a8
VZ
628 si.cb = sizeof(si);
629
f6bcfd97
BP
630#if wxUSE_STREAMS
631 if ( redirect )
cb6827a8 632 {
fbf456aa 633 si.dwFlags = STARTF_USESTDHANDLES;
f6bcfd97 634
79066131
VZ
635 si.hStdInput = pipeIn[wxPipe::Read];
636 si.hStdOutput = pipeOut[wxPipe::Write];
637 si.hStdError = pipeErr[wxPipe::Write];
f6bcfd97 638
fbf456aa
VZ
639 // when the std IO is redirected, we don't show the (console) process
640 // window by default, but this can be overridden by the caller by
641 // specifying wxEXEC_NOHIDE flag
642 if ( !(flags & wxEXEC_NOHIDE) )
643 {
644 si.dwFlags |= STARTF_USESHOWWINDOW;
645 si.wShowWindow = SW_HIDE;
646 }
f6bcfd97
BP
647
648 // we must duplicate the handle to the write side of stdin pipe to make
79066131
VZ
649 // it non inheritable: indeed, we must close the writing end of pipeIn
650 // before launching the child process as otherwise this handle will be
f6bcfd97
BP
651 // inherited by the child which will never close it and so the pipe
652 // will never be closed and the child will be left stuck in ReadFile()
79066131 653 HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
f6bcfd97
BP
654 if ( !::DuplicateHandle
655 (
79066131
VZ
656 ::GetCurrentProcess(),
657 pipeInWrite,
658 ::GetCurrentProcess(),
f6bcfd97
BP
659 &hpipeStdinWrite,
660 0, // desired access: unused here
661 FALSE, // not inherited
662 DUPLICATE_SAME_ACCESS // same access as for src handle
663 ) )
cd6ce4a9 664 {
f6bcfd97 665 wxLogLastError(_T("DuplicateHandle"));
8b33ae2d 666 }
cd6ce4a9 667
79066131 668 ::CloseHandle(pipeInWrite);
f6bcfd97
BP
669 }
670#endif // wxUSE_STREAMS
cb6827a8 671
f6bcfd97
BP
672 PROCESS_INFORMATION pi;
673 DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
674
675 bool ok = ::CreateProcess
676 (
677 NULL, // application name (use only cmd line)
678 (wxChar *)
679 command.c_str(), // full command line
680 NULL, // security attributes: defaults for both
681 NULL, // the process and its main thread
682 redirect, // inherit handles if we use pipes
683 dwFlags, // process creation flags
684 NULL, // environment (use the same)
685 NULL, // current directory (use the same)
686 &si, // startup info (unused here)
687 &pi // process info
688 ) != 0;
689
690#if wxUSE_STREAMS
691 // we can close the pipe ends used by child anyhow
692 if ( redirect )
693 {
79066131
VZ
694 ::CloseHandle(pipeIn.Detach(wxPipe::Read));
695 ::CloseHandle(pipeOut.Detach(wxPipe::Write));
696 ::CloseHandle(pipeErr.Detach(wxPipe::Write));
cb6827a8 697 }
f6bcfd97 698#endif // wxUSE_STREAMS
cb6827a8 699
f6bcfd97 700 if ( !ok )
5e1febfa 701 {
f6bcfd97
BP
702#if wxUSE_STREAMS
703 // close the other handles too
704 if ( redirect )
5e1febfa 705 {
79066131
VZ
706 ::CloseHandle(pipeOut.Detach(wxPipe::Read));
707 ::CloseHandle(pipeErr.Detach(wxPipe::Read));
5e1febfa 708 }
f6bcfd97 709#endif // wxUSE_STREAMS
5e1febfa 710
f6bcfd97
BP
711 wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
712
fbf456aa 713 return flags & wxEXEC_SYNC ? -1 : 0;
f6bcfd97 714 }
8b33ae2d 715
f6bcfd97 716#if wxUSE_STREAMS
79066131
VZ
717 // the input buffer bufOut is connected to stdout, this is why it is
718 // called bufOut and not bufIn
719 wxStreamTempInputBuffer bufOut,
720 bufErr;
721
f6bcfd97
BP
722 if ( redirect )
723 {
8b33ae2d 724 // We can now initialize the wxStreams
79066131
VZ
725 wxPipeInputStream *
726 outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
727 wxPipeInputStream *
728 errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
729 wxPipeOutputStream *
730 inStream = new wxPipeOutputStream(hpipeStdinWrite);
731
732 handler->SetPipeStreams(outStream, inStream, errStream);
8b33ae2d 733
79066131
VZ
734 bufOut.Init(outStream);
735 bufErr.Init(errStream);
8b33ae2d 736 }
f6bcfd97 737#endif // wxUSE_STREAMS
8b33ae2d 738
0d7ea902 739 // register the class for the hidden window used for the notifications
b568d04f
VZ
740 if ( !gs_classForHiddenWindow )
741 {
742 gs_classForHiddenWindow = _T("wxHiddenWindow");
743
744 WNDCLASS wndclass;
745 wxZeroMemory(wndclass);
746 wndclass.lpfnWndProc = (WNDPROC)wxExecuteWindowCbk;
747 wndclass.hInstance = wxGetInstance();
748 wndclass.lpszClassName = gs_classForHiddenWindow;
749
750 if ( !::RegisterClass(&wndclass) )
751 {
f6bcfd97 752 wxLogLastError(wxT("RegisterClass(hidden window)"));
b568d04f
VZ
753 }
754 }
755
cb6827a8
VZ
756 // create a hidden window to receive notification about process
757 // termination
b568d04f 758 HWND hwnd = ::CreateWindow(gs_classForHiddenWindow, NULL,
0d7ea902
VZ
759 WS_OVERLAPPEDWINDOW,
760 0, 0, 0, 0, NULL,
cb6827a8 761 (HMENU)NULL, wxGetInstance(), 0);
223d09f6 762 wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
e6045e08 763
cb6827a8
VZ
764 // Alloc data
765 wxExecuteData *data = new wxExecuteData;
766 data->hProcess = pi.hProcess;
767 data->dwProcessId = pi.dwProcessId;
768 data->hWnd = hwnd;
fbf456aa
VZ
769 data->state = (flags & wxEXEC_SYNC) != 0;
770 if ( flags & wxEXEC_SYNC )
e6045e08 771 {
5e1febfa
VZ
772 // handler may be !NULL for capturing program output, but we don't use
773 // it wxExecuteData struct in this case
e6045e08
VZ
774 data->handler = NULL;
775 }
776 else
777 {
778 // may be NULL or not
779 data->handler = handler;
780 }
cb6827a8
VZ
781
782 DWORD tid;
783 HANDLE hThread = ::CreateThread(NULL,
784 0,
19193a2c 785 wxExecuteThread,
cb6827a8
VZ
786 (void *)data,
787 0,
788 &tid);
789
0d7ea902
VZ
790 // resume process we created now - whether the thread creation succeeded or
791 // not
792 if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
793 {
794 // ignore it - what can we do?
f6bcfd97 795 wxLogLastError(wxT("ResumeThread in wxExecute"));
0d7ea902
VZ
796 }
797
798 // close unneeded handle
799 if ( !::CloseHandle(pi.hThread) )
f6bcfd97 800 wxLogLastError(wxT("CloseHandle(hThread)"));
0d7ea902 801
cb6827a8
VZ
802 if ( !hThread )
803 {
f6bcfd97 804 wxLogLastError(wxT("CreateThread in wxExecute"));
cb6827a8
VZ
805
806 DestroyWindow(hwnd);
807 delete data;
808
809 // the process still started up successfully...
810 return pi.dwProcessId;
811 }
e6045e08 812
f6bcfd97
BP
813 ::CloseHandle(hThread);
814
731dd422 815#if wxUSE_IPC
5bd3a2da
VZ
816 // second part of DDE hack: now establish the DDE conversation with the
817 // just launched process
ca289436 818 if ( !ddeServer.empty() )
5bd3a2da 819 {
ca289436
VZ
820 bool ok;
821
822 // give the process the time to init itself
823 //
824 // we use a very big timeout hoping that WaitForInputIdle() will return
825 // much sooner, but not INFINITE just in case the process hangs
826 // completely - like this we will regain control sooner or later
827 switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
6ba63600 828 {
ca289436
VZ
829 default:
830 wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") );
831 // fall through
6ba63600 832
ca289436
VZ
833 case -1:
834 wxLogLastError(_T("WaitForInputIdle() in wxExecute"));
6ba63600 835
ca289436
VZ
836 case WAIT_TIMEOUT:
837 wxLogDebug(_T("Timeout too small in WaitForInputIdle"));
838
839 ok = FALSE;
840 break;
841
842 case 0:
843 // ok, process ready to accept DDE requests
844 ok = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
6ba63600 845 }
5bd3a2da 846 }
731dd422 847#endif // wxUSE_IPC
5bd3a2da 848
fbf456aa 849 if ( !(flags & wxEXEC_SYNC) )
cb6827a8
VZ
850 {
851 // clean up will be done when the process terminates
e6045e08
VZ
852
853 // return the pid
cb6827a8
VZ
854 return pi.dwProcessId;
855 }
e6045e08 856
068a7cfe 857 // disable all app windows while waiting for the child process to finish
0d7ea902 858#if wxUSE_GUI
068a7cfe
VZ
859
860 /*
861 We use a dirty hack here to disable all application windows (which we
862 must do because otherwise the calls to wxYield() could lead to some very
863 unexpected reentrancies in the users code) but to avoid losing
864 focus/activation entirely when the child process terminates which would
865 happen if we simply disabled everything using wxWindowDisabler. Indeed,
866 remember that Windows will never activate a disabled window and when the
867 last childs window is closed and Windows looks for a window to activate
868 all our windows are still disabled. There is no way to enable them in
869 time because we don't know when the childs windows are going to be
870 closed, so the solution we use here is to keep one special tiny frame
871 enabled all the time. Then when the child terminates it will get
872 activated and when we close it below -- after reenabling all the other
873 windows! -- the previously active window becomes activated again and
874 everything is ok.
875 */
876 wxWindow *winActive;
cd6ce4a9
VZ
877 {
878 wxBusyCursor bc;
879
068a7cfe 880 // first disable all existing windows
cd6ce4a9 881 wxWindowDisabler wd;
068a7cfe
VZ
882
883 // then create an "invisible" frame: it has minimal size, is positioned
884 // (hopefully) outside the screen and doesn't appear on the taskbar
885 winActive = new wxFrame
886 (
887 wxTheApp->GetTopWindow(),
888 -1,
889 _T(""),
890 wxPoint(32600, 32600),
891 wxSize(1, 1),
892 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
893 );
894 winActive->Show();
0d7ea902
VZ
895#endif // wxUSE_GUI
896
79066131
VZ
897 // wait until the child process terminates
898 while ( data->state )
899 {
900#if wxUSE_STREAMS
901 bufOut.Update();
902 bufErr.Update();
903#endif // wxUSE_STREAMS
904
905 // don't eat 100% of the CPU -- ugly but anything else requires
906 // real async IO which we don't have for the moment
907 ::Sleep(50);
908
45d5a0c6 909#if wxUSE_GUI
79066131
VZ
910 // repaint the GUI
911 wxYield();
45d5a0c6
VZ
912#else // !GUI
913 // dispatch the messages to the hidden window so that it could
914 // process the wxWM_PROC_TERMINATED notification
915 MSG msg;
916 ::PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE);
917#endif // GUI/!GUI
79066131 918 }
e6045e08 919
0d7ea902 920#if wxUSE_GUI
cd6ce4a9 921 }
068a7cfe
VZ
922
923 // finally delete the dummy frame and, as wd has been already destroyed and
924 // the other windows reenabled, the activation is going to return to the
925 // window which had it before
926 winActive->Destroy();
0d7ea902
VZ
927#endif // wxUSE_GUI
928
e6045e08 929 DWORD dwExitCode = data->dwExitCode;
cb6827a8
VZ
930 delete data;
931
e6045e08
VZ
932 // return the exit code
933 return dwExitCode;
cb6827a8
VZ
934#else // Win16
935 long instanceID = WinExec((LPCSTR) WXSTRINGCAST command, SW_SHOW);
fbf456aa
VZ
936 if (instanceID < 32)
937 return flags & wxEXEC_SYNC ? -1 : 0;
e6045e08 938
fbf456aa
VZ
939 if ( flags & wxEXEC_SYNC )
940 {
cb6827a8 941 int running;
fbf456aa
VZ
942 do
943 {
cb6827a8 944 wxYield();
27a9bd48 945 running = GetModuleUsage((HINSTANCE)instanceID);
cb6827a8
VZ
946 } while (running);
947 }
948
fbf456aa 949 return instanceID;
cb6827a8 950#endif // Win16/32
32592631 951}
c740f496 952
0493ba13 953long wxExecute(wxChar **argv, int flags, wxProcess *handler)
c740f496 954{
cb6827a8 955 wxString command;
e6045e08 956
0493ba13 957 for ( ;; )
cb6827a8 958 {
0493ba13
VZ
959 command += *argv++;
960 if ( !*argv )
961 break;
cb6827a8 962
0493ba13
VZ
963 command += _T(' ');
964 }
cb6827a8 965
fbf456aa 966 return wxExecute(command, flags, handler);
c740f496 967}
006162a9 968