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