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