fixes for wxExecute with redirection

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_2_BRANCH@7471 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2000-05-23 22:50:45 +00:00
parent b91a42d6d9
commit a1777c3e92

View File

@@ -121,17 +121,20 @@ public:
bool state; // set to FALSE when the process finishes
};
#if defined(__WIN32__) && wxUSE_STREAMS
// ----------------------------------------------------------------------------
// wxPipeStreams
// ----------------------------------------------------------------------------
class wxPipeInputStream: public wxInputStream {
class wxPipeInputStream: public wxInputStream
{
public:
wxPipeInputStream(HANDLE hInput);
~wxPipeInputStream();
virtual bool Eof() const;
protected:
size_t OnSysRead(void *buffer, size_t len);
@@ -139,7 +142,8 @@ protected:
HANDLE m_hInput;
};
class wxPipeOutputStream: public wxOutputStream {
class wxPipeOutputStream: public wxOutputStream
{
public:
wxPipeOutputStream(HANDLE hOutput);
~wxPipeOutputStream();
@@ -165,17 +169,55 @@ 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 )
{
// unexpected error
wxLogLastError(_T("PeekNamedPipe"));
}
// 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)
{
DWORD bytesRead;
// reading from a pipe may block if there is no more data, always check for
// EOF first
m_lasterror = wxSTREAM_NOERROR;
if (! ::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) ) {
if (GetLastError() == ERROR_BROKEN_PIPE)
if ( Eof() )
return 0;
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;
}
@@ -198,12 +240,14 @@ 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)
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;
}
@@ -214,6 +258,7 @@ size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
// ============================================================================
#ifdef __WIN32__
static DWORD wxExecuteThread(wxExecuteData *data)
{
WaitForSingleObject(data->hProcess, INFINITE);
@@ -347,166 +392,115 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
}
#if defined(__WIN32__) && !defined(__TWIN32__)
// the old code is disabled because we really need a process handle
// if we want to execute it asynchronously or even just get its
// return code and for this we must use CreateProcess() and not
// ShellExecute()
#if 0
// isolate command and arguments
wxString commandName;
bool insideQuotes = FALSE;
const char *pc;
for ( pc = command.c_str(); *pc != '\0'; pc++ )
{
switch ( *pc )
{
case ' ':
case '\t':
if ( !insideQuotes )
break;
// fall through
case '"':
insideQuotes = !insideQuotes;
// fall through
default:
commandName += *pc;
continue; // skip the next break
}
// only reached for space not inside quotes
break;
}
wxString commandArgs = pc;
wxWindow *winTop = wxTheApp->GetTopWindow();
HWND hwndTop = (HWND)(winTop ? winTop->GetHWND() : 0);
HANDLE result;
#ifdef __GNUWIN32__
result = ShellExecute(hwndTop,
(const wchar_t)"open",
(const wchar_t)commandName,
(const wchar_t)commandArgs,
(const wchar_t)NULL,
SW_SHOWNORMAL);
#else // !GNUWIN32
result = ShellExecute(hwndTop, "open", commandName,
commandArgs, NULL, SW_SHOWNORMAL);
#endif // GNUWIN32
if ( ((long)result) <= 32 )
wxLogSysError(_("Can't execute command '%s'"), command.c_str());
return result;
#else // 1
HANDLE hpipeRead[2];
HANDLE hpipeWrite[2];
HANDLE hStdIn = INVALID_HANDLE_VALUE;
HANDLE hStdOut = INVALID_HANDLE_VALUE;
// we need to inherit handles in the child process if we want to redirect
// its IO
BOOL inheritHandles = FALSE;
// 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];
// 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;
if ( !::CreatePipe(&hpipeRead[0], &hpipeRead[1], &security, 0) )
// create stdin pipe
if ( !::CreatePipe(&hpipeStdin[0], &hpipeStdin[1], &security, 0) )
{
wxLogSysError(_("Can't create the inter-process read pipe"));
return 0;
// indicate failure in both cases
return sync ? -1 : 0;
}
if ( !::CreatePipe(&hpipeWrite[0], &hpipeWrite[1], &security, 0) )
// and a stdout one
if ( !::CreatePipe(&hpipeStdout[0], &hpipeStdout[1], &security, 0) )
{
::CloseHandle(hpipeRead[0]);
::CloseHandle(hpipeRead[1]);
::CloseHandle(hpipeStdin[0]);
::CloseHandle(hpipeStdin[1]);
wxLogSysError(_("Can't create the inter-process write pipe"));
return 0;
return sync ? -1 : 0;
}
// We need to save the old stdio handles to restore them after the call
// to CreateProcess
hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
if ( !::SetStdHandle(STD_INPUT_HANDLE, hpipeRead[0]) ||
!::SetStdHandle(STD_OUTPUT_HANDLE, hpipeWrite[1]) )
{
wxLogDebug(_T("Failed to change stdin/out handles"));
}
inheritHandles = TRUE;
redirect = TRUE;
}
#endif // wxUSE_STREAMS
// create the process
STARTUPINFO si;
wxZeroMemory(si);
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
if ( ::CreateProcess(
NULL, // application name (use only cmd line)
(wxChar *)command.c_str(), // full command line
NULL, // security attributes: defaults for both
NULL, // the process and its main thread
inheritHandles, // inherit handles if we need pipes
CREATE_DEFAULT_ERROR_MODE |
CREATE_SUSPENDED, // flags
NULL, // environment (use the same)
NULL, // current directory (use the same)
&si, // startup info (unused here)
&pi // process info
) == 0 )
#if wxUSE_STREAMS
if ( redirect )
{
if ( inheritHandles )
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hpipeStdin[0];
si.hStdOutput = hpipeStdout[1];
}
#endif // wxUSE_STREAMS
PROCESS_INFORMATION pi;
DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
bool ok = ::CreateProcess
(
NULL, // application name (use only cmd line)
(wxChar *)
command.c_str(), // full command line
NULL, // security attributes: defaults for both
NULL, // the process and its main thread
redirect, // inherit handles if we use pipes
dwFlags, // process creation flags
NULL, // environment (use the same)
NULL, // current directory (use the same)
&si, // startup info (unused here)
&pi // process info
) != 0;
#if wxUSE_STREAMS
// we can close the pipe ends used by child anyhow
if ( redirect )
{
::CloseHandle(hpipeStdin[0]);
::CloseHandle(hpipeStdout[1]);
}
#endif // wxUSE_STREAMS
if ( !ok )
{
#if wxUSE_STREAMS
// close the other handles too
if ( redirect )
{
::CloseHandle(hpipeWrite[0]);
::CloseHandle(hpipeWrite[1]);
::CloseHandle(hpipeRead[0]);
::CloseHandle(hpipeRead[1]);
::CloseHandle(hpipeStdin[1]);
::CloseHandle(hpipeStdout[0]);
}
#endif // wxUSE_STREAMS
wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
return 0;
return sync ? -1 : 0;
}
// Restore the old stdio handles
if ( inheritHandles )
{
if ( !::SetStdHandle(STD_INPUT_HANDLE, hStdIn) ||
!::SetStdHandle(STD_OUTPUT_HANDLE, hStdOut) )
{
wxLogDebug(_T("Failed to restore old stdin/out handles"));
}
// they're still opened in child process
::CloseHandle(hpipeWrite[1]);
::CloseHandle(hpipeRead[0]);
#if wxUSE_STREAMS
if ( redirect )
{
// We can now initialize the wxStreams
wxInputStream *inStream = new wxPipeInputStream(hpipeWrite[0]);
wxOutputStream *outStream = new wxPipeOutputStream(hpipeRead[1]);
wxInputStream *inStream = new wxPipeInputStream(hpipeStdout[0]);
wxOutputStream *outStream = new wxPipeOutputStream(hpipeStdin[1]);
handler->SetPipeStreams(inStream, outStream);
#endif
}
#endif // wxUSE_STREAMS
// register the class for the hidden window used for the notifications
if ( !gs_classForHiddenWindow )
@@ -582,6 +576,8 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
return pi.dwProcessId;
}
::CloseHandle(hThread);
#if wxUSE_IPC
// second part of DDE hack: now establish the DDE conversation with the
// just launched process
@@ -626,7 +622,6 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
// return the exit code
return dwExitCode;
#endif // 0/1
#else // Win16
long instanceID = WinExec((LPCSTR) WXSTRINGCAST command, SW_SHOW);
if (instanceID < 32) return(0);