]> git.saurik.com Git - wxWidgets.git/blob - src/msw/utilsexc.cpp
Attempt to make wxWindows play better with XP themes
[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 licence
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__)
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 #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
503 wxString command;
504
505 #if wxUSE_IPC
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
512 // of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which
513 // case we execute just <command> and process the rest below
514 wxString ddeServer, ddeTopic, ddeCommand;
515 static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
516 if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
517 {
518 // speed up the concatenations below
519 ddeServer.reserve(256);
520 ddeTopic.reserve(256);
521 ddeCommand.reserve(256);
522
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 }
573
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
578 if ( !(flags & wxEXEC_SYNC) )
579 {
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 }
588 }
589 }
590 else
591 #endif // wxUSE_IPC
592 {
593 // no DDE
594 command = cmd;
595 }
596
597 #if defined(__WIN32__)
598
599 // the IO redirection is only supported with wxUSE_STREAMS
600 BOOL redirect = FALSE;
601
602 #if wxUSE_STREAMS
603 wxPipe pipeIn, pipeOut, pipeErr;
604
605 // we'll save here the copy of pipeIn[Write]
606 HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
607
608 // open the pipes to which child process IO will be redirected if needed
609 if ( handler && handler->IsRedirected() )
610 {
611 // create pipes for redirecting stdin, stdout and stderr
612 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
613 {
614 wxLogSysError(_("Failed to redirect the child process IO"));
615
616 // indicate failure: we need to return different error code
617 // depending on the sync flag
618 return flags & wxEXEC_SYNC ? -1 : 0;
619 }
620
621 redirect = TRUE;
622 }
623 #endif // wxUSE_STREAMS
624
625 // create the process
626 STARTUPINFO si;
627 wxZeroMemory(si);
628 si.cb = sizeof(si);
629
630 #if wxUSE_STREAMS
631 if ( redirect )
632 {
633 si.dwFlags = STARTF_USESTDHANDLES;
634
635 si.hStdInput = pipeIn[wxPipe::Read];
636 si.hStdOutput = pipeOut[wxPipe::Write];
637 si.hStdError = pipeErr[wxPipe::Write];
638
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 }
647
648 // we must duplicate the handle to the write side of stdin pipe to make
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
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()
653 HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
654 if ( !::DuplicateHandle
655 (
656 ::GetCurrentProcess(),
657 pipeInWrite,
658 ::GetCurrentProcess(),
659 &hpipeStdinWrite,
660 0, // desired access: unused here
661 FALSE, // not inherited
662 DUPLICATE_SAME_ACCESS // same access as for src handle
663 ) )
664 {
665 wxLogLastError(_T("DuplicateHandle"));
666 }
667
668 ::CloseHandle(pipeInWrite);
669 }
670 #endif // wxUSE_STREAMS
671
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 {
694 ::CloseHandle(pipeIn.Detach(wxPipe::Read));
695 ::CloseHandle(pipeOut.Detach(wxPipe::Write));
696 ::CloseHandle(pipeErr.Detach(wxPipe::Write));
697 }
698 #endif // wxUSE_STREAMS
699
700 if ( !ok )
701 {
702 #if wxUSE_STREAMS
703 // close the other handles too
704 if ( redirect )
705 {
706 ::CloseHandle(pipeOut.Detach(wxPipe::Read));
707 ::CloseHandle(pipeErr.Detach(wxPipe::Read));
708 }
709 #endif // wxUSE_STREAMS
710
711 wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
712
713 return flags & wxEXEC_SYNC ? -1 : 0;
714 }
715
716 #if wxUSE_STREAMS
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
722 if ( redirect )
723 {
724 // We can now initialize the wxStreams
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);
733
734 bufOut.Init(outStream);
735 bufErr.Init(errStream);
736 }
737 #endif // wxUSE_STREAMS
738
739 // register the class for the hidden window used for the notifications
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 {
752 wxLogLastError(wxT("RegisterClass(hidden window)"));
753 }
754 }
755
756 // create a hidden window to receive notification about process
757 // termination
758 HWND hwnd = ::CreateWindow(gs_classForHiddenWindow, NULL,
759 WS_OVERLAPPEDWINDOW,
760 0, 0, 0, 0, NULL,
761 (HMENU)NULL, wxGetInstance(), 0);
762 wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
763
764 // Alloc data
765 wxExecuteData *data = new wxExecuteData;
766 data->hProcess = pi.hProcess;
767 data->dwProcessId = pi.dwProcessId;
768 data->hWnd = hwnd;
769 data->state = (flags & wxEXEC_SYNC) != 0;
770 if ( flags & wxEXEC_SYNC )
771 {
772 // handler may be !NULL for capturing program output, but we don't use
773 // it wxExecuteData struct in this case
774 data->handler = NULL;
775 }
776 else
777 {
778 // may be NULL or not
779 data->handler = handler;
780 }
781
782 DWORD tid;
783 HANDLE hThread = ::CreateThread(NULL,
784 0,
785 wxExecuteThread,
786 (void *)data,
787 0,
788 &tid);
789
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?
795 wxLogLastError(wxT("ResumeThread in wxExecute"));
796 }
797
798 // close unneeded handle
799 if ( !::CloseHandle(pi.hThread) )
800 wxLogLastError(wxT("CloseHandle(hThread)"));
801
802 if ( !hThread )
803 {
804 wxLogLastError(wxT("CreateThread in wxExecute"));
805
806 DestroyWindow(hwnd);
807 delete data;
808
809 // the process still started up successfully...
810 return pi.dwProcessId;
811 }
812
813 ::CloseHandle(hThread);
814
815 #if wxUSE_IPC
816 // second part of DDE hack: now establish the DDE conversation with the
817 // just launched process
818 if ( !ddeServer.empty() )
819 {
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 */) )
828 {
829 default:
830 wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") );
831 // fall through
832
833 case -1:
834 wxLogLastError(_T("WaitForInputIdle() in wxExecute"));
835
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);
845 }
846 }
847 #endif // wxUSE_IPC
848
849 if ( !(flags & wxEXEC_SYNC) )
850 {
851 // clean up will be done when the process terminates
852
853 // return the pid
854 return pi.dwProcessId;
855 }
856
857 // disable all app windows while waiting for the child process to finish
858 #if wxUSE_GUI
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;
877 {
878 wxBusyCursor bc;
879
880 // first disable all existing windows
881 wxWindowDisabler wd;
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();
895 #endif // wxUSE_GUI
896
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
909 #if wxUSE_GUI
910 // repaint the GUI
911 wxYield();
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
918 }
919
920 #if wxUSE_GUI
921 }
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();
927 #endif // wxUSE_GUI
928
929 DWORD dwExitCode = data->dwExitCode;
930 delete data;
931
932 // return the exit code
933 return dwExitCode;
934 #else // Win16
935 long instanceID = WinExec((LPCSTR) WXSTRINGCAST command, SW_SHOW);
936 if (instanceID < 32)
937 return flags & wxEXEC_SYNC ? -1 : 0;
938
939 if ( flags & wxEXEC_SYNC )
940 {
941 int running;
942 do
943 {
944 wxYield();
945 running = GetModuleUsage((HINSTANCE)instanceID);
946 } while (running);
947 }
948
949 return instanceID;
950 #endif // Win16/32
951 }
952
953 long wxExecute(wxChar **argv, int flags, wxProcess *handler)
954 {
955 wxString command;
956
957 for ( ;; )
958 {
959 command += *argv++;
960 if ( !*argv )
961 break;
962
963 command += _T(' ');
964 }
965
966 return wxExecute(command, flags, handler);
967 }
968