fixes for wxExecute() with IO redirection: wxStreamTempInputBuffer is now used under MSW as well
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@16638 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -227,6 +227,7 @@ All (GUI):
|
||||
wxMSW:
|
||||
|
||||
- small appearance fixes for native look under Windows XP
|
||||
- fixed multiple bugs in wxExecute() with IO redirection
|
||||
- refresh the buttons properly when the window is resized (Hans Van Leemputten)
|
||||
- huge (40*) speed up in wxMask::Create()
|
||||
- changing wxWindows styles also changes the underlying Windows window style
|
||||
@@ -256,12 +257,9 @@ wxMSW:
|
||||
- multiple events avoided in wxComboBox
|
||||
- tooltip asserts avoided for read-only wxComboBox
|
||||
- fixed a race condition during a thread exit and a join
|
||||
- fixed a condition where a thread can hang during
|
||||
message/event processing
|
||||
- fixed a condition where a thread can hang during message/event processing
|
||||
- increased space between wxRadioBox label and first radio button
|
||||
- don't fail to register remaining window classes if one fails to register
|
||||
- set window proc for non-control windows to avoid problems
|
||||
with multiple wxWindows apps running simultaneously
|
||||
|
||||
wxGTK:
|
||||
|
||||
|
134
src/common/execcmn.cpp
Normal file
134
src/common/execcmn.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: common/wxexec.cpp
|
||||
// Purpose: defines wxStreamTempInputBuffer which is used by Unix and MSW
|
||||
// implementations of wxExecute; this file is only used by the
|
||||
// library and never by the user code
|
||||
// Author: Vadim Zeitlin
|
||||
// Modified by:
|
||||
// Created: 20.08.02
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) 2002 Vadim Zeitlin <vadim@wxwindows.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_WXEXEC_CPP_
|
||||
#define _WX_WXEXEC_CPP_
|
||||
|
||||
// this file should never be compiled directly, just included by other code
|
||||
#ifndef _WX_USED_BY_WXEXECUTE_
|
||||
#error "Please don't exclude this file from build!"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxStreamTempInputBuffer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
wxStreamTempInputBuffer is a hack which we need to solve the problem of
|
||||
executing a child process synchronously with IO redirecting: when we do
|
||||
this, the child writes to a pipe we open to it but when the pipe buffer
|
||||
(which has finite capacity, e.g. commonly just 4Kb) becomes full we have to
|
||||
read data from it because the child blocks in its write() until then and if
|
||||
it blocks we are never going to return from wxExecute() so we dead lock.
|
||||
|
||||
So here is the fix: we now read the output as soon as it appears into a temp
|
||||
buffer (wxStreamTempInputBuffer object) and later just stuff it back into
|
||||
the stream when the process terminates. See supporting code in wxExecute()
|
||||
itself as well.
|
||||
|
||||
Note that this is horribly inefficient for large amounts of output (count
|
||||
the number of times we copy the data around) and so a better API is badly
|
||||
needed! However it's not easy to devise a way to do this keeping backwards
|
||||
compatibility with the existing wxExecute(wxEXEC_SYNC)...
|
||||
*/
|
||||
|
||||
class wxStreamTempInputBuffer
|
||||
{
|
||||
public:
|
||||
wxStreamTempInputBuffer();
|
||||
|
||||
// call to associate a stream with this buffer, otherwise nothing happens
|
||||
// at all
|
||||
void Init(wxPipeInputStream *stream);
|
||||
|
||||
// check for input on our stream and cache it in our buffer if any
|
||||
void Update();
|
||||
|
||||
~wxStreamTempInputBuffer();
|
||||
|
||||
private:
|
||||
// the stream we're buffering, if NULL we don't do anything at all
|
||||
wxPipeInputStream *m_stream;
|
||||
|
||||
// the buffer of size m_size (NULL if m_size == 0)
|
||||
void *m_buffer;
|
||||
|
||||
// the size of the buffer
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
inline wxStreamTempInputBuffer::wxStreamTempInputBuffer()
|
||||
{
|
||||
m_stream = NULL;
|
||||
m_buffer = NULL;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
inline void wxStreamTempInputBuffer::Init(wxPipeInputStream *stream)
|
||||
{
|
||||
m_stream = stream;
|
||||
}
|
||||
|
||||
void wxStreamTempInputBuffer::Update()
|
||||
{
|
||||
if ( m_stream && m_stream->IsAvailable() )
|
||||
{
|
||||
// realloc in blocks of 4Kb: this is the default (and minimal) buffer
|
||||
// size of the Unix pipes so it should be the optimal step
|
||||
static const size_t incSize = 4096;
|
||||
|
||||
void *buf = realloc(m_buffer, m_size + incSize);
|
||||
if ( !buf )
|
||||
{
|
||||
// don't read any more, we don't have enough memory to do it
|
||||
m_stream = NULL;
|
||||
}
|
||||
else // got memory for the buffer
|
||||
{
|
||||
m_buffer = buf;
|
||||
m_stream->Read((char *)m_buffer + m_size, incSize);
|
||||
m_size += m_stream->LastRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxStreamTempInputBuffer::~wxStreamTempInputBuffer()
|
||||
{
|
||||
if ( m_buffer )
|
||||
{
|
||||
m_stream->Ungetch(m_buffer, m_size);
|
||||
free(m_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// platform-dependent parts of wxProcess implementation included
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool wxProcess::IsInputOpened() const
|
||||
{
|
||||
return m_inputStream && ((wxPipeInputStream *)m_inputStream)->IsOpened();
|
||||
}
|
||||
|
||||
bool wxProcess::IsInputAvailable() const
|
||||
{
|
||||
return m_inputStream && ((wxPipeInputStream *)m_inputStream)->IsAvailable();
|
||||
}
|
||||
|
||||
bool wxProcess::IsErrorAvailable() const
|
||||
{
|
||||
return m_errorStream && ((wxPipeInputStream *)m_errorStream)->IsAvailable();
|
||||
}
|
||||
|
||||
#endif // _WX_WXEXEC_CPP_
|
||||
|
@@ -1,11 +1,11 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: msw/utilsexec.cpp
|
||||
// Purpose: Various utilities
|
||||
// Purpose: wxExecute implementation for MSW
|
||||
// Author: Julian Smart
|
||||
// Modified by:
|
||||
// Created: 04/01/98
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) Julian Smart and Markus Holzem
|
||||
// Copyright: (c) 1998-2002 wxWindows dev team
|
||||
// Licence: wxWindows license
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -131,9 +131,13 @@ class wxPipeInputStream: public wxInputStream
|
||||
{
|
||||
public:
|
||||
wxPipeInputStream(HANDLE hInput);
|
||||
~wxPipeInputStream();
|
||||
virtual ~wxPipeInputStream();
|
||||
|
||||
virtual bool Eof() const;
|
||||
// returns TRUE if the pipe is still opened
|
||||
bool IsOpened() const { return m_hInput != INVALID_HANDLE_VALUE; }
|
||||
|
||||
// returns TRUE if there is any data to be read from the pipe
|
||||
bool IsAvailable() const;
|
||||
|
||||
protected:
|
||||
size_t OnSysRead(void *buffer, size_t len);
|
||||
@@ -146,7 +150,7 @@ class wxPipeOutputStream: public wxOutputStream
|
||||
{
|
||||
public:
|
||||
wxPipeOutputStream(HANDLE hOutput);
|
||||
~wxPipeOutputStream();
|
||||
virtual ~wxPipeOutputStream();
|
||||
|
||||
protected:
|
||||
size_t OnSysWrite(const void *buffer, size_t len);
|
||||
@@ -155,103 +159,93 @@ protected:
|
||||
HANDLE m_hOutput;
|
||||
};
|
||||
|
||||
// ==================
|
||||
// wxPipeInputStream
|
||||
// ==================
|
||||
// define this to let wxexec.cpp know that we know what we're doing
|
||||
#define _WX_USED_BY_WXEXECUTE_
|
||||
#include "../common/execcmn.cpp"
|
||||
|
||||
wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxPipe represents a Win32 anonymous pipe
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class wxPipe
|
||||
{
|
||||
m_hInput = hInput;
|
||||
}
|
||||
|
||||
wxPipeInputStream::~wxPipeInputStream()
|
||||
{
|
||||
::CloseHandle(m_hInput);
|
||||
}
|
||||
|
||||
bool wxPipeInputStream::Eof() const
|
||||
{
|
||||
DWORD nAvailable;
|
||||
|
||||
// function name is misleading, it works with anon pipes as well
|
||||
DWORD rc = ::PeekNamedPipe
|
||||
(
|
||||
m_hInput, // handle
|
||||
NULL, 0, // ptr to buffer and its size
|
||||
NULL, // [out] bytes read
|
||||
&nAvailable, // [out] bytes available
|
||||
NULL // [out] bytes left
|
||||
);
|
||||
|
||||
if ( !rc )
|
||||
public:
|
||||
// the symbolic names for the pipe ends
|
||||
enum Direction
|
||||
{
|
||||
if ( ::GetLastError() != ERROR_BROKEN_PIPE )
|
||||
Read,
|
||||
Write
|
||||
};
|
||||
|
||||
// default ctor doesn't do anything
|
||||
wxPipe() { m_handles[Read] = m_handles[Write] = INVALID_HANDLE_VALUE; }
|
||||
|
||||
// create the pipe, return TRUE if ok, FALSE on error
|
||||
bool Create()
|
||||
{
|
||||
// unexpected error
|
||||
wxLogLastError(_T("PeekNamedPipe"));
|
||||
// default secutiry attributes
|
||||
SECURITY_ATTRIBUTES security;
|
||||
|
||||
security.nLength = sizeof(security);
|
||||
security.lpSecurityDescriptor = NULL;
|
||||
security.bInheritHandle = TRUE; // to pass it to the child
|
||||
|
||||
if ( !::CreatePipe(&m_handles[0], &m_handles[1], &security, 0) )
|
||||
{
|
||||
wxLogSysError(_("Failed to create an anonymous pipe"));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// don't try to continue reading from a pipe if an error occured or if
|
||||
// it had been closed
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nAvailable == 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
|
||||
{
|
||||
// reading from a pipe may block if there is no more data, always check for
|
||||
// EOF first
|
||||
m_lasterror = wxSTREAM_NOERROR;
|
||||
if ( Eof() )
|
||||
return 0;
|
||||
// return TRUE if we were created successfully
|
||||
bool IsOk() const { return m_handles[Read] != INVALID_HANDLE_VALUE; }
|
||||
|
||||
DWORD bytesRead;
|
||||
if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
|
||||
// return the descriptor for one of the pipe ends
|
||||
HANDLE operator[](Direction which) const
|
||||
{
|
||||
if ( ::GetLastError() == ERROR_BROKEN_PIPE )
|
||||
m_lasterror = wxSTREAM_EOF;
|
||||
else
|
||||
m_lasterror = wxSTREAM_READ_ERROR;
|
||||
wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
|
||||
_T("invalid pipe index") );
|
||||
|
||||
return m_handles[which];
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
// ==================
|
||||
// wxPipeOutputStream
|
||||
// ==================
|
||||
|
||||
wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput)
|
||||
{
|
||||
m_hOutput = hOutput;
|
||||
}
|
||||
|
||||
wxPipeOutputStream::~wxPipeOutputStream()
|
||||
{
|
||||
::CloseHandle(m_hOutput);
|
||||
}
|
||||
|
||||
size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
|
||||
{
|
||||
DWORD bytesRead;
|
||||
|
||||
m_lasterror = wxSTREAM_NOERROR;
|
||||
if ( !::WriteFile(m_hOutput, buffer, len, &bytesRead, NULL) )
|
||||
// detach a descriptor, meaning that the pipe dtor won't close it, and
|
||||
// return it
|
||||
HANDLE Detach(Direction which)
|
||||
{
|
||||
if ( ::GetLastError() == ERROR_BROKEN_PIPE )
|
||||
m_lasterror = wxSTREAM_EOF;
|
||||
else
|
||||
m_lasterror = wxSTREAM_READ_ERROR;
|
||||
wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
|
||||
_T("invalid pipe index") );
|
||||
|
||||
HANDLE handle = m_handles[which];
|
||||
m_handles[which] = INVALID_HANDLE_VALUE;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
// close the pipe descriptors
|
||||
void Close()
|
||||
{
|
||||
for ( size_t n = 0; n < WXSIZEOF(m_handles); n++ )
|
||||
{
|
||||
if ( m_handles[n] != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
::CloseHandle(m_handles[n]);
|
||||
m_handles[n] = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WIN32__
|
||||
// dtor closes the pipe descriptors
|
||||
~wxPipe() { Close(); }
|
||||
|
||||
private:
|
||||
HANDLE m_handles[2];
|
||||
};
|
||||
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
// ============================================================================
|
||||
// implementation
|
||||
@@ -259,14 +253,22 @@ size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
|
||||
|
||||
#ifdef __WIN32__
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// process termination detecting support
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// thread function for the thread monitoring the process termination
|
||||
static DWORD __stdcall wxExecuteThread(void *arg)
|
||||
{
|
||||
wxExecuteData *data = (wxExecuteData*)arg;
|
||||
|
||||
WaitForSingleObject(data->hProcess, INFINITE);
|
||||
if ( ::WaitForSingleObject(data->hProcess, INFINITE) != WAIT_OBJECT_0 )
|
||||
{
|
||||
wxLogDebug(_T("Waiting for the process termination failed!"));
|
||||
}
|
||||
|
||||
// get the exit code
|
||||
if ( !GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
|
||||
if ( !::GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
|
||||
{
|
||||
wxLogLastError(wxT("GetExitCodeProcess"));
|
||||
}
|
||||
@@ -275,7 +277,7 @@ static DWORD __stdcall wxExecuteThread(void *arg)
|
||||
wxT("process should have terminated") );
|
||||
|
||||
// send a message indicating process termination to the window
|
||||
SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
|
||||
::SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -315,8 +317,128 @@ LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// implementation of IO redirection support classes
|
||||
// ============================================================================
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxPipeInputStreams
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
|
||||
{
|
||||
m_hInput = hInput;
|
||||
}
|
||||
|
||||
wxPipeInputStream::~wxPipeInputStream()
|
||||
{
|
||||
if ( m_hInput != INVALID_HANDLE_VALUE )
|
||||
::CloseHandle(m_hInput);
|
||||
}
|
||||
|
||||
bool wxPipeInputStream::IsAvailable() const
|
||||
{
|
||||
if ( !IsOpened() )
|
||||
return FALSE;
|
||||
|
||||
DWORD nAvailable;
|
||||
|
||||
// function name is misleading, it works with anon pipes as well
|
||||
DWORD rc = ::PeekNamedPipe
|
||||
(
|
||||
m_hInput, // handle
|
||||
NULL, 0, // ptr to buffer and its size
|
||||
NULL, // [out] bytes read
|
||||
&nAvailable, // [out] bytes available
|
||||
NULL // [out] bytes left
|
||||
);
|
||||
|
||||
if ( !rc )
|
||||
{
|
||||
if ( ::GetLastError() != ERROR_BROKEN_PIPE )
|
||||
{
|
||||
// unexpected error
|
||||
wxLogLastError(_T("PeekNamedPipe"));
|
||||
}
|
||||
|
||||
// don't try to continue reading from a pipe if an error occured or if
|
||||
// it had been closed
|
||||
::CloseHandle(m_hInput);
|
||||
|
||||
wxConstCast(this, wxPipeInputStream)->m_hInput = INVALID_HANDLE_VALUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return nAvailable != 0;
|
||||
}
|
||||
|
||||
size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
|
||||
{
|
||||
// reading from a pipe may block if there is no more data, always check for
|
||||
// EOF first
|
||||
if ( !IsAvailable() )
|
||||
{
|
||||
m_lasterror = wxSTREAM_EOF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_lasterror = wxSTREAM_NOERROR;
|
||||
|
||||
DWORD bytesRead;
|
||||
if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
|
||||
{
|
||||
if ( ::GetLastError() == ERROR_BROKEN_PIPE )
|
||||
m_lasterror = wxSTREAM_EOF;
|
||||
else
|
||||
m_lasterror = wxSTREAM_READ_ERROR;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxPipeOutputStream
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput)
|
||||
{
|
||||
m_hOutput = hOutput;
|
||||
}
|
||||
|
||||
wxPipeOutputStream::~wxPipeOutputStream()
|
||||
{
|
||||
::CloseHandle(m_hOutput);
|
||||
}
|
||||
|
||||
size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
|
||||
{
|
||||
DWORD bytesRead;
|
||||
|
||||
m_lasterror = wxSTREAM_NOERROR;
|
||||
if ( !::WriteFile(m_hOutput, buffer, len, &bytesRead, NULL) )
|
||||
{
|
||||
if ( ::GetLastError() == ERROR_BROKEN_PIPE )
|
||||
m_lasterror = wxSTREAM_EOF;
|
||||
else
|
||||
m_lasterror = wxSTREAM_READ_ERROR;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
#endif // Win32
|
||||
|
||||
// ============================================================================
|
||||
// wxExecute functions family
|
||||
// ============================================================================
|
||||
|
||||
#if wxUSE_IPC
|
||||
|
||||
// connect to the given server via DDE and ask it to execute the command
|
||||
@@ -462,46 +584,26 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
|
||||
// the IO redirection is only supported with wxUSE_STREAMS
|
||||
BOOL redirect = FALSE;
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
// the first elements are reading ends, the second are the writing ones
|
||||
HANDLE hpipeStdin[2],
|
||||
hpipeStdout[2],
|
||||
hpipeStderr[2];
|
||||
wxPipe pipeIn, pipeOut, pipeErr;
|
||||
|
||||
// we'll save here the copy of pipeIn[Write]
|
||||
HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
|
||||
|
||||
// open the pipes to which child process IO will be redirected if needed
|
||||
if ( handler && handler->IsRedirected() )
|
||||
{
|
||||
// default secutiry attributes
|
||||
SECURITY_ATTRIBUTES security;
|
||||
|
||||
security.nLength = sizeof(security);
|
||||
security.lpSecurityDescriptor = NULL;
|
||||
security.bInheritHandle = TRUE;
|
||||
|
||||
// create stdin pipe
|
||||
if ( !::CreatePipe(&hpipeStdin[0], &hpipeStdin[1], &security, 0) )
|
||||
// create pipes for redirecting stdin, stdout and stderr
|
||||
if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
|
||||
{
|
||||
wxLogSysError(_("Can't create the inter-process read pipe"));
|
||||
wxLogSysError(_("Failed to redirect the child process IO"));
|
||||
|
||||
// indicate failure: we need to return different error code
|
||||
// depending on the sync flag
|
||||
return flags & wxEXEC_SYNC ? -1 : 0;
|
||||
}
|
||||
|
||||
// and a stdout one
|
||||
if ( !::CreatePipe(&hpipeStdout[0], &hpipeStdout[1], &security, 0) )
|
||||
{
|
||||
::CloseHandle(hpipeStdin[0]);
|
||||
::CloseHandle(hpipeStdin[1]);
|
||||
|
||||
wxLogSysError(_("Can't create the inter-process write pipe"));
|
||||
|
||||
return flags & wxEXEC_SYNC ? -1 : 0;
|
||||
}
|
||||
|
||||
(void)::CreatePipe(&hpipeStderr[0], &hpipeStderr[1], &security, 0);
|
||||
|
||||
redirect = TRUE;
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
@@ -516,9 +618,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
{
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
si.hStdInput = hpipeStdin[0];
|
||||
si.hStdOutput = hpipeStdout[1];
|
||||
si.hStdError = hpipeStderr[1];
|
||||
si.hStdInput = pipeIn[wxPipe::Read];
|
||||
si.hStdOutput = pipeOut[wxPipe::Write];
|
||||
si.hStdError = pipeErr[wxPipe::Write];
|
||||
|
||||
// when the std IO is redirected, we don't show the (console) process
|
||||
// window by default, but this can be overridden by the caller by
|
||||
@@ -530,15 +632,16 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
}
|
||||
|
||||
// we must duplicate the handle to the write side of stdin pipe to make
|
||||
// it non inheritable: indeed, we must close hpipeStdin[1] before
|
||||
// launching the child process as otherwise this handle will be
|
||||
// it non inheritable: indeed, we must close the writing end of pipeIn
|
||||
// before launching the child process as otherwise this handle will be
|
||||
// inherited by the child which will never close it and so the pipe
|
||||
// will never be closed and the child will be left stuck in ReadFile()
|
||||
HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
|
||||
if ( !::DuplicateHandle
|
||||
(
|
||||
GetCurrentProcess(),
|
||||
hpipeStdin[1],
|
||||
GetCurrentProcess(),
|
||||
::GetCurrentProcess(),
|
||||
pipeInWrite,
|
||||
::GetCurrentProcess(),
|
||||
&hpipeStdinWrite,
|
||||
0, // desired access: unused here
|
||||
FALSE, // not inherited
|
||||
@@ -548,7 +651,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
wxLogLastError(_T("DuplicateHandle"));
|
||||
}
|
||||
|
||||
::CloseHandle(hpipeStdin[1]);
|
||||
::CloseHandle(pipeInWrite);
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
@@ -574,9 +677,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
// we can close the pipe ends used by child anyhow
|
||||
if ( redirect )
|
||||
{
|
||||
::CloseHandle(hpipeStdin[0]);
|
||||
::CloseHandle(hpipeStdout[1]);
|
||||
::CloseHandle(hpipeStderr[1]);
|
||||
::CloseHandle(pipeIn.Detach(wxPipe::Read));
|
||||
::CloseHandle(pipeOut.Detach(wxPipe::Write));
|
||||
::CloseHandle(pipeErr.Detach(wxPipe::Write));
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
@@ -586,8 +689,8 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
// close the other handles too
|
||||
if ( redirect )
|
||||
{
|
||||
::CloseHandle(hpipeStdout[0]);
|
||||
::CloseHandle(hpipeStderr[0]);
|
||||
::CloseHandle(pipeOut.Detach(wxPipe::Read));
|
||||
::CloseHandle(pipeErr.Detach(wxPipe::Read));
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
@@ -597,14 +700,25 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
}
|
||||
|
||||
#if wxUSE_STREAMS
|
||||
// the input buffer bufOut is connected to stdout, this is why it is
|
||||
// called bufOut and not bufIn
|
||||
wxStreamTempInputBuffer bufOut,
|
||||
bufErr;
|
||||
|
||||
if ( redirect )
|
||||
{
|
||||
// We can now initialize the wxStreams
|
||||
wxInputStream *inStream = new wxPipeInputStream(hpipeStdout[0]),
|
||||
*errStream = new wxPipeInputStream(hpipeStderr[0]);
|
||||
wxOutputStream *outStream = new wxPipeOutputStream(hpipeStdinWrite);
|
||||
wxPipeInputStream *
|
||||
outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
|
||||
wxPipeInputStream *
|
||||
errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
|
||||
wxPipeOutputStream *
|
||||
inStream = new wxPipeOutputStream(hpipeStdinWrite);
|
||||
|
||||
handler->SetPipeStreams(inStream, outStream, errStream);
|
||||
handler->SetPipeStreams(outStream, inStream, errStream);
|
||||
|
||||
bufOut.Init(outStream);
|
||||
bufErr.Init(errStream);
|
||||
}
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
@@ -734,10 +848,19 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
||||
wxWindowDisabler wd;
|
||||
#endif // wxUSE_GUI
|
||||
|
||||
// wait until the child process terminates
|
||||
while ( data->state )
|
||||
{
|
||||
// don't take 100% of the CPU
|
||||
::Sleep(500);
|
||||
#if wxUSE_STREAMS
|
||||
bufOut.Update();
|
||||
bufErr.Update();
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
// don't eat 100% of the CPU -- ugly but anything else requires
|
||||
// real async IO which we don't have for the moment
|
||||
::Sleep(50);
|
||||
|
||||
// repaint the GUI
|
||||
wxYield();
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: utilsunx.cpp
|
||||
// Name: unix/utilsunx.cpp
|
||||
// Purpose: generic Unix implementation of many wx functions
|
||||
// Author: Vadim Zeitlin
|
||||
// Id: $Id$
|
||||
@@ -354,19 +354,22 @@ void wxHandleProcessTermination(wxEndProcessData *proc_data)
|
||||
#if wxUSE_STREAMS
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxProcessFileInputStream: stream for reading from a pipe
|
||||
// wxPipeInputStream: stream for reading from a pipe
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class wxProcessFileInputStream : public wxFileInputStream
|
||||
class wxPipeInputStream : public wxFileInputStream
|
||||
{
|
||||
public:
|
||||
wxProcessFileInputStream(int fd) : wxFileInputStream(fd) { }
|
||||
wxPipeInputStream(int fd) : wxFileInputStream(fd) { }
|
||||
|
||||
// return TRUE if the pipe is still opened
|
||||
bool IsOpened() const { return TRUE; } // TODO
|
||||
|
||||
// return TRUE if we have anything to read, don't block
|
||||
bool IsAvailable() const;
|
||||
};
|
||||
|
||||
bool wxProcessFileInputStream::IsAvailable() const
|
||||
bool wxPipeInputStream::IsAvailable() const
|
||||
{
|
||||
if ( m_lasterror == wxSTREAM_EOF )
|
||||
return TRUE;
|
||||
@@ -400,109 +403,9 @@ bool wxProcessFileInputStream::IsAvailable() const
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxStreamTempInputBuffer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Extract of a mail to wx-users to give the context of the problem we are
|
||||
trying to solve here:
|
||||
|
||||
MC> If I run the command:
|
||||
MC> find . -name "*.h" -exec grep linux {} \;
|
||||
MC> in the exec sample synchronously from the 'Capture command output'
|
||||
MC> menu, wxExecute never returns. I have to xkill it. Has anyone
|
||||
MC> else encountered this?
|
||||
|
||||
Yes, I can reproduce it too.
|
||||
|
||||
I even think I understand why it happens: before launching the external
|
||||
command we set up a pipe with a valid file descriptor on the reading side
|
||||
when the output is redirected. So the subprocess happily writes to it ...
|
||||
until the pipe buffer (which is usually quite big on Unix, I think the
|
||||
default is 4Kb) is full. Then the writing process stops and waits until we
|
||||
read some data from the pipe to be able to continue writing to it but we
|
||||
never do it because we wait until it terminates to start reading and so we
|
||||
have a classical deadlock.
|
||||
|
||||
Here is the fix: we now read the output as soon as it appears into a temp
|
||||
buffer (wxStreamTempInputBuffer object) and later just stuff it back into the
|
||||
stream when the process terminates. See supporting code in wxExecute()
|
||||
itself as well.
|
||||
|
||||
Note that this is horribly inefficient for large amounts of output (count
|
||||
the number of times we copy the data around) and so a better API is badly
|
||||
needed!
|
||||
*/
|
||||
|
||||
class wxStreamTempInputBuffer
|
||||
{
|
||||
public:
|
||||
wxStreamTempInputBuffer();
|
||||
|
||||
// call to associate a stream with this buffer, otherwise nothing happens
|
||||
// at all
|
||||
void Init(wxProcessFileInputStream *stream);
|
||||
|
||||
// check for input on our stream and cache it in our buffer if any
|
||||
void Update();
|
||||
|
||||
~wxStreamTempInputBuffer();
|
||||
|
||||
private:
|
||||
// the stream we're buffering, if NULL we don't do anything at all
|
||||
wxProcessFileInputStream *m_stream;
|
||||
|
||||
// the buffer of size m_size (NULL if m_size == 0)
|
||||
void *m_buffer;
|
||||
|
||||
// the size of the buffer
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
wxStreamTempInputBuffer::wxStreamTempInputBuffer()
|
||||
{
|
||||
m_stream = NULL;
|
||||
m_buffer = NULL;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void wxStreamTempInputBuffer::Init(wxProcessFileInputStream *stream)
|
||||
{
|
||||
m_stream = stream;
|
||||
}
|
||||
|
||||
void wxStreamTempInputBuffer::Update()
|
||||
{
|
||||
if ( m_stream && m_stream->IsAvailable() )
|
||||
{
|
||||
// realloc in blocks of 4Kb: this is the default (and minimal) buffer
|
||||
// size of the Unix pipes so it should be the optimal step
|
||||
static const size_t incSize = 4096;
|
||||
|
||||
void *buf = realloc(m_buffer, m_size + incSize);
|
||||
if ( !buf )
|
||||
{
|
||||
// don't read any more, we don't have enough memory to do it
|
||||
m_stream = NULL;
|
||||
}
|
||||
else // got memory for the buffer
|
||||
{
|
||||
m_buffer = buf;
|
||||
m_stream->Read((char *)m_buffer + m_size, incSize);
|
||||
m_size += m_stream->LastRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxStreamTempInputBuffer::~wxStreamTempInputBuffer()
|
||||
{
|
||||
if ( m_buffer )
|
||||
{
|
||||
m_stream->Ungetch(m_buffer, m_size);
|
||||
free(m_buffer);
|
||||
}
|
||||
}
|
||||
// define this to let wxexec.cpp know that we know what we're doing
|
||||
#define _WX_USED_BY_WXEXECUTE_
|
||||
#include "../common/execcmn.cpp"
|
||||
|
||||
#endif // wxUSE_STREAMS
|
||||
|
||||
@@ -586,8 +489,9 @@ private:
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxExecute: the real worker function
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifdef __VMS
|
||||
#pragma message disable codeunreachable
|
||||
#pragma message disable codeunreachable
|
||||
#endif
|
||||
|
||||
long wxExecute(wxChar **argv,
|
||||
@@ -764,11 +668,11 @@ long wxExecute(wxChar **argv,
|
||||
wxOutputStream *inStream =
|
||||
new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
|
||||
|
||||
wxProcessFileInputStream *outStream =
|
||||
new wxProcessFileInputStream(pipeOut.Detach(wxPipe::Read));
|
||||
wxPipeInputStream *outStream =
|
||||
new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
|
||||
|
||||
wxProcessFileInputStream *errStream =
|
||||
new wxProcessFileInputStream(pipeErr.Detach(wxPipe::Read));
|
||||
wxPipeInputStream *errStream =
|
||||
new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
|
||||
|
||||
process->SetPipeStreams(outStream, inStream, errStream);
|
||||
|
||||
@@ -854,8 +758,9 @@ long wxExecute(wxChar **argv,
|
||||
|
||||
return ERROR_RETURN_CODE;
|
||||
}
|
||||
|
||||
#ifdef __VMS
|
||||
#pragma message enable codeunreachable
|
||||
#pragma message enable codeunreachable
|
||||
#endif
|
||||
|
||||
#undef ERROR_RETURN_CODE
|
||||
|
Reference in New Issue
Block a user