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