]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/utilsexc.cpp
fixes needed for separate DLL build to work
[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__)
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__)
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
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
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(_T(""),
458 ddeServer,
459 ddeTopic);
460 if ( !conn )
461 {
462 ok = FALSE;
463 }
464 else // connected to DDE server
465 {
466 // the added complication here is that although most
467 // programs use XTYP_EXECUTE for their DDE API, some
468 // important ones - like IE and other MS stuff - use
469 // XTYP_REQUEST!
470 //
471 // so we try one first and then the other one if it
472 // failed
473 {
474 wxLogNull noErrors;
475 ok = conn->Execute(ddeCommand);
476 }
477
478 if ( !ok )
479 {
480 // now try request - but show the errors
481 ok = conn->Request(ddeCommand) != NULL;
482 }
483 }
484
485 return ok;
486}
487
488#endif // wxUSE_IPC
489
490long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
491{
492 wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
493
494#if wxUSE_THREADS
495 // for many reasons, the code below breaks down if it's called from another
496 // thread -- this could be fixed, but as Unix versions don't support this
497 // neither I don't want to waste time on this now
498 wxASSERT_MSG( wxThread::IsMain(),
499 _T("wxExecute() can be called only from the main thread") );
500#endif // wxUSE_THREADS
501
502 wxString command;
503
504#if wxUSE_IPC
505 // DDE hack: this is really not pretty, but we need to allow this for
506 // transparent handling of DDE servers in wxMimeTypesManager. Usually it
507 // returns the command which should be run to view/open/... a file of the
508 // given type. Sometimes, however, this command just launches the server
509 // and an additional DDE request must be made to really open the file. To
510 // keep all this well hidden from the application, we allow a special form
511 // of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which
512 // case we execute just <command> and process the rest below
513 wxString ddeServer, ddeTopic, ddeCommand;
514 static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
515 if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
516 {
517 // speed up the concatenations below
518 ddeServer.reserve(256);
519 ddeTopic.reserve(256);
520 ddeCommand.reserve(256);
521
522 const wxChar *p = cmd.c_str() + 7;
523 while ( *p && *p != _T('#') )
524 {
525 command += *p++;
526 }
527
528 if ( *p )
529 {
530 // skip '#'
531 p++;
532 }
533 else
534 {
535 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
536 }
537
538 while ( *p && *p != _T('#') )
539 {
540 ddeServer += *p++;
541 }
542
543 if ( *p )
544 {
545 // skip '#'
546 p++;
547 }
548 else
549 {
550 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
551 }
552
553 while ( *p && *p != _T('#') )
554 {
555 ddeTopic += *p++;
556 }
557
558 if ( *p )
559 {
560 // skip '#'
561 p++;
562 }
563 else
564 {
565 wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
566 }
567
568 while ( *p )
569 {
570 ddeCommand += *p++;
571 }
572
573 // if we want to just launch the program and not wait for its
574 // termination, try to execute DDE command right now, it can succeed if
575 // the process is already running - but as it fails if it's not
576 // running, suppress any errors it might generate
577 if ( !(flags & wxEXEC_SYNC) )
578 {
579 wxLogNull noErrors;
580 if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
581 {
582 // a dummy PID - this is a hack, of course, but it's well worth
583 // it as we don't open a new server each time we're called
584 // which would be quite bad
585 return -1;
586 }
587 }
588 }
589 else
590#endif // wxUSE_IPC
591 {
592 // no DDE
593 command = cmd;
594 }
595
596 // the IO redirection is only supported with wxUSE_STREAMS
597 BOOL redirect = FALSE;
598
599#if wxUSE_STREAMS
600 wxPipe pipeIn, pipeOut, pipeErr;
601
602 // we'll save here the copy of pipeIn[Write]
603 HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
604
605 // open the pipes to which child process IO will be redirected if needed
606 if ( handler && handler->IsRedirected() )
607 {
608 // create pipes for redirecting stdin, stdout and stderr
609 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
610 {
611 wxLogSysError(_("Failed to redirect the child process IO"));
612
613 // indicate failure: we need to return different error code
614 // depending on the sync flag
615 return flags & wxEXEC_SYNC ? -1 : 0;
616 }
617
618 redirect = TRUE;
619 }
620#endif // wxUSE_STREAMS
621
622 // create the process
623 STARTUPINFO si;
624 wxZeroMemory(si);
625 si.cb = sizeof(si);
626
627#if wxUSE_STREAMS
628 if ( redirect )
629 {
630 si.dwFlags = STARTF_USESTDHANDLES;
631
632 si.hStdInput = pipeIn[wxPipe::Read];
633 si.hStdOutput = pipeOut[wxPipe::Write];
634 si.hStdError = pipeErr[wxPipe::Write];
635
636 // when the std IO is redirected, we don't show the (console) process
637 // window by default, but this can be overridden by the caller by
638 // specifying wxEXEC_NOHIDE flag
639 if ( !(flags & wxEXEC_NOHIDE) )
640 {
641 si.dwFlags |= STARTF_USESHOWWINDOW;
642 si.wShowWindow = SW_HIDE;
643 }
644
645 // we must duplicate the handle to the write side of stdin pipe to make
646 // it non inheritable: indeed, we must close the writing end of pipeIn
647 // before launching the child process as otherwise this handle will be
648 // inherited by the child which will never close it and so the pipe
649 // will never be closed and the child will be left stuck in ReadFile()
650 HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
651 if ( !::DuplicateHandle
652 (
653 ::GetCurrentProcess(),
654 pipeInWrite,
655 ::GetCurrentProcess(),
656 &hpipeStdinWrite,
657 0, // desired access: unused here
658 FALSE, // not inherited
659 DUPLICATE_SAME_ACCESS // same access as for src handle
660 ) )
661 {
662 wxLogLastError(_T("DuplicateHandle"));
663 }
664
665 ::CloseHandle(pipeInWrite);
666 }
667#endif // wxUSE_STREAMS
668
669 PROCESS_INFORMATION pi;
670 DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
671
672 bool ok = ::CreateProcess
673 (
674 NULL, // application name (use only cmd line)
675 (wxChar *)
676 command.c_str(), // full command line
677 NULL, // security attributes: defaults for both
678 NULL, // the process and its main thread
679 redirect, // inherit handles if we use pipes
680 dwFlags, // process creation flags
681 NULL, // environment (use the same)
682 NULL, // current directory (use the same)
683 &si, // startup info (unused here)
684 &pi // process info
685 ) != 0;
686
687#if wxUSE_STREAMS
688 // we can close the pipe ends used by child anyhow
689 if ( redirect )
690 {
691 ::CloseHandle(pipeIn.Detach(wxPipe::Read));
692 ::CloseHandle(pipeOut.Detach(wxPipe::Write));
693 ::CloseHandle(pipeErr.Detach(wxPipe::Write));
694 }
695#endif // wxUSE_STREAMS
696
697 if ( !ok )
698 {
699#if wxUSE_STREAMS
700 // close the other handles too
701 if ( redirect )
702 {
703 ::CloseHandle(pipeOut.Detach(wxPipe::Read));
704 ::CloseHandle(pipeErr.Detach(wxPipe::Read));
705 }
706#endif // wxUSE_STREAMS
707
708 wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
709
710 return flags & wxEXEC_SYNC ? -1 : 0;
711 }
712
713#if wxUSE_STREAMS
714 // the input buffer bufOut is connected to stdout, this is why it is
715 // called bufOut and not bufIn
716 wxStreamTempInputBuffer bufOut,
717 bufErr;
718
719 if ( redirect )
720 {
721 // We can now initialize the wxStreams
722 wxPipeInputStream *
723 outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
724 wxPipeInputStream *
725 errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
726 wxPipeOutputStream *
727 inStream = new wxPipeOutputStream(hpipeStdinWrite);
728
729 handler->SetPipeStreams(outStream, inStream, errStream);
730
731 bufOut.Init(outStream);
732 bufErr.Init(errStream);
733 }
734#endif // wxUSE_STREAMS
735
736 // register the class for the hidden window used for the notifications
737 if ( !gs_classForHiddenWindow )
738 {
739 gs_classForHiddenWindow = _T("wxHiddenWindow");
740
741 WNDCLASS wndclass;
742 wxZeroMemory(wndclass);
743 wndclass.lpfnWndProc = (WNDPROC)wxExecuteWindowCbk;
744 wndclass.hInstance = wxGetInstance();
745 wndclass.lpszClassName = gs_classForHiddenWindow;
746
747 if ( !::RegisterClass(&wndclass) )
748 {
749 wxLogLastError(wxT("RegisterClass(hidden window)"));
750 }
751 }
752
753 // create a hidden window to receive notification about process
754 // termination
755 HWND hwnd = ::CreateWindow(gs_classForHiddenWindow, NULL,
756 WS_OVERLAPPEDWINDOW,
757 0, 0, 0, 0, NULL,
758 (HMENU)NULL, wxGetInstance(), 0);
759 wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
760
761 // Alloc data
762 wxExecuteData *data = new wxExecuteData;
763 data->hProcess = pi.hProcess;
764 data->dwProcessId = pi.dwProcessId;
765 data->hWnd = hwnd;
766 data->state = (flags & wxEXEC_SYNC) != 0;
767 if ( flags & wxEXEC_SYNC )
768 {
769 // handler may be !NULL for capturing program output, but we don't use
770 // it wxExecuteData struct in this case
771 data->handler = NULL;
772 }
773 else
774 {
775 // may be NULL or not
776 data->handler = handler;
777 }
778
779 DWORD tid;
780 HANDLE hThread = ::CreateThread(NULL,
781 0,
782 wxExecuteThread,
783 (void *)data,
784 0,
785 &tid);
786
787 // resume process we created now - whether the thread creation succeeded or
788 // not
789 if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
790 {
791 // ignore it - what can we do?
792 wxLogLastError(wxT("ResumeThread in wxExecute"));
793 }
794
795 // close unneeded handle
796 if ( !::CloseHandle(pi.hThread) )
797 wxLogLastError(wxT("CloseHandle(hThread)"));
798
799 if ( !hThread )
800 {
801 wxLogLastError(wxT("CreateThread in wxExecute"));
802
803 DestroyWindow(hwnd);
804 delete data;
805
806 // the process still started up successfully...
807 return pi.dwProcessId;
808 }
809
810 ::CloseHandle(hThread);
811
812#if wxUSE_IPC
813 // second part of DDE hack: now establish the DDE conversation with the
814 // just launched process
815 if ( !ddeServer.empty() )
816 {
817 bool ok;
818
819 // give the process the time to init itself
820 //
821 // we use a very big timeout hoping that WaitForInputIdle() will return
822 // much sooner, but not INFINITE just in case the process hangs
823 // completely - like this we will regain control sooner or later
824 switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
825 {
826 default:
827 wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") );
828 // fall through
829
830 case -1:
831 wxLogLastError(_T("WaitForInputIdle() in wxExecute"));
832
833 case WAIT_TIMEOUT:
834 wxLogDebug(_T("Timeout too small in WaitForInputIdle"));
835
836 ok = FALSE;
837 break;
838
839 case 0:
840 // ok, process ready to accept DDE requests
841 ok = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
842 }
843 }
844#endif // wxUSE_IPC
845
846 if ( !(flags & wxEXEC_SYNC) )
847 {
848 // clean up will be done when the process terminates
849
850 // return the pid
851 return pi.dwProcessId;
852 }
853
854 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
855 wxCHECK_MSG( traits, -1, _T("no wxAppTraits in wxExecute()?") );
856
857 // disable all app windows while waiting for the child process to finish
858 void *cookie = traits->BeforeChildWaitLoop();
859
860 // wait until the child process terminates
861 while ( data->state )
862 {
863#if wxUSE_STREAMS
864 bufOut.Update();
865 bufErr.Update();
866#endif // wxUSE_STREAMS
867
868 // don't eat 100% of the CPU -- ugly but anything else requires
869 // real async IO which we don't have for the moment
870 ::Sleep(50);
871
872 // we must process messages or we'd never get wxWM_PROC_TERMINATED
873 traits->AlwaysYield();
874 }
875
876 traits->AfterChildWaitLoop(cookie);
877
878 DWORD dwExitCode = data->dwExitCode;
879 delete data;
880
881 // return the exit code
882 return dwExitCode;
883}
884
885long wxExecute(wxChar **argv, int flags, wxProcess *handler)
886{
887 wxString command;
888
889 for ( ;; )
890 {
891 command += *argv++;
892 if ( !*argv )
893 break;
894
895 command += _T(' ');
896 }
897
898 return wxExecute(command, flags, handler);
899}
900