#include "wx/process.h"
#include "wx/thread.h"
#include "wx/apptrait.h"
+#include "wx/evtloop.h"
#include "wx/vector.h"
#if !defined(__GNUWIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
#include <direct.h>
-#ifndef __MWERKS__
#include <dos.h>
#endif
-#endif
#if defined(__GNUWIN32__)
#include <sys/unistd.h>
#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
// 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);
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++ )
{
if ( !::UnregisterClass(wxMSWEXEC_WNDCLASSNAME, wxGetInstance()) )
{
- wxLogLastError(_T("UnregisterClass(wxExecClass)"));
+ wxLogLastError(wxT("UnregisterClass(wxExecClass)"));
}
gs_classForHiddenWindow = NULL;
protected:
HANDLE m_hInput;
- DECLARE_NO_COPY_CLASS(wxPipeInputStream)
+ wxDECLARE_NO_COPY_CLASS(wxPipeInputStream);
};
class wxPipeOutputStream: public wxOutputStream
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
gs_heventShutdown = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if ( !gs_heventShutdown )
{
- wxLogDebug(_T("CreateEvent() in wxExecuteThread failed"));
+ wxLogDebug(wxT("CreateEvent() in wxExecuteThread failed"));
}
}
break;
default:
- wxLogDebug(_T("Waiting for the process termination failed!"));
+ wxLogDebug(wxT("Waiting for the process termination failed!"));
}
return 0;
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;
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;
NULL // timeout (we don't set it neither)
) )
{
- wxLogLastError(_T("SetNamedPipeHandleState(PIPE_NOWAIT)"));
+ wxLogLastError(wxT("SetNamedPipeHandleState(PIPE_NOWAIT)"));
}
}
#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") );
// 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;
// case we execute just <command> 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);
ddeCommand.reserve(256);
const wxChar *p = cmd.c_str() + 7;
- while ( *p && *p != _T('#') )
+ while ( *p && *p != wxT('#') )
{
command += *p++;
}
}
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++;
}
}
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++;
}
}
else
{
- wxFAIL_MSG(_T("invalid WX_DDE command in wxExecute"));
+ wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute"));
}
while ( *p )
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
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
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
// Win32 allows for null
#ifdef __WXWINCE__
- (wxChar *)
- moduleName.wx_str(),// application name
- (wxChar *)
- arguments.wx_str(), // arguments
+ moduleName.t_str(), // application name
+ wxMSW_CONV_LPTSTR(arguments), // arguments
#else
NULL, // application name (use only cmd line)
- (wxChar *)
- command.wx_str(), // full command line
+ wxMSW_CONV_LPTSTR(command), // full command line
#endif
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)
+ envBuffer.data(), // environment (may be NULL which is fine)
+ useCwd // initial working directory
+ ? wxMSW_CONV_LPTSTR(env->cwd)
+ : NULL, // (or use the same)
&si, // startup info (unused here)
&pi // process info
) != 0;
{
// may be NULL or not
data->handler = handler;
+
+ if (handler)
+ handler->SetPid(pi.dwProcessId);
}
DWORD tid;
// close unneeded handle
if ( !::CloseHandle(pi.hThread) )
+ {
wxLogLastError(wxT("CloseHandle(hThread)"));
+ }
if ( !hThread )
{
// just launched process
if ( !ddeServer.empty() )
{
- bool ok;
+ bool ddeOK;
// give the process the time to init itself
//
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;
+ ddeOK = false;
break;
case 0:
// ok, process ready to accept DDE requests
- ok = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
+ ddeOK = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
}
- if ( !ok )
+ if ( !ddeOK )
{
- 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());
}
}
}
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) )
::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) )
return dwExitCode;
}
-long wxExecute(wxChar **argv, int flags, wxProcess *handler)
+template <typename CharType>
+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