]> git.saurik.com Git - wxWidgets.git/blob - src/msw/utilsexc.cpp
Some improvements to accessibility behaviour
[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 #if wxUSE_GUI // See 'dirty hack' below.
37 #include "wx/frame.h"
38 #endif
39 #endif
40
41 #ifdef __WIN32__
42 #include "wx/stream.h"
43 #include "wx/process.h"
44 #endif
45
46 #include "wx/msw/private.h"
47
48 #include <ctype.h>
49
50 #if !defined(__GNUWIN32__) && !defined(__SALFORDC__) && !defined(__WXMICROWIN__)
51 #include <direct.h>
52 #ifndef __MWERKS__
53 #include <dos.h>
54 #endif
55 #endif
56
57 #if defined(__GNUWIN32__) && !defined(__TWIN32__)
58 #include <sys/unistd.h>
59 #include <sys/stat.h>
60 #endif
61
62 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
63 #ifndef __UNIX__
64 #include <io.h>
65 #endif
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__
76 #if !(defined(_MSC_VER) && (_MSC_VER > 800))
77 #include <errno.h>
78 #endif
79 #endif
80 #include <stdarg.h>
81
82 #if wxUSE_IPC
83 #include "wx/dde.h" // for WX_DDE hack in wxExecute
84 #endif // wxUSE_IPC
85
86 // ----------------------------------------------------------------------------
87 // constants
88 // ----------------------------------------------------------------------------
89
90 // this message is sent when the process we're waiting for terminates
91 #define wxWM_PROC_TERMINATED (WM_USER + 10000)
92
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
100 static const wxChar *gs_classForHiddenWindow = NULL;
101
102 // ----------------------------------------------------------------------------
103 // private types
104 // ----------------------------------------------------------------------------
105
106 // structure describing the process we're being waiting for
107 struct wxExecuteData
108 {
109 public:
110 ~wxExecuteData()
111 {
112 #ifndef __WIN16__
113 if ( !::CloseHandle(hProcess) )
114 {
115 wxLogLastError(wxT("CloseHandle(hProcess)"));
116 }
117 #endif
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
126 };
127
128 #if defined(__WIN32__) && wxUSE_STREAMS
129
130 // ----------------------------------------------------------------------------
131 // wxPipeStreams
132 // ----------------------------------------------------------------------------
133
134 class wxPipeInputStream: public wxInputStream
135 {
136 public:
137 wxPipeInputStream(HANDLE hInput);
138 virtual ~wxPipeInputStream();
139
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
144 virtual bool CanRead() const;
145
146 protected:
147 size_t OnSysRead(void *buffer, size_t len);
148
149 protected:
150 HANDLE m_hInput;
151
152 DECLARE_NO_COPY_CLASS(wxPipeInputStream)
153 };
154
155 class wxPipeOutputStream: public wxOutputStream
156 {
157 public:
158 wxPipeOutputStream(HANDLE hOutput);
159 virtual ~wxPipeOutputStream();
160
161 protected:
162 size_t OnSysWrite(const void *buffer, size_t len);
163
164 protected:
165 HANDLE m_hOutput;
166
167 DECLARE_NO_COPY_CLASS(wxPipeOutputStream)
168 };
169
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
178 class wxPipe
179 {
180 public:
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
252 private:
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
269 static DWORD __stdcall wxExecuteThread(void *arg)
270 {
271 wxExecuteData * const data = (wxExecuteData *)arg;
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
295 LRESULT 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
302 wxExecuteData * const data = (wxExecuteData *)lParam;
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 {
325 return ::DefWindowProc(hWnd, message, wParam, lParam);
326 }
327 }
328
329 // ============================================================================
330 // implementation of IO redirection support classes
331 // ============================================================================
332
333 #if wxUSE_STREAMS
334
335 // ----------------------------------------------------------------------------
336 // wxPipeInputStreams
337 // ----------------------------------------------------------------------------
338
339 wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
340 {
341 m_hInput = hInput;
342 }
343
344 wxPipeInputStream::~wxPipeInputStream()
345 {
346 if ( m_hInput != INVALID_HANDLE_VALUE )
347 ::CloseHandle(m_hInput);
348 }
349
350 bool wxPipeInputStream::CanRead() const
351 {
352 if ( !IsOpened() )
353 return FALSE;
354
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 }
374
375 // don't try to continue reading from a pipe if an error occured or if
376 // it had been closed
377 ::CloseHandle(m_hInput);
378
379 wxPipeInputStream *self = wxConstCast(this, wxPipeInputStream);
380
381 self->m_hInput = INVALID_HANDLE_VALUE;
382 self->m_lasterror = wxSTREAM_EOF;
383
384 nAvailable = 0;
385 }
386
387 return nAvailable != 0;
388 }
389
390 size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
391 {
392 if ( !IsOpened() )
393 {
394 m_lasterror = wxSTREAM_EOF;
395
396 return 0;
397 }
398
399 DWORD bytesRead;
400 if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
401 {
402 m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
403 ? wxSTREAM_EOF
404 : wxSTREAM_READ_ERROR;
405 }
406
407 // bytesRead is set to 0, as desired, if an error occured
408 return bytesRead;
409 }
410
411 // ----------------------------------------------------------------------------
412 // wxPipeOutputStream
413 // ----------------------------------------------------------------------------
414
415 wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput)
416 {
417 m_hOutput = hOutput;
418 }
419
420 wxPipeOutputStream::~wxPipeOutputStream()
421 {
422 ::CloseHandle(m_hOutput);
423 }
424
425 size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
426 {
427 DWORD bytesWritten;
428
429 m_lasterror = wxSTREAM_NO_ERROR;
430 if ( !::WriteFile(m_hOutput, buffer, len, &bytesWritten, NULL) )
431 {
432 m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
433 ? wxSTREAM_EOF
434 : wxSTREAM_WRITE_ERROR;
435 }
436
437 return bytesWritten;
438 }
439
440 #endif // wxUSE_STREAMS
441
442 #endif // Win32
443
444 // ============================================================================
445 // wxExecute functions family
446 // ============================================================================
447
448 #if wxUSE_IPC
449
450 // connect to the given server via DDE and ask it to execute the command
451 static bool wxExecuteDDE(const wxString& ddeServer,
452 const wxString& ddeTopic,
453 const wxString& ddeCommand)
454 {
455 bool ok = FALSE;
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 //
472 // so we try one first and then the other one if it
473 // failed
474 {
475 wxLogNull noErrors;
476 ok = conn->Execute(ddeCommand);
477 }
478
479 if ( !ok )
480 {
481 // now try request - but show the errors
482 ok = conn->Request(ddeCommand) != NULL;
483 }
484 }
485
486 return ok;
487 }
488
489 #endif // wxUSE_IPC
490
491 long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
492 {
493 wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
494
495 wxString command;
496
497 #if wxUSE_IPC
498 // DDE hack: this is really not pretty, but we need to allow this for
499 // transparent handling of DDE servers in wxMimeTypesManager. Usually it
500 // returns the command which should be run to view/open/... a file of the
501 // given type. Sometimes, however, this command just launches the server
502 // and an additional DDE request must be made to really open the file. To
503 // keep all this well hidden from the application, we allow a special form
504 // of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which
505 // case we execute just <command> and process the rest below
506 wxString ddeServer, ddeTopic, ddeCommand;
507 static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
508 if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
509 {
510 // speed up the concatenations below
511 ddeServer.reserve(256);
512 ddeTopic.reserve(256);
513 ddeCommand.reserve(256);
514
515 const wxChar *p = cmd.c_str() + 7;
516 while ( *p && *p != _T('#') )
517 {
518 command += *p++;
519 }
520
521 if ( *p )
522 {
523 // skip '#'
524 p++;
525 }
526 else
527 {
528 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
529 }
530
531 while ( *p && *p != _T('#') )
532 {
533 ddeServer += *p++;
534 }
535
536 if ( *p )
537 {
538 // skip '#'
539 p++;
540 }
541 else
542 {
543 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
544 }
545
546 while ( *p && *p != _T('#') )
547 {
548 ddeTopic += *p++;
549 }
550
551 if ( *p )
552 {
553 // skip '#'
554 p++;
555 }
556 else
557 {
558 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
559 }
560
561 while ( *p )
562 {
563 ddeCommand += *p++;
564 }
565
566 // if we want to just launch the program and not wait for its
567 // termination, try to execute DDE command right now, it can succeed if
568 // the process is already running - but as it fails if it's not
569 // running, suppress any errors it might generate
570 if ( !(flags & wxEXEC_SYNC) )
571 {
572 wxLogNull noErrors;
573 if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
574 {
575 // a dummy PID - this is a hack, of course, but it's well worth
576 // it as we don't open a new server each time we're called
577 // which would be quite bad
578 return -1;
579 }
580 }
581 }
582 else
583 #endif // wxUSE_IPC
584 {
585 // no DDE
586 command = cmd;
587 }
588
589 #if defined(__WIN32__) && !defined(__TWIN32__)
590
591 // the IO redirection is only supported with wxUSE_STREAMS
592 BOOL redirect = FALSE;
593
594 #if wxUSE_STREAMS
595 wxPipe pipeIn, pipeOut, pipeErr;
596
597 // we'll save here the copy of pipeIn[Write]
598 HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
599
600 // open the pipes to which child process IO will be redirected if needed
601 if ( handler && handler->IsRedirected() )
602 {
603 // create pipes for redirecting stdin, stdout and stderr
604 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
605 {
606 wxLogSysError(_("Failed to redirect the child process IO"));
607
608 // indicate failure: we need to return different error code
609 // depending on the sync flag
610 return flags & wxEXEC_SYNC ? -1 : 0;
611 }
612
613 redirect = TRUE;
614 }
615 #endif // wxUSE_STREAMS
616
617 // create the process
618 STARTUPINFO si;
619 wxZeroMemory(si);
620 si.cb = sizeof(si);
621
622 #if wxUSE_STREAMS
623 if ( redirect )
624 {
625 si.dwFlags = STARTF_USESTDHANDLES;
626
627 si.hStdInput = pipeIn[wxPipe::Read];
628 si.hStdOutput = pipeOut[wxPipe::Write];
629 si.hStdError = pipeErr[wxPipe::Write];
630
631 // when the std IO is redirected, we don't show the (console) process
632 // window by default, but this can be overridden by the caller by
633 // specifying wxEXEC_NOHIDE flag
634 if ( !(flags & wxEXEC_NOHIDE) )
635 {
636 si.dwFlags |= STARTF_USESHOWWINDOW;
637 si.wShowWindow = SW_HIDE;
638 }
639
640 // we must duplicate the handle to the write side of stdin pipe to make
641 // it non inheritable: indeed, we must close the writing end of pipeIn
642 // before launching the child process as otherwise this handle will be
643 // inherited by the child which will never close it and so the pipe
644 // will never be closed and the child will be left stuck in ReadFile()
645 HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
646 if ( !::DuplicateHandle
647 (
648 ::GetCurrentProcess(),
649 pipeInWrite,
650 ::GetCurrentProcess(),
651 &hpipeStdinWrite,
652 0, // desired access: unused here
653 FALSE, // not inherited
654 DUPLICATE_SAME_ACCESS // same access as for src handle
655 ) )
656 {
657 wxLogLastError(_T("DuplicateHandle"));
658 }
659
660 ::CloseHandle(pipeInWrite);
661 }
662 #endif // wxUSE_STREAMS
663
664 PROCESS_INFORMATION pi;
665 DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
666
667 bool ok = ::CreateProcess
668 (
669 NULL, // application name (use only cmd line)
670 (wxChar *)
671 command.c_str(), // full command line
672 NULL, // security attributes: defaults for both
673 NULL, // the process and its main thread
674 redirect, // inherit handles if we use pipes
675 dwFlags, // process creation flags
676 NULL, // environment (use the same)
677 NULL, // current directory (use the same)
678 &si, // startup info (unused here)
679 &pi // process info
680 ) != 0;
681
682 #if wxUSE_STREAMS
683 // we can close the pipe ends used by child anyhow
684 if ( redirect )
685 {
686 ::CloseHandle(pipeIn.Detach(wxPipe::Read));
687 ::CloseHandle(pipeOut.Detach(wxPipe::Write));
688 ::CloseHandle(pipeErr.Detach(wxPipe::Write));
689 }
690 #endif // wxUSE_STREAMS
691
692 if ( !ok )
693 {
694 #if wxUSE_STREAMS
695 // close the other handles too
696 if ( redirect )
697 {
698 ::CloseHandle(pipeOut.Detach(wxPipe::Read));
699 ::CloseHandle(pipeErr.Detach(wxPipe::Read));
700 }
701 #endif // wxUSE_STREAMS
702
703 wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
704
705 return flags & wxEXEC_SYNC ? -1 : 0;
706 }
707
708 #if wxUSE_STREAMS
709 // the input buffer bufOut is connected to stdout, this is why it is
710 // called bufOut and not bufIn
711 wxStreamTempInputBuffer bufOut,
712 bufErr;
713
714 if ( redirect )
715 {
716 // We can now initialize the wxStreams
717 wxPipeInputStream *
718 outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
719 wxPipeInputStream *
720 errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
721 wxPipeOutputStream *
722 inStream = new wxPipeOutputStream(hpipeStdinWrite);
723
724 handler->SetPipeStreams(outStream, inStream, errStream);
725
726 bufOut.Init(outStream);
727 bufErr.Init(errStream);
728 }
729 #endif // wxUSE_STREAMS
730
731 // register the class for the hidden window used for the notifications
732 if ( !gs_classForHiddenWindow )
733 {
734 gs_classForHiddenWindow = _T("wxHiddenWindow");
735
736 WNDCLASS wndclass;
737 wxZeroMemory(wndclass);
738 wndclass.lpfnWndProc = (WNDPROC)wxExecuteWindowCbk;
739 wndclass.hInstance = wxGetInstance();
740 wndclass.lpszClassName = gs_classForHiddenWindow;
741
742 if ( !::RegisterClass(&wndclass) )
743 {
744 wxLogLastError(wxT("RegisterClass(hidden window)"));
745 }
746 }
747
748 // create a hidden window to receive notification about process
749 // termination
750 HWND hwnd = ::CreateWindow(gs_classForHiddenWindow, NULL,
751 WS_OVERLAPPEDWINDOW,
752 0, 0, 0, 0, NULL,
753 (HMENU)NULL, wxGetInstance(), 0);
754 wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
755
756 // Alloc data
757 wxExecuteData *data = new wxExecuteData;
758 data->hProcess = pi.hProcess;
759 data->dwProcessId = pi.dwProcessId;
760 data->hWnd = hwnd;
761 data->state = (flags & wxEXEC_SYNC) != 0;
762 if ( flags & wxEXEC_SYNC )
763 {
764 // handler may be !NULL for capturing program output, but we don't use
765 // it wxExecuteData struct in this case
766 data->handler = NULL;
767 }
768 else
769 {
770 // may be NULL or not
771 data->handler = handler;
772 }
773
774 DWORD tid;
775 HANDLE hThread = ::CreateThread(NULL,
776 0,
777 wxExecuteThread,
778 (void *)data,
779 0,
780 &tid);
781
782 // resume process we created now - whether the thread creation succeeded or
783 // not
784 if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
785 {
786 // ignore it - what can we do?
787 wxLogLastError(wxT("ResumeThread in wxExecute"));
788 }
789
790 // close unneeded handle
791 if ( !::CloseHandle(pi.hThread) )
792 wxLogLastError(wxT("CloseHandle(hThread)"));
793
794 if ( !hThread )
795 {
796 wxLogLastError(wxT("CreateThread in wxExecute"));
797
798 DestroyWindow(hwnd);
799 delete data;
800
801 // the process still started up successfully...
802 return pi.dwProcessId;
803 }
804
805 ::CloseHandle(hThread);
806
807 #if wxUSE_IPC
808 // second part of DDE hack: now establish the DDE conversation with the
809 // just launched process
810 if ( !ddeServer.empty() )
811 {
812 bool ok;
813
814 // give the process the time to init itself
815 //
816 // we use a very big timeout hoping that WaitForInputIdle() will return
817 // much sooner, but not INFINITE just in case the process hangs
818 // completely - like this we will regain control sooner or later
819 switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
820 {
821 default:
822 wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") );
823 // fall through
824
825 case -1:
826 wxLogLastError(_T("WaitForInputIdle() in wxExecute"));
827
828 case WAIT_TIMEOUT:
829 wxLogDebug(_T("Timeout too small in WaitForInputIdle"));
830
831 ok = FALSE;
832 break;
833
834 case 0:
835 // ok, process ready to accept DDE requests
836 ok = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
837 }
838 }
839 #endif // wxUSE_IPC
840
841 if ( !(flags & wxEXEC_SYNC) )
842 {
843 // clean up will be done when the process terminates
844
845 // return the pid
846 return pi.dwProcessId;
847 }
848
849 // disable all app windows while waiting for the child process to finish
850 #if wxUSE_GUI
851
852 /*
853 We use a dirty hack here to disable all application windows (which we
854 must do because otherwise the calls to wxYield() could lead to some very
855 unexpected reentrancies in the users code) but to avoid losing
856 focus/activation entirely when the child process terminates which would
857 happen if we simply disabled everything using wxWindowDisabler. Indeed,
858 remember that Windows will never activate a disabled window and when the
859 last childs window is closed and Windows looks for a window to activate
860 all our windows are still disabled. There is no way to enable them in
861 time because we don't know when the childs windows are going to be
862 closed, so the solution we use here is to keep one special tiny frame
863 enabled all the time. Then when the child terminates it will get
864 activated and when we close it below -- after reenabling all the other
865 windows! -- the previously active window becomes activated again and
866 everything is ok.
867 */
868 wxWindow *winActive;
869 {
870 wxBusyCursor bc;
871
872 // first disable all existing windows
873 wxWindowDisabler wd;
874
875 // then create an "invisible" frame: it has minimal size, is positioned
876 // (hopefully) outside the screen and doesn't appear on the taskbar
877 winActive = new wxFrame
878 (
879 wxTheApp->GetTopWindow(),
880 -1,
881 _T(""),
882 wxPoint(32600, 32600),
883 wxSize(1, 1),
884 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
885 );
886 winActive->Show();
887 #endif // wxUSE_GUI
888
889 // wait until the child process terminates
890 while ( data->state )
891 {
892 #if wxUSE_STREAMS
893 bufOut.Update();
894 bufErr.Update();
895 #endif // wxUSE_STREAMS
896
897 // don't eat 100% of the CPU -- ugly but anything else requires
898 // real async IO which we don't have for the moment
899 ::Sleep(50);
900
901 #if wxUSE_GUI
902 // repaint the GUI
903 wxYield();
904 #else // !GUI
905 // dispatch the messages to the hidden window so that it could
906 // process the wxWM_PROC_TERMINATED notification
907 MSG msg;
908 ::PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE);
909 #endif // GUI/!GUI
910 }
911
912 #if wxUSE_GUI
913 }
914
915 // finally delete the dummy frame and, as wd has been already destroyed and
916 // the other windows reenabled, the activation is going to return to the
917 // window which had it before
918 winActive->Destroy();
919 #endif // wxUSE_GUI
920
921 DWORD dwExitCode = data->dwExitCode;
922 delete data;
923
924 // return the exit code
925 return dwExitCode;
926 #else // Win16
927 long instanceID = WinExec((LPCSTR) WXSTRINGCAST command, SW_SHOW);
928 if (instanceID < 32)
929 return flags & wxEXEC_SYNC ? -1 : 0;
930
931 if ( flags & wxEXEC_SYNC )
932 {
933 int running;
934 do
935 {
936 wxYield();
937 running = GetModuleUsage((HINSTANCE)instanceID);
938 } while (running);
939 }
940
941 return instanceID;
942 #endif // Win16/32
943 }
944
945 long wxExecute(wxChar **argv, int flags, wxProcess *handler)
946 {
947 wxString command;
948
949 for ( ;; )
950 {
951 command += *argv++;
952 if ( !*argv )
953 break;
954
955 command += _T(' ');
956 }
957
958 return wxExecute(command, flags, handler);
959 }
960