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:
|
wxMSW:
|
||||||
|
|
||||||
- small appearance fixes for native look under Windows XP
|
- 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)
|
- refresh the buttons properly when the window is resized (Hans Van Leemputten)
|
||||||
- huge (40*) speed up in wxMask::Create()
|
- huge (40*) speed up in wxMask::Create()
|
||||||
- changing wxWindows styles also changes the underlying Windows window style
|
- changing wxWindows styles also changes the underlying Windows window style
|
||||||
@@ -256,12 +257,9 @@ wxMSW:
|
|||||||
- multiple events avoided in wxComboBox
|
- multiple events avoided in wxComboBox
|
||||||
- tooltip asserts avoided for read-only wxComboBox
|
- tooltip asserts avoided for read-only wxComboBox
|
||||||
- fixed a race condition during a thread exit and a join
|
- fixed a race condition during a thread exit and a join
|
||||||
- fixed a condition where a thread can hang during
|
- fixed a condition where a thread can hang during message/event processing
|
||||||
message/event processing
|
|
||||||
- increased space between wxRadioBox label and first radio button
|
- increased space between wxRadioBox label and first radio button
|
||||||
- don't fail to register remaining window classes if one fails to register
|
- 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:
|
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
|
// Name: msw/utilsexec.cpp
|
||||||
// Purpose: Various utilities
|
// Purpose: wxExecute implementation for MSW
|
||||||
// Author: Julian Smart
|
// Author: Julian Smart
|
||||||
// Modified by:
|
// Modified by:
|
||||||
// Created: 04/01/98
|
// Created: 04/01/98
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) Julian Smart and Markus Holzem
|
// Copyright: (c) 1998-2002 wxWindows dev team
|
||||||
// Licence: wxWindows license
|
// Licence: wxWindows license
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -131,9 +131,13 @@ class wxPipeInputStream: public wxInputStream
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxPipeInputStream(HANDLE hInput);
|
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:
|
protected:
|
||||||
size_t OnSysRead(void *buffer, size_t len);
|
size_t OnSysRead(void *buffer, size_t len);
|
||||||
@@ -146,7 +150,7 @@ class wxPipeOutputStream: public wxOutputStream
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxPipeOutputStream(HANDLE hOutput);
|
wxPipeOutputStream(HANDLE hOutput);
|
||||||
~wxPipeOutputStream();
|
virtual ~wxPipeOutputStream();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t OnSysWrite(const void *buffer, size_t len);
|
size_t OnSysWrite(const void *buffer, size_t len);
|
||||||
@@ -155,103 +159,93 @@ protected:
|
|||||||
HANDLE m_hOutput;
|
HANDLE m_hOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================
|
// define this to let wxexec.cpp know that we know what we're doing
|
||||||
// wxPipeInputStream
|
#define _WX_USED_BY_WXEXECUTE_
|
||||||
// ==================
|
#include "../common/execcmn.cpp"
|
||||||
|
|
||||||
wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
|
// ----------------------------------------------------------------------------
|
||||||
|
// wxPipe represents a Win32 anonymous pipe
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class wxPipe
|
||||||
{
|
{
|
||||||
m_hInput = hInput;
|
public:
|
||||||
}
|
// the symbolic names for the pipe ends
|
||||||
|
enum Direction
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
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()
|
||||||
|
{
|
||||||
|
// 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) )
|
||||||
{
|
{
|
||||||
// unexpected error
|
wxLogSysError(_("Failed to create an anonymous pipe"));
|
||||||
wxLogLastError(_T("PeekNamedPipe"));
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't try to continue reading from a pipe if an error occured or if
|
|
||||||
// it had been closed
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return nAvailable == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
|
// return TRUE if we were created successfully
|
||||||
{
|
bool IsOk() const { return m_handles[Read] != INVALID_HANDLE_VALUE; }
|
||||||
// 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;
|
|
||||||
|
|
||||||
DWORD bytesRead;
|
// return the descriptor for one of the pipe ends
|
||||||
if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
|
HANDLE operator[](Direction which) const
|
||||||
{
|
{
|
||||||
if ( ::GetLastError() == ERROR_BROKEN_PIPE )
|
wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
|
||||||
m_lasterror = wxSTREAM_EOF;
|
_T("invalid pipe index") );
|
||||||
else
|
|
||||||
m_lasterror = wxSTREAM_READ_ERROR;
|
return m_handles[which];
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesRead;
|
// detach a descriptor, meaning that the pipe dtor won't close it, and
|
||||||
}
|
// return it
|
||||||
|
HANDLE Detach(Direction which)
|
||||||
// ==================
|
|
||||||
// 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 )
|
wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_handles),
|
||||||
m_lasterror = wxSTREAM_EOF;
|
_T("invalid pipe index") );
|
||||||
else
|
|
||||||
m_lasterror = wxSTREAM_READ_ERROR;
|
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
|
// implementation
|
||||||
@@ -259,14 +253,22 @@ size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
|
|||||||
|
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// process termination detecting support
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// thread function for the thread monitoring the process termination
|
||||||
static DWORD __stdcall wxExecuteThread(void *arg)
|
static DWORD __stdcall wxExecuteThread(void *arg)
|
||||||
{
|
{
|
||||||
wxExecuteData *data = (wxExecuteData*)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
|
// get the exit code
|
||||||
if ( !GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
|
if ( !::GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
|
||||||
{
|
{
|
||||||
wxLogLastError(wxT("GetExitCodeProcess"));
|
wxLogLastError(wxT("GetExitCodeProcess"));
|
||||||
}
|
}
|
||||||
@@ -275,7 +277,7 @@ static DWORD __stdcall wxExecuteThread(void *arg)
|
|||||||
wxT("process should have terminated") );
|
wxT("process should have terminated") );
|
||||||
|
|
||||||
// send a message indicating process termination to the window
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -315,8 +317,128 @@ LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
|
|||||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
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
|
#endif // Win32
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// wxExecute functions family
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
#if wxUSE_IPC
|
#if wxUSE_IPC
|
||||||
|
|
||||||
// connect to the given server via DDE and ask it to execute the command
|
// 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
|
// the IO redirection is only supported with wxUSE_STREAMS
|
||||||
BOOL redirect = FALSE;
|
BOOL redirect = FALSE;
|
||||||
|
|
||||||
#if wxUSE_STREAMS
|
#if wxUSE_STREAMS
|
||||||
// the first elements are reading ends, the second are the writing ones
|
wxPipe pipeIn, pipeOut, pipeErr;
|
||||||
HANDLE hpipeStdin[2],
|
|
||||||
hpipeStdout[2],
|
// we'll save here the copy of pipeIn[Write]
|
||||||
hpipeStderr[2];
|
|
||||||
HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
|
HANDLE hpipeStdinWrite = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
// open the pipes to which child process IO will be redirected if needed
|
// open the pipes to which child process IO will be redirected if needed
|
||||||
if ( handler && handler->IsRedirected() )
|
if ( handler && handler->IsRedirected() )
|
||||||
{
|
{
|
||||||
// default secutiry attributes
|
// create pipes for redirecting stdin, stdout and stderr
|
||||||
SECURITY_ATTRIBUTES security;
|
if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
|
||||||
|
|
||||||
security.nLength = sizeof(security);
|
|
||||||
security.lpSecurityDescriptor = NULL;
|
|
||||||
security.bInheritHandle = TRUE;
|
|
||||||
|
|
||||||
// create stdin pipe
|
|
||||||
if ( !::CreatePipe(&hpipeStdin[0], &hpipeStdin[1], &security, 0) )
|
|
||||||
{
|
{
|
||||||
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
|
// indicate failure: we need to return different error code
|
||||||
// depending on the sync flag
|
// depending on the sync flag
|
||||||
return flags & wxEXEC_SYNC ? -1 : 0;
|
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;
|
redirect = TRUE;
|
||||||
}
|
}
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
@@ -516,9 +618,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
|||||||
{
|
{
|
||||||
si.dwFlags = STARTF_USESTDHANDLES;
|
si.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
si.hStdInput = hpipeStdin[0];
|
si.hStdInput = pipeIn[wxPipe::Read];
|
||||||
si.hStdOutput = hpipeStdout[1];
|
si.hStdOutput = pipeOut[wxPipe::Write];
|
||||||
si.hStdError = hpipeStderr[1];
|
si.hStdError = pipeErr[wxPipe::Write];
|
||||||
|
|
||||||
// when the std IO is redirected, we don't show the (console) process
|
// 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
|
// 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
|
// we must duplicate the handle to the write side of stdin pipe to make
|
||||||
// it non inheritable: indeed, we must close hpipeStdin[1] before
|
// it non inheritable: indeed, we must close the writing end of pipeIn
|
||||||
// launching the child process as otherwise this handle will be
|
// before launching the child process as otherwise this handle will be
|
||||||
// inherited by the child which will never close it and so the pipe
|
// 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()
|
// will never be closed and the child will be left stuck in ReadFile()
|
||||||
|
HANDLE pipeInWrite = pipeIn.Detach(wxPipe::Write);
|
||||||
if ( !::DuplicateHandle
|
if ( !::DuplicateHandle
|
||||||
(
|
(
|
||||||
GetCurrentProcess(),
|
::GetCurrentProcess(),
|
||||||
hpipeStdin[1],
|
pipeInWrite,
|
||||||
GetCurrentProcess(),
|
::GetCurrentProcess(),
|
||||||
&hpipeStdinWrite,
|
&hpipeStdinWrite,
|
||||||
0, // desired access: unused here
|
0, // desired access: unused here
|
||||||
FALSE, // not inherited
|
FALSE, // not inherited
|
||||||
@@ -548,7 +651,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
|||||||
wxLogLastError(_T("DuplicateHandle"));
|
wxLogLastError(_T("DuplicateHandle"));
|
||||||
}
|
}
|
||||||
|
|
||||||
::CloseHandle(hpipeStdin[1]);
|
::CloseHandle(pipeInWrite);
|
||||||
}
|
}
|
||||||
#endif // wxUSE_STREAMS
|
#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
|
// we can close the pipe ends used by child anyhow
|
||||||
if ( redirect )
|
if ( redirect )
|
||||||
{
|
{
|
||||||
::CloseHandle(hpipeStdin[0]);
|
::CloseHandle(pipeIn.Detach(wxPipe::Read));
|
||||||
::CloseHandle(hpipeStdout[1]);
|
::CloseHandle(pipeOut.Detach(wxPipe::Write));
|
||||||
::CloseHandle(hpipeStderr[1]);
|
::CloseHandle(pipeErr.Detach(wxPipe::Write));
|
||||||
}
|
}
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
@@ -586,8 +689,8 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
|||||||
// close the other handles too
|
// close the other handles too
|
||||||
if ( redirect )
|
if ( redirect )
|
||||||
{
|
{
|
||||||
::CloseHandle(hpipeStdout[0]);
|
::CloseHandle(pipeOut.Detach(wxPipe::Read));
|
||||||
::CloseHandle(hpipeStderr[0]);
|
::CloseHandle(pipeErr.Detach(wxPipe::Read));
|
||||||
}
|
}
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
@@ -597,14 +700,25 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if wxUSE_STREAMS
|
#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 )
|
if ( redirect )
|
||||||
{
|
{
|
||||||
// We can now initialize the wxStreams
|
// We can now initialize the wxStreams
|
||||||
wxInputStream *inStream = new wxPipeInputStream(hpipeStdout[0]),
|
wxPipeInputStream *
|
||||||
*errStream = new wxPipeInputStream(hpipeStderr[0]);
|
outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
|
||||||
wxOutputStream *outStream = new wxPipeOutputStream(hpipeStdinWrite);
|
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
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
@@ -734,12 +848,21 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
|
|||||||
wxWindowDisabler wd;
|
wxWindowDisabler wd;
|
||||||
#endif // wxUSE_GUI
|
#endif // wxUSE_GUI
|
||||||
|
|
||||||
while ( data->state )
|
// wait until the child process terminates
|
||||||
{
|
while ( data->state )
|
||||||
// don't take 100% of the CPU
|
{
|
||||||
::Sleep(500);
|
#if wxUSE_STREAMS
|
||||||
wxYield();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
#if wxUSE_GUI
|
#if wxUSE_GUI
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// Name: utilsunx.cpp
|
// Name: unix/utilsunx.cpp
|
||||||
// Purpose: generic Unix implementation of many wx functions
|
// Purpose: generic Unix implementation of many wx functions
|
||||||
// Author: Vadim Zeitlin
|
// Author: Vadim Zeitlin
|
||||||
// Id: $Id$
|
// Id: $Id$
|
||||||
@@ -354,19 +354,22 @@ void wxHandleProcessTermination(wxEndProcessData *proc_data)
|
|||||||
#if wxUSE_STREAMS
|
#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:
|
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
|
// return TRUE if we have anything to read, don't block
|
||||||
bool IsAvailable() const;
|
bool IsAvailable() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wxProcessFileInputStream::IsAvailable() const
|
bool wxPipeInputStream::IsAvailable() const
|
||||||
{
|
{
|
||||||
if ( m_lasterror == wxSTREAM_EOF )
|
if ( m_lasterror == wxSTREAM_EOF )
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -400,109 +403,9 @@ bool wxProcessFileInputStream::IsAvailable() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// define this to let wxexec.cpp know that we know what we're doing
|
||||||
// wxStreamTempInputBuffer
|
#define _WX_USED_BY_WXEXECUTE_
|
||||||
// ----------------------------------------------------------------------------
|
#include "../common/execcmn.cpp"
|
||||||
|
|
||||||
/*
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // wxUSE_STREAMS
|
#endif // wxUSE_STREAMS
|
||||||
|
|
||||||
@@ -586,8 +489,9 @@ private:
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// wxExecute: the real worker function
|
// wxExecute: the real worker function
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __VMS
|
#ifdef __VMS
|
||||||
#pragma message disable codeunreachable
|
#pragma message disable codeunreachable
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
long wxExecute(wxChar **argv,
|
long wxExecute(wxChar **argv,
|
||||||
@@ -764,11 +668,11 @@ long wxExecute(wxChar **argv,
|
|||||||
wxOutputStream *inStream =
|
wxOutputStream *inStream =
|
||||||
new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
|
new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
|
||||||
|
|
||||||
wxProcessFileInputStream *outStream =
|
wxPipeInputStream *outStream =
|
||||||
new wxProcessFileInputStream(pipeOut.Detach(wxPipe::Read));
|
new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
|
||||||
|
|
||||||
wxProcessFileInputStream *errStream =
|
wxPipeInputStream *errStream =
|
||||||
new wxProcessFileInputStream(pipeErr.Detach(wxPipe::Read));
|
new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
|
||||||
|
|
||||||
process->SetPipeStreams(outStream, inStream, errStream);
|
process->SetPipeStreams(outStream, inStream, errStream);
|
||||||
|
|
||||||
@@ -854,8 +758,9 @@ long wxExecute(wxChar **argv,
|
|||||||
|
|
||||||
return ERROR_RETURN_CODE;
|
return ERROR_RETURN_CODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __VMS
|
#ifdef __VMS
|
||||||
#pragma message enable codeunreachable
|
#pragma message enable codeunreachable
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef ERROR_RETURN_CODE
|
#undef ERROR_RETURN_CODE
|
||||||
@@ -868,13 +773,13 @@ long wxExecute(wxChar **argv,
|
|||||||
const wxChar* wxGetHomeDir( wxString *home )
|
const wxChar* wxGetHomeDir( wxString *home )
|
||||||
{
|
{
|
||||||
*home = wxGetUserHome( wxString() );
|
*home = wxGetUserHome( wxString() );
|
||||||
wxString tmp;
|
wxString tmp;
|
||||||
if ( home->IsEmpty() )
|
if ( home->IsEmpty() )
|
||||||
*home = wxT("/");
|
*home = wxT("/");
|
||||||
#ifdef __VMS
|
#ifdef __VMS
|
||||||
tmp = *home;
|
tmp = *home;
|
||||||
if ( tmp.Last() != wxT(']'))
|
if ( tmp.Last() != wxT(']'))
|
||||||
if ( tmp.Last() != wxT('/')) *home << wxT('/');
|
if ( tmp.Last() != wxT('/')) *home << wxT('/');
|
||||||
#endif
|
#endif
|
||||||
return home->c_str();
|
return home->c_str();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user