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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user