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