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