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