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