X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ca5016d4ddae7f9a2d72a16326e9339f8d3e8748..e4a23857ec9c4f4e3f3e1251f566f55df31e4177:/src/msw/utilsexc.cpp diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp index aaf6176b1c..3fdf3b9aad 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" @@ -81,9 +82,7 @@ #include "wx/dde.h" // for WX_DDE hack in wxExecute #endif // wxUSE_IPC -// implemented in utils.cpp -extern "C" WXDLLIMPEXP_BASE HWND -wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc); +#include "wx/msw/private/hiddenwin.h" // ---------------------------------------------------------------------------- // constants @@ -145,7 +144,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 +163,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 +179,7 @@ public: { if ( !::UnregisterClass(wxMSWEXEC_WNDCLASSNAME, wxGetInstance()) ) { - wxLogLastError(_T("UnregisterClass(wxExecClass)")); + wxLogLastError(wxT("UnregisterClass(wxExecClass)")); } gs_classForHiddenWindow = NULL; @@ -335,7 +334,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 +367,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; @@ -464,7 +463,7 @@ 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 @@ -521,7 +520,7 @@ wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput) NULL // timeout (we don't set it neither) ) ) { - wxLogLastError(_T("SetNamedPipeHandleState(PIPE_NOWAIT)")); + wxLogLastError(wxT("SetNamedPipeHandleState(PIPE_NOWAIT)")); } } @@ -609,7 +608,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") ); @@ -618,7 +618,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; @@ -634,7 +634,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); @@ -642,7 +642,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++; } @@ -654,10 +654,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++; } @@ -669,10 +669,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++; } @@ -684,7 +684,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 ) @@ -755,15 +755,6 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) 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 - // specifying wxEXEC_NOHIDE flag - if ( !(flags & wxEXEC_NOHIDE) ) - { - si.dwFlags |= STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - } - // we must duplicate the handle to the write side of stdin pipe to make // it non inheritable: indeed, we must close the writing end of pipeIn // before launching the child process as otherwise this handle will be @@ -781,17 +772,32 @@ 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); } #endif // wxUSE_STREAMS + // The default logic for showing the console is to show it only if the IO + // is not redirected however wxEXEC_{SHOW,HIDE}_CONSOLE flags can be + // explicitly specified to change it. + if ( (flags & wxEXEC_HIDE_CONSOLE) || + (redirect && !(flags & wxEXEC_SHOW_CONSOLE)) ) + { + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + } + + PROCESS_INFORMATION pi; DWORD dwFlags = CREATE_SUSPENDED; #ifndef __WXWINCE__ + if ( (flags & wxEXEC_MAKE_GROUP_LEADER) && + (wxGetOsVersion() == wxOS_WINDOWS_NT) ) + dwFlags |= CREATE_NEW_PROCESS_GROUP; + dwFlags |= CREATE_DEFAULT_ERROR_MODE ; #else // we are assuming commands without spaces for now @@ -799,6 +805,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 @@ -817,8 +872,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; @@ -922,7 +979,9 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) // close unneeded handle if ( !::CloseHandle(pi.hThread) ) + { wxLogLastError(wxT("CloseHandle(hThread)")); + } if ( !hThread ) { @@ -952,14 +1011,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; @@ -971,7 +1030,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()); } } @@ -986,7 +1045,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) ) @@ -1007,8 +1066,19 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) ::Sleep(50); } - // we must process messages or we'd never get wxWM_PROC_TERMINATED - traits->AlwaysYield(); + // 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 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) ) @@ -1025,7 +1095,8 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } template -long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler) +long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { wxString command; command.reserve(1024); @@ -1064,19 +1135,21 @@ long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler) command += ' '; } - return wxExecute(command, flags, handler); + return wxExecute(command, flags, handler, env); } -long wxExecute(char **argv, int flags, wxProcess *handler) +long wxExecute(char **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { - return wxExecuteImpl(argv, flags, handler); + return wxExecuteImpl(argv, flags, handler, env); } #if wxUSE_UNICODE -long wxExecute(wchar_t **argv, int flags, wxProcess *handler) +long wxExecute(wchar_t **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { - return wxExecuteImpl(argv, flags, handler); + return wxExecuteImpl(argv, flags, handler, env); } #endif // wxUSE_UNICODE