X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9978ac8e4f52e47a07c146cfa17ba69bf2cc0338..b404a8f3b072129c107c6d9a5e0f6f53cd34807b:/src/msw/utilsexc.cpp diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp index a675969f7e..2887ce5340 100644 --- a/src/msw/utilsexc.cpp +++ b/src/msw/utilsexc.cpp @@ -38,6 +38,7 @@ #include "wx/process.h" #include "wx/thread.h" #include "wx/apptrait.h" +#include "wx/evtloop.h" #include "wx/vector.h" @@ -145,7 +146,7 @@ public: // running processes if ( !::SetEvent(gs_heventShutdown) ) { - wxLogDebug(_T("Failed to set shutdown event in wxExecuteModule")); + wxLogDebug(wxT("Failed to set shutdown event in wxExecuteModule")); } ::CloseHandle(gs_heventShutdown); @@ -164,7 +165,7 @@ public: 3000 // long but finite value ) == WAIT_TIMEOUT ) { - wxLogDebug(_T("Failed to stop all wxExecute monitor threads")); + wxLogDebug(wxT("Failed to stop all wxExecute monitor threads")); } for ( size_t n = 0; n < numThreads; n++ ) @@ -180,7 +181,7 @@ public: { if ( !::UnregisterClass(wxMSWEXEC_WNDCLASSNAME, wxGetInstance()) ) { - wxLogLastError(_T("UnregisterClass(wxExecClass)")); + wxLogLastError(wxT("UnregisterClass(wxExecClass)")); } gs_classForHiddenWindow = NULL; @@ -217,7 +218,7 @@ protected: protected: HANDLE m_hInput; - DECLARE_NO_COPY_CLASS(wxPipeInputStream) + wxDECLARE_NO_COPY_CLASS(wxPipeInputStream); }; class wxPipeOutputStream: public wxOutputStream @@ -233,7 +234,7 @@ protected: protected: HANDLE m_hOutput; - DECLARE_NO_COPY_CLASS(wxPipeOutputStream) + wxDECLARE_NO_COPY_CLASS(wxPipeOutputStream); }; // define this to let wxexec.cpp know that we know what we're doing @@ -335,7 +336,7 @@ static DWORD __stdcall wxExecuteThread(void *arg) gs_heventShutdown = ::CreateEvent(NULL, TRUE, FALSE, NULL); if ( !gs_heventShutdown ) { - wxLogDebug(_T("CreateEvent() in wxExecuteThread failed")); + wxLogDebug(wxT("CreateEvent() in wxExecuteThread failed")); } } @@ -368,7 +369,7 @@ static DWORD __stdcall wxExecuteThread(void *arg) break; default: - wxLogDebug(_T("Waiting for the process termination failed!")); + wxLogDebug(wxT("Waiting for the process termination failed!")); } return 0; @@ -433,8 +434,19 @@ wxPipeInputStream::~wxPipeInputStream() bool wxPipeInputStream::CanRead() const { + // we can read if there's something in the put back buffer + // even pipe is closed + if ( m_wbacksize > m_wbackcur ) + return true; + + wxPipeInputStream * const self = wxConstCast(this, wxPipeInputStream); + if ( !IsOpened() ) + { + // set back to mark Eof as it may have been unset by Ungetch() + self->m_lasterror = wxSTREAM_EOF; return false; + } DWORD nAvailable; @@ -453,15 +465,13 @@ bool wxPipeInputStream::CanRead() const if ( ::GetLastError() != ERROR_BROKEN_PIPE ) { // unexpected error - wxLogLastError(_T("PeekNamedPipe")); + wxLogLastError(wxT("PeekNamedPipe")); } // don't try to continue reading from a pipe if an error occurred or if // it had been closed ::CloseHandle(m_hInput); - wxPipeInputStream *self = wxConstCast(this, wxPipeInputStream); - self->m_hInput = INVALID_HANDLE_VALUE; self->m_lasterror = wxSTREAM_EOF; @@ -512,7 +522,7 @@ wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput) NULL // timeout (we don't set it neither) ) ) { - wxLogLastError(_T("SetNamedPipeHandleState(PIPE_NOWAIT)")); + wxLogLastError(wxT("SetNamedPipeHandleState(PIPE_NOWAIT)")); } } @@ -600,7 +610,8 @@ wxExecuteDDE(const wxString& ddeServer, #endif // wxUSE_IPC -long wxExecute(const wxString& cmd, int flags, wxProcess *handler) +long wxExecute(const wxString& cmd, int flags, wxProcess *handler, + const wxExecuteEnv *env) { wxCHECK_MSG( !cmd.empty(), 0, wxT("empty command in wxExecute") ); @@ -609,7 +620,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) // thread -- this could be fixed, but as Unix versions don't support this // neither I don't want to waste time on this now wxASSERT_MSG( wxThread::IsMain(), - _T("wxExecute() can be called only from the main thread") ); + wxT("wxExecute() can be called only from the main thread") ); #endif // wxUSE_THREADS wxString command; @@ -625,7 +636,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) // case we execute just and process the rest below wxString ddeServer, ddeTopic, ddeCommand; static const size_t lenDdePrefix = 7; // strlen("WX_DDE:") - if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") ) + if ( cmd.Left(lenDdePrefix) == wxT("WX_DDE#") ) { // speed up the concatenations below ddeServer.reserve(256); @@ -633,7 +644,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) ddeCommand.reserve(256); const wxChar *p = cmd.c_str() + 7; - while ( *p && *p != _T('#') ) + while ( *p && *p != wxT('#') ) { command += *p++; } @@ -645,10 +656,10 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } else { - wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } - while ( *p && *p != _T('#') ) + while ( *p && *p != wxT('#') ) { ddeServer += *p++; } @@ -660,10 +671,10 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } else { - wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } - while ( *p && *p != _T('#') ) + while ( *p && *p != wxT('#') ) { ddeTopic += *p++; } @@ -675,7 +686,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } else { - wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute")); + wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute")); } while ( *p ) @@ -772,7 +783,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) DUPLICATE_SAME_ACCESS // same access as for src handle ) ) { - wxLogLastError(_T("DuplicateHandle")); + wxLogLastError(wxT("DuplicateHandle")); } ::CloseHandle(pipeInWrite); @@ -790,6 +801,55 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) wxString arguments = command.AfterFirst(wxT(' ')); #endif + wxWxCharBuffer envBuffer; + bool useCwd = false; + if ( env ) + { + useCwd = !env->cwd.empty(); + + // Translate environment variable map into NUL-terminated list of + // NUL-terminated strings. + if ( !env->env.empty() ) + { +#if wxUSE_UNICODE + // Environment variables can contain non-ASCII characters. We could + // check for it and not use this flag if everything is really ASCII + // only but there doesn't seem to be any reason to do it so just + // assume Unicode by default. + dwFlags |= CREATE_UNICODE_ENVIRONMENT; +#endif // wxUSE_UNICODE + + wxEnvVariableHashMap::const_iterator it; + + size_t envSz = 1; // ending '\0' + for ( it = env->env.begin(); it != env->env.end(); ++it ) + { + // Add size of env variable name and value, and '=' char and + // ending '\0' + envSz += it->first.length() + it->second.length() + 2; + } + + envBuffer.extend(envSz); + + wxChar *p = envBuffer.data(); + for ( it = env->env.begin(); it != env->env.end(); ++it ) + { + const wxString line = it->first + wxS("=") + it->second; + + // Include the trailing NUL which will always terminate the + // buffer returned by t_str(). + const size_t len = line.length() + 1; + + wxTmemcpy(p, line.t_str(), len); + + p += len; + } + + // And another NUL to terminate the list of NUL-terminated strings. + *p = 0; + } + } + bool ok = ::CreateProcess ( // WinCE requires appname to be non null @@ -808,8 +868,10 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) 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) + envBuffer.data(), // environment (may be NULL which is fine) + useCwd // initial working directory + ? const_cast(env->cwd.wx_str()) + : NULL, // (or use the same) &si, // startup info (unused here) &pi // process info ) != 0; @@ -890,6 +952,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) { // may be NULL or not data->handler = handler; + + if (handler) + handler->SetPid(pi.dwProcessId); } DWORD tid; @@ -910,7 +975,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) // close unneeded handle if ( !::CloseHandle(pi.hThread) ) + { wxLogLastError(wxT("CloseHandle(hThread)")); + } if ( !hThread ) { @@ -940,14 +1007,14 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) ) { default: - wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") ); + wxFAIL_MSG( wxT("unexpected WaitForInputIdle() return code") ); // fall through - case -1: - wxLogLastError(_T("WaitForInputIdle() in wxExecute")); + case WAIT_FAILED: + wxLogLastError(wxT("WaitForInputIdle() in wxExecute")); case WAIT_TIMEOUT: - wxLogDebug(_T("Timeout too small in WaitForInputIdle")); + wxLogDebug(wxT("Timeout too small in WaitForInputIdle")); ok = false; break; @@ -959,7 +1026,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) if ( !ok ) { - wxLogDebug(_T("Failed to send DDE request to the process \"%s\"."), + wxLogDebug(wxT("Failed to send DDE request to the process \"%s\"."), cmd.c_str()); } } @@ -974,7 +1041,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; - wxCHECK_MSG( traits, -1, _T("no wxAppTraits in wxExecute()?") ); + wxCHECK_MSG( traits, -1, wxT("no wxAppTraits in wxExecute()?") ); void *cookie = NULL; if ( !(flags & wxEXEC_NODISABLE) ) @@ -987,16 +1054,27 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) while ( data->state ) { #if wxUSE_STREAMS && !defined(__WXWINCE__) - bufOut.Update(); - bufErr.Update(); + if ( !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); + } - // 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); + // we must always process messages for our hidden window or we'd never + // get wxWM_PROC_TERMINATED and so this loop would never terminate + MSG msg; + ::PeekMessage(&msg, data->hWnd, 0, 0, PM_REMOVE); - // we must process messages or we'd never get wxWM_PROC_TERMINATED - traits->AlwaysYield(); + // we may also need to process messages for all the other application + // windows + if ( !(flags & wxEXEC_NOEVENTS) ) + { + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); + if ( loop ) + loop->Yield(); + } } if ( !(flags & wxEXEC_NODISABLE) ) @@ -1012,18 +1090,62 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) return dwExitCode; } -long wxExecute(wxChar **argv, int flags, wxProcess *handler) +template +long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { wxString command; + command.reserve(1024); + wxString arg; for ( ;; ) { - command += *argv++; + arg = *argv++; + + bool quote; + if ( arg.empty() ) + { + // we need to quote empty arguments, otherwise they'd just + // disappear + quote = true; + } + else // non-empty + { + // escape any quotes present in the string to avoid interfering + // with the command line parsing in the child process + arg.Replace("\"", "\\\"", true /* replace all */); + + // and quote any arguments containing the spaces to prevent them from + // being broken down + quote = arg.find_first_of(" \t") != wxString::npos; + } + + if ( quote ) + command += '\"' + arg + '\"'; + else + command += arg; + if ( !*argv ) break; - command += _T(' '); + command += ' '; } - return wxExecute(command, flags, handler); + return wxExecute(command, flags, handler, env); +} + +long wxExecute(char **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) +{ + return wxExecuteImpl(argv, flags, handler, env); } + +#if wxUSE_UNICODE + +long wxExecute(wchar_t **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) +{ + return wxExecuteImpl(argv, flags, handler, env); +} + +#endif // wxUSE_UNICODE