Windows CE doesn't seem to be supported by Microsoft any longer. Last CE release was in early 2013 and the PocketPC and Smartphone targets supported by wxWidgets are long gone. The build files where already removed in an earlier cleanup this commit removes all files, every #ifdef and all documentation regarding the Windows CE support. Closes https://github.com/wxWidgets/wxWidgets/pull/81
1129 lines
33 KiB
C++
1129 lines
33 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/utilsexc.cpp
|
|
// Purpose: wxExecute implementation for MSW
|
|
// Author: Julian Smart
|
|
// Modified by:
|
|
// Created: 04/01/98
|
|
// Copyright: (c) 1998-2002 wxWidgets dev team
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/utils.h"
|
|
#include "wx/app.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#if wxUSE_STREAMS
|
|
#include "wx/stream.h"
|
|
#endif
|
|
#include "wx/module.h"
|
|
#endif
|
|
|
|
#include "wx/process.h"
|
|
#include "wx/thread.h"
|
|
#include "wx/apptrait.h"
|
|
#include "wx/evtloop.h"
|
|
#include "wx/vector.h"
|
|
|
|
|
|
#include "wx/msw/private.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#if !defined(__GNUWIN32__)
|
|
#include <direct.h>
|
|
#include <dos.h>
|
|
#endif
|
|
|
|
#if defined(__GNUWIN32__)
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifndef __UNIX__
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#ifndef __GNUWIN32__
|
|
#include <shellapi.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
|
|
#if wxUSE_IPC
|
|
#include "wx/dde.h" // for WX_DDE hack in wxExecute
|
|
#endif // wxUSE_IPC
|
|
|
|
#include "wx/msw/private/hiddenwin.h"
|
|
#include "wx/msw/private/event.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// this message is sent when the process we're waiting for terminates
|
|
#define wxWM_PROC_TERMINATED (WM_USER + 10000)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// this module globals
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// we need to create a hidden window to receive the process termination
|
|
// notifications and for this we need a (Win) class name for it which we will
|
|
// register the first time it's needed
|
|
static const wxChar *wxMSWEXEC_WNDCLASSNAME = wxT("_wxExecute_Internal_Class");
|
|
static const wxChar *gs_classForHiddenWindow = NULL;
|
|
|
|
// event used to wake up threads waiting in wxExecuteThread
|
|
static wxWinAPI::Event gs_heventShutdown;
|
|
|
|
// handles of all threads monitoring the execution of asynchronously running
|
|
// processes
|
|
static wxVector<HANDLE> gs_asyncThreads;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// private types
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// structure describing the process we're being waiting for
|
|
struct wxExecuteData
|
|
{
|
|
public:
|
|
wxExecuteData()
|
|
{
|
|
// The rest is initialized in the code creating the objects of this
|
|
// class, but the thread handle can't be set until later, so initialize
|
|
// it here to ensure we never use an uninitialized value in our dtor.
|
|
hThread = 0;
|
|
}
|
|
|
|
~wxExecuteData()
|
|
{
|
|
if ( !::CloseHandle(hProcess) )
|
|
{
|
|
wxLogLastError(wxT("CloseHandle(hProcess)"));
|
|
}
|
|
}
|
|
|
|
HWND hWnd; // window to send wxWM_PROC_TERMINATED to
|
|
HANDLE hProcess; // handle of the process
|
|
HANDLE hThread; // handle of the thread monitoring its termination
|
|
DWORD dwProcessId; // pid of the process
|
|
wxProcess *handler;
|
|
DWORD dwExitCode; // the exit code of the process
|
|
bool state; // set to false when the process finishes
|
|
};
|
|
|
|
class wxExecuteModule : public wxModule
|
|
{
|
|
public:
|
|
virtual bool OnInit() { return true; }
|
|
virtual void OnExit()
|
|
{
|
|
if ( gs_heventShutdown.IsOk() )
|
|
{
|
|
// stop any threads waiting for the termination of asynchronously
|
|
// running processes
|
|
if ( !gs_heventShutdown.Set() )
|
|
{
|
|
wxLogDebug(wxT("Failed to set shutdown event in wxExecuteModule"));
|
|
}
|
|
|
|
gs_heventShutdown.Close();
|
|
|
|
// now wait until they terminate
|
|
if ( !gs_asyncThreads.empty() )
|
|
{
|
|
const size_t numThreads = gs_asyncThreads.size();
|
|
|
|
if ( ::WaitForMultipleObjects
|
|
(
|
|
numThreads,
|
|
&gs_asyncThreads[0],
|
|
TRUE, // wait for all of them to become signalled
|
|
3000 // long but finite value
|
|
) == WAIT_TIMEOUT )
|
|
{
|
|
wxLogDebug(wxT("Failed to stop all wxExecute monitor threads"));
|
|
}
|
|
|
|
for ( size_t n = 0; n < numThreads; n++ )
|
|
{
|
|
::CloseHandle(gs_asyncThreads[n]);
|
|
}
|
|
|
|
gs_asyncThreads.clear();
|
|
}
|
|
}
|
|
|
|
if ( gs_classForHiddenWindow )
|
|
{
|
|
if ( !::UnregisterClass(wxMSWEXEC_WNDCLASSNAME, wxGetInstance()) )
|
|
{
|
|
wxLogLastError(wxT("UnregisterClass(wxExecClass)"));
|
|
}
|
|
|
|
gs_classForHiddenWindow = NULL;
|
|
}
|
|
}
|
|
|
|
private:
|
|
wxDECLARE_DYNAMIC_CLASS(wxExecuteModule);
|
|
};
|
|
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxExecuteModule, wxModule);
|
|
|
|
#if wxUSE_STREAMS
|
|
|
|
#include "wx/private/pipestream.h"
|
|
#include "wx/private/streamtempinput.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPipe represents a Win32 anonymous pipe
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxPipe
|
|
{
|
|
public:
|
|
// the symbolic names for the pipe ends
|
|
enum Direction
|
|
{
|
|
Read,
|
|
Write
|
|
};
|
|
|
|
// default ctor doesn't do anything
|
|
wxPipe() { m_handles[Read] = m_handles[Write] = INVALID_HANDLE_VALUE; }
|
|
|
|
// create the pipe, return true if ok, false on error
|
|
bool Create()
|
|
{
|
|
// default secutiry attributes
|
|
SECURITY_ATTRIBUTES security;
|
|
|
|
security.nLength = sizeof(security);
|
|
security.lpSecurityDescriptor = NULL;
|
|
security.bInheritHandle = TRUE; // to pass it to the child
|
|
|
|
if ( !::CreatePipe(&m_handles[0], &m_handles[1], &security, 0) )
|
|
{
|
|
wxLogSysError(_("Failed to create an anonymous pipe"));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// return true if we were created successfully
|
|
bool IsOk() const { return m_handles[Read] != INVALID_HANDLE_VALUE; }
|
|
|
|
// return the descriptor for one of the pipe ends
|
|
HANDLE operator[](Direction which) const { return m_handles[which]; }
|
|
|
|
// detach a descriptor, meaning that the pipe dtor won't close it, and
|
|
// return it
|
|
HANDLE Detach(Direction which)
|
|
{
|
|
HANDLE handle = m_handles[which];
|
|
m_handles[which] = INVALID_HANDLE_VALUE;
|
|
|
|
return handle;
|
|
}
|
|
|
|
// close the pipe descriptors
|
|
void Close()
|
|
{
|
|
for ( size_t n = 0; n < WXSIZEOF(m_handles); n++ )
|
|
{
|
|
if ( m_handles[n] != INVALID_HANDLE_VALUE )
|
|
{
|
|
::CloseHandle(m_handles[n]);
|
|
m_handles[n] = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// dtor closes the pipe descriptors
|
|
~wxPipe() { Close(); }
|
|
|
|
private:
|
|
HANDLE m_handles[2];
|
|
};
|
|
|
|
#endif // wxUSE_STREAMS
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// process termination detecting support
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// thread function for the thread monitoring the process termination
|
|
static DWORD __stdcall wxExecuteThread(void *arg)
|
|
{
|
|
wxExecuteData * const data = (wxExecuteData *)arg;
|
|
|
|
// create the shutdown event if we're the first thread starting to wait
|
|
if ( !gs_heventShutdown.IsOk() )
|
|
{
|
|
// create a manual initially non-signalled event object
|
|
if ( !gs_heventShutdown.Create(wxWinAPI::Event::ManualReset, wxWinAPI::Event::Nonsignaled) )
|
|
{
|
|
wxLogDebug(wxT("CreateEvent() in wxExecuteThread failed"));
|
|
}
|
|
}
|
|
|
|
HANDLE handles[2] = { data->hProcess, gs_heventShutdown };
|
|
switch ( ::WaitForMultipleObjects(2, handles, FALSE, INFINITE) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
// process terminated, get its exit code
|
|
if ( !::GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
|
|
{
|
|
wxLogLastError(wxT("GetExitCodeProcess"));
|
|
}
|
|
|
|
wxASSERT_MSG( data->dwExitCode != STILL_ACTIVE,
|
|
wxT("process should have terminated") );
|
|
|
|
// send a message indicating process termination to the window
|
|
::SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
// we're shutting down but the process is still running -- leave it
|
|
// run but clean up the associated data
|
|
if ( !data->state )
|
|
{
|
|
delete data;
|
|
}
|
|
//else: exiting while synchronously executing process is still
|
|
// running? this shouldn't happen...
|
|
break;
|
|
|
|
default:
|
|
wxLogDebug(wxT("Waiting for the process termination failed!"));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// window procedure of a hidden window which is created just to receive
|
|
// the notification message when a process exits
|
|
LRESULT APIENTRY
|
|
wxExecuteWindowCbk(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if ( message == wxWM_PROC_TERMINATED )
|
|
{
|
|
DestroyWindow(hWnd); // we don't need it any more
|
|
|
|
wxExecuteData * const data = (wxExecuteData *)lParam;
|
|
if ( data->handler )
|
|
{
|
|
data->handler->OnTerminate((int)data->dwProcessId,
|
|
(int)data->dwExitCode);
|
|
}
|
|
|
|
if ( data->state )
|
|
{
|
|
// we're executing synchronously, tell the waiting thread
|
|
// that the process finished
|
|
data->state = false;
|
|
}
|
|
else
|
|
{
|
|
// asynchronous execution - we should do the clean up
|
|
for ( wxVector<HANDLE>::iterator it = gs_asyncThreads.begin();
|
|
it != gs_asyncThreads.end();
|
|
++it )
|
|
{
|
|
if ( *it == data->hThread )
|
|
{
|
|
gs_asyncThreads.erase(it);
|
|
if ( !::CloseHandle(data->hThread) )
|
|
{
|
|
wxLogLastError(wxT("CloseHandle(hThread)"));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete data;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return ::DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// implementation of IO redirection support classes
|
|
// ============================================================================
|
|
|
|
#if wxUSE_STREAMS
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPipeInputStreams
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxPipeInputStream::wxPipeInputStream(HANDLE hInput)
|
|
{
|
|
m_hInput = hInput;
|
|
}
|
|
|
|
wxPipeInputStream::~wxPipeInputStream()
|
|
{
|
|
if ( m_hInput != INVALID_HANDLE_VALUE )
|
|
::CloseHandle(m_hInput);
|
|
}
|
|
|
|
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;
|
|
|
|
// 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(wxT("PeekNamedPipe"));
|
|
}
|
|
|
|
// don't try to continue reading from a pipe if an error occurred or if
|
|
// it had been closed
|
|
::CloseHandle(m_hInput);
|
|
|
|
self->m_hInput = INVALID_HANDLE_VALUE;
|
|
self->m_lasterror = wxSTREAM_EOF;
|
|
|
|
nAvailable = 0;
|
|
}
|
|
|
|
return nAvailable != 0;
|
|
}
|
|
|
|
size_t wxPipeInputStream::OnSysRead(void *buffer, size_t len)
|
|
{
|
|
if ( !IsOpened() )
|
|
{
|
|
m_lasterror = wxSTREAM_EOF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD bytesRead;
|
|
if ( !::ReadFile(m_hInput, buffer, len, &bytesRead, NULL) )
|
|
{
|
|
m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
|
|
? wxSTREAM_EOF
|
|
: wxSTREAM_READ_ERROR;
|
|
}
|
|
|
|
// bytesRead is set to 0, as desired, if an error occurred
|
|
return bytesRead;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPipeOutputStream
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxPipeOutputStream::wxPipeOutputStream(HANDLE hOutput)
|
|
{
|
|
m_hOutput = hOutput;
|
|
|
|
// unblock the pipe to prevent deadlocks when we're writing to the pipe
|
|
// from which the child process can't read because it is writing in its own
|
|
// end of it
|
|
DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
|
|
if ( !::SetNamedPipeHandleState
|
|
(
|
|
m_hOutput,
|
|
&mode,
|
|
NULL, // collection count (we don't set it)
|
|
NULL // timeout (we don't set it neither)
|
|
) )
|
|
{
|
|
wxLogLastError(wxT("SetNamedPipeHandleState(PIPE_NOWAIT)"));
|
|
}
|
|
}
|
|
|
|
bool wxPipeOutputStream::Close()
|
|
{
|
|
return ::CloseHandle(m_hOutput) != 0;
|
|
}
|
|
|
|
|
|
size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t len)
|
|
{
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
DWORD totalWritten = 0;
|
|
while ( len > 0 )
|
|
{
|
|
DWORD chunkWritten;
|
|
if ( !::WriteFile(m_hOutput, buffer, len, &chunkWritten, NULL) )
|
|
{
|
|
m_lasterror = ::GetLastError() == ERROR_BROKEN_PIPE
|
|
? wxSTREAM_EOF
|
|
: wxSTREAM_WRITE_ERROR;
|
|
break;
|
|
}
|
|
|
|
if ( !chunkWritten )
|
|
break;
|
|
|
|
buffer = (char *)buffer + chunkWritten;
|
|
totalWritten += chunkWritten;
|
|
len -= chunkWritten;
|
|
}
|
|
|
|
return totalWritten;
|
|
}
|
|
|
|
#endif // wxUSE_STREAMS
|
|
|
|
// ============================================================================
|
|
// wxExecute functions family
|
|
// ============================================================================
|
|
|
|
#if wxUSE_IPC
|
|
|
|
// connect to the given server via DDE and ask it to execute the command
|
|
bool
|
|
wxExecuteDDE(const wxString& ddeServer,
|
|
const wxString& ddeTopic,
|
|
const wxString& ddeCommand)
|
|
{
|
|
bool ok wxDUMMY_INITIALIZE(false);
|
|
|
|
wxDDEClient client;
|
|
wxConnectionBase *
|
|
conn = client.MakeConnection(wxEmptyString, ddeServer, ddeTopic);
|
|
if ( !conn )
|
|
{
|
|
ok = false;
|
|
}
|
|
else // connected to DDE server
|
|
{
|
|
// the added complication here is that although most programs use
|
|
// XTYP_EXECUTE for their DDE API, some important ones -- like Word
|
|
// and other MS stuff - use XTYP_REQUEST!
|
|
//
|
|
// moreover, anotheri mportant program (IE) understands both but
|
|
// returns an error from Execute() so we must try Request() first
|
|
// to avoid doing it twice
|
|
{
|
|
// we're prepared for this one to fail, so don't show errors
|
|
wxLogNull noErrors;
|
|
|
|
ok = conn->Request(ddeCommand) != NULL;
|
|
}
|
|
|
|
if ( !ok )
|
|
{
|
|
// now try execute -- but show the errors
|
|
ok = conn->Execute(ddeCommand);
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
#endif // wxUSE_IPC
|
|
|
|
long wxExecute(const wxString& cmd, int flags, wxProcess *handler,
|
|
const wxExecuteEnv *env)
|
|
{
|
|
wxCHECK_MSG( !cmd.empty(), 0, wxT("empty command in wxExecute") );
|
|
|
|
#if wxUSE_THREADS
|
|
// for many reasons, the code below breaks down if it's called from another
|
|
// 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(),
|
|
wxT("wxExecute() can be called only from the main thread") );
|
|
#endif // wxUSE_THREADS
|
|
|
|
wxString command;
|
|
|
|
#if wxUSE_IPC
|
|
// DDE hack: this is really not pretty, but we need to allow this for
|
|
// transparent handling of DDE servers in wxMimeTypesManager. Usually it
|
|
// returns the command which should be run to view/open/... a file of the
|
|
// given type. Sometimes, however, this command just launches the server
|
|
// and an additional DDE request must be made to really open the file. To
|
|
// keep all this well hidden from the application, we allow a special form
|
|
// of command: WX_DDE#<command>#DDE_SERVER#DDE_TOPIC#DDE_COMMAND in which
|
|
// 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) == wxT("WX_DDE#") )
|
|
{
|
|
// speed up the concatenations below
|
|
ddeServer.reserve(256);
|
|
ddeTopic.reserve(256);
|
|
ddeCommand.reserve(256);
|
|
|
|
const wxChar *p = cmd.c_str() + 7;
|
|
while ( *p && *p != wxT('#') )
|
|
{
|
|
command += *p++;
|
|
}
|
|
|
|
if ( *p )
|
|
{
|
|
// skip '#'
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute"));
|
|
}
|
|
|
|
while ( *p && *p != wxT('#') )
|
|
{
|
|
ddeServer += *p++;
|
|
}
|
|
|
|
if ( *p )
|
|
{
|
|
// skip '#'
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute"));
|
|
}
|
|
|
|
while ( *p && *p != wxT('#') )
|
|
{
|
|
ddeTopic += *p++;
|
|
}
|
|
|
|
if ( *p )
|
|
{
|
|
// skip '#'
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
wxFAIL_MSG(wxT("invalid WX_DDE command in wxExecute"));
|
|
}
|
|
|
|
while ( *p )
|
|
{
|
|
ddeCommand += *p++;
|
|
}
|
|
|
|
// if we want to just launch the program and not wait for its
|
|
// termination, try to execute DDE command right now, it can succeed if
|
|
// the process is already running - but as it fails if it's not
|
|
// running, suppress any errors it might generate
|
|
if ( !(flags & wxEXEC_SYNC) )
|
|
{
|
|
wxLogNull noErrors;
|
|
if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
|
|
{
|
|
// a dummy PID - this is a hack, of course, but it's well worth
|
|
// it as we don't open a new server each time we're called
|
|
// which would be quite bad
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif // wxUSE_IPC
|
|
{
|
|
// no DDE
|
|
command = cmd;
|
|
}
|
|
|
|
// the IO redirection is only supported with wxUSE_STREAMS
|
|
BOOL redirect = FALSE;
|
|
|
|
#if wxUSE_STREAMS
|
|
wxPipe pipeIn, pipeOut, pipeErr;
|
|
|
|
// open the pipes to which child process IO will be redirected if needed
|
|
if ( handler && handler->IsRedirected() )
|
|
{
|
|
// create pipes for redirecting stdin, stdout and stderr
|
|
if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
|
|
{
|
|
wxLogSysError(_("Failed to redirect the child process IO"));
|
|
|
|
// indicate failure: we need to return different error code
|
|
// depending on the sync flag
|
|
return flags & wxEXEC_SYNC ? -1 : 0;
|
|
}
|
|
|
|
redirect = TRUE;
|
|
}
|
|
#endif // wxUSE_STREAMS
|
|
|
|
// create the process
|
|
STARTUPINFO si;
|
|
wxZeroMemory(si);
|
|
si.cb = sizeof(si);
|
|
|
|
#if wxUSE_STREAMS
|
|
if ( redirect )
|
|
{
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
si.hStdInput = pipeIn[wxPipe::Read];
|
|
si.hStdOutput = pipeOut[wxPipe::Write];
|
|
si.hStdError = pipeErr[wxPipe::Write];
|
|
|
|
// We must set the handles to those sides of std* pipes that we won't
|
|
// in the child to be non-inheritable. We must do this before launching
|
|
// the child process as otherwise these handles will be inherited by
|
|
// the child which will never close them and so the pipe will not
|
|
// return ERROR_BROKEN_PIPE if the parent or child exits unexpectedly
|
|
// causing the remaining process to potentially become deadlocked in
|
|
// ReadFile() or WriteFile().
|
|
if ( !::SetHandleInformation(pipeIn[wxPipe::Write],
|
|
HANDLE_FLAG_INHERIT, 0) )
|
|
wxLogLastError(wxT("SetHandleInformation(pipeIn)"));
|
|
|
|
if ( !::SetHandleInformation(pipeOut[wxPipe::Read],
|
|
HANDLE_FLAG_INHERIT, 0) )
|
|
wxLogLastError(wxT("SetHandleInformation(pipeOut)"));
|
|
|
|
if ( !::SetHandleInformation(pipeErr[wxPipe::Read],
|
|
HANDLE_FLAG_INHERIT, 0) )
|
|
wxLogLastError(wxT("SetHandleInformation(pipeErr)"));
|
|
}
|
|
#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;
|
|
|
|
if ( (flags & wxEXEC_MAKE_GROUP_LEADER) )
|
|
dwFlags |= CREATE_NEW_PROCESS_GROUP;
|
|
|
|
dwFlags |= CREATE_DEFAULT_ERROR_MODE ;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Translate wxWidgets priority to Windows conventions.
|
|
if ( handler )
|
|
{
|
|
unsigned prio = handler->GetPriority();
|
|
if ( prio <= 20 )
|
|
dwFlags |= IDLE_PRIORITY_CLASS;
|
|
else if ( prio <= 40 )
|
|
dwFlags |= BELOW_NORMAL_PRIORITY_CLASS;
|
|
else if ( prio <= 60 )
|
|
dwFlags |= NORMAL_PRIORITY_CLASS;
|
|
else if ( prio <= 80 )
|
|
dwFlags |= ABOVE_NORMAL_PRIORITY_CLASS;
|
|
else if ( prio <= 99 )
|
|
dwFlags |= HIGH_PRIORITY_CLASS;
|
|
else if ( prio <= 100 )
|
|
dwFlags |= REALTIME_PRIORITY_CLASS;
|
|
else
|
|
{
|
|
wxFAIL_MSG(wxT("invalid value of thread priority parameter"));
|
|
dwFlags |= NORMAL_PRIORITY_CLASS;
|
|
}
|
|
}
|
|
|
|
bool ok = ::CreateProcess
|
|
(
|
|
NULL, // application name (use only cmd line)
|
|
wxMSW_CONV_LPTSTR(command), // 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
|
|
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;
|
|
|
|
#if wxUSE_STREAMS
|
|
// we can close the pipe ends used by child anyhow
|
|
if ( redirect )
|
|
{
|
|
::CloseHandle(pipeIn.Detach(wxPipe::Read));
|
|
::CloseHandle(pipeOut.Detach(wxPipe::Write));
|
|
::CloseHandle(pipeErr.Detach(wxPipe::Write));
|
|
}
|
|
#endif // wxUSE_STREAMS
|
|
|
|
if ( !ok )
|
|
{
|
|
#if wxUSE_STREAMS
|
|
// close the other handles too
|
|
if ( redirect )
|
|
{
|
|
::CloseHandle(pipeIn.Detach(wxPipe::Write));
|
|
::CloseHandle(pipeOut.Detach(wxPipe::Read));
|
|
::CloseHandle(pipeErr.Detach(wxPipe::Read));
|
|
}
|
|
#endif // wxUSE_STREAMS
|
|
|
|
wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
|
|
|
|
return flags & wxEXEC_SYNC ? -1 : 0;
|
|
}
|
|
|
|
#if wxUSE_STREAMS
|
|
// the input buffer bufOut is connected to stdout, this is why it is
|
|
// called bufOut and not bufIn
|
|
wxStreamTempInputBuffer bufOut,
|
|
bufErr;
|
|
|
|
if ( redirect )
|
|
{
|
|
// We can now initialize the wxStreams
|
|
wxPipeInputStream *
|
|
outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
|
|
wxPipeInputStream *
|
|
errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
|
|
wxPipeOutputStream *
|
|
inStream = new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write));
|
|
|
|
handler->SetPipeStreams(outStream, inStream, errStream);
|
|
|
|
bufOut.Init(outStream);
|
|
bufErr.Init(errStream);
|
|
}
|
|
#endif // wxUSE_STREAMS
|
|
|
|
// create a hidden window to receive notification about process
|
|
// termination
|
|
HWND hwnd = wxCreateHiddenWindow
|
|
(
|
|
&gs_classForHiddenWindow,
|
|
wxMSWEXEC_WNDCLASSNAME,
|
|
(WNDPROC)wxExecuteWindowCbk
|
|
);
|
|
|
|
wxASSERT_MSG( hwnd, wxT("can't create a hidden window for wxExecute") );
|
|
|
|
// Alloc data
|
|
wxExecuteData *data = new wxExecuteData;
|
|
data->hProcess = pi.hProcess;
|
|
data->dwProcessId = pi.dwProcessId;
|
|
data->hWnd = hwnd;
|
|
data->state = (flags & wxEXEC_SYNC) != 0;
|
|
if ( flags & wxEXEC_SYNC )
|
|
{
|
|
// handler may be !NULL for capturing program output, but we don't use
|
|
// it wxExecuteData struct in this case
|
|
data->handler = NULL;
|
|
}
|
|
else
|
|
{
|
|
// may be NULL or not
|
|
data->handler = handler;
|
|
|
|
if (handler)
|
|
handler->SetPid(pi.dwProcessId);
|
|
}
|
|
|
|
DWORD tid;
|
|
HANDLE hThread = ::CreateThread(NULL,
|
|
0,
|
|
wxExecuteThread,
|
|
(void *)data,
|
|
0,
|
|
&tid);
|
|
|
|
// resume process we created now - whether the thread creation succeeded or
|
|
// not
|
|
if ( ::ResumeThread(pi.hThread) == (DWORD)-1 )
|
|
{
|
|
// ignore it - what can we do?
|
|
wxLogLastError(wxT("ResumeThread in wxExecute"));
|
|
}
|
|
|
|
// close unneeded handle
|
|
if ( !::CloseHandle(pi.hThread) )
|
|
{
|
|
wxLogLastError(wxT("CloseHandle(hThread)"));
|
|
}
|
|
|
|
if ( !hThread )
|
|
{
|
|
wxLogLastError(wxT("CreateThread in wxExecute"));
|
|
|
|
DestroyWindow(hwnd);
|
|
delete data;
|
|
|
|
// the process still started up successfully...
|
|
return pi.dwProcessId;
|
|
}
|
|
|
|
gs_asyncThreads.push_back(hThread);
|
|
data->hThread = hThread;
|
|
|
|
#if wxUSE_IPC
|
|
// second part of DDE hack: now establish the DDE conversation with the
|
|
// just launched process
|
|
if ( !ddeServer.empty() )
|
|
{
|
|
bool ddeOK;
|
|
|
|
// give the process the time to init itself
|
|
//
|
|
// we use a very big timeout hoping that WaitForInputIdle() will return
|
|
// much sooner, but not INFINITE just in case the process hangs
|
|
// completely - like this we will regain control sooner or later
|
|
switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
|
|
{
|
|
default:
|
|
wxFAIL_MSG( wxT("unexpected WaitForInputIdle() return code") );
|
|
// fall through
|
|
|
|
case WAIT_FAILED:
|
|
wxLogLastError(wxT("WaitForInputIdle() in wxExecute"));
|
|
|
|
case WAIT_TIMEOUT:
|
|
wxLogDebug(wxT("Timeout too small in WaitForInputIdle"));
|
|
|
|
ddeOK = false;
|
|
break;
|
|
|
|
case 0:
|
|
// ok, process ready to accept DDE requests
|
|
ddeOK = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
|
|
}
|
|
|
|
if ( !ddeOK )
|
|
{
|
|
wxLogDebug(wxT("Failed to send DDE request to the process \"%s\"."),
|
|
cmd.c_str());
|
|
}
|
|
}
|
|
#endif // wxUSE_IPC
|
|
|
|
if ( !(flags & wxEXEC_SYNC) )
|
|
{
|
|
// clean up will be done when the process terminates
|
|
|
|
// return the pid
|
|
return pi.dwProcessId;
|
|
}
|
|
|
|
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
|
|
wxCHECK_MSG( traits, -1, wxT("no wxAppTraits in wxExecute()?") );
|
|
|
|
void *cookie = NULL;
|
|
if ( !(flags & wxEXEC_NODISABLE) )
|
|
{
|
|
// disable all app windows while waiting for the child process to finish
|
|
cookie = traits->BeforeChildWaitLoop();
|
|
}
|
|
|
|
// wait until the child process terminates
|
|
while ( data->state )
|
|
{
|
|
#if wxUSE_STREAMS
|
|
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);
|
|
}
|
|
|
|
// 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) )
|
|
{
|
|
// reenable disabled windows back
|
|
traits->AfterChildWaitLoop(cookie);
|
|
}
|
|
|
|
DWORD dwExitCode = data->dwExitCode;
|
|
delete data;
|
|
|
|
// return the exit code
|
|
return dwExitCode;
|
|
}
|
|
|
|
template <typename CharType>
|
|
long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler,
|
|
const wxExecuteEnv *env)
|
|
{
|
|
wxString command;
|
|
command.reserve(1024);
|
|
|
|
wxString arg;
|
|
for ( ;; )
|
|
{
|
|
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 += ' ';
|
|
}
|
|
|
|
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
|