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