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