// Define an array of process pointers used by MyFrame
class MyPipedProcess;
-WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyProcessesArray);
+WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
+
+class MyProcess;
+WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
- // ctor(s)
+ // ctor and dtor
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
+ virtual ~MyFrame();
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
void OnProcessTerminated(MyPipedProcess *process);
wxListBox *GetLogListBox() const { return m_lbox; }
+ // for MyProcess
+ void OnAsyncTermination(MyProcess *process);
+
private:
void ShowOutput(const wxString& cmd,
const wxArrayString& output,
void DoAsyncExec(const wxString& cmd);
- void AddAsyncProcess(MyPipedProcess *process)
- {
- if ( m_running.IsEmpty() )
- {
- // we want to start getting the timer events to ensure that a
- // steady stream of idle events comes in -- otherwise we
- // wouldn't be able to poll the child process input
- m_timerIdleWakeUp.Start(100);
- }
- //else: the timer is already running
+ void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
- m_running.Add(process);
- }
-
- void RemoveAsyncProcess(MyPipedProcess *process)
- {
- m_running.Remove(process);
+ void AddPipedProcess(MyPipedProcess *process);
+ void RemovePipedProcess(MyPipedProcess *process);
- if ( m_running.IsEmpty() )
- {
- // we don't need to get idle events all the time any more
- m_timerIdleWakeUp.Stop();
- }
- }
// the PID of the last process we launched asynchronously
long m_pidLast;
wxListBox *m_lbox;
- MyProcessesArray m_running;
+ // array of running processes with redirected IO
+ MyPipedProcessesArray m_running;
+
+ // array of all asynchrously running processes
+ MyProcessesArray m_allAsync;
// the idle event wake up timer
wxTimer m_timerIdleWakeUp;
#endif // wxUSE_STATUSBAR
}
+MyFrame::~MyFrame()
+{
+ // any processes left until now must be deleted manually: normally this is
+ // done when the associated process terminates but it must be still running
+ // if this didn't happen until now
+ for ( size_t n = 0; n < m_allAsync.size(); n++ )
+ {
+ delete m_allAsync[n];
+ }
+}
+
// ----------------------------------------------------------------------------
// event handlers: file and help menu
// ----------------------------------------------------------------------------
void MyFrame::DoAsyncExec(const wxString& cmd)
{
- wxProcess *process = new MyProcess(this, cmd);
+ MyProcess * const process = new MyProcess(this, cmd);
m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
if ( !m_pidLast )
{
- wxLogError( _T("Execution of '%s' failed."), cmd.c_str() );
+ wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
delete process;
}
else
{
- wxLogStatus( _T("Process %ld (%s) launched."),
- m_pidLast, cmd.c_str() );
+ wxLogStatus(_T("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
m_cmdLast = cmd;
+
+ // the parent frame keeps track of all async processes as it needs to
+ // free them if we exit before the child process terminates
+ AddAsyncProcess(process);
}
}
}
else
{
- AddAsyncProcess(process);
+ AddPipedProcess(process);
}
}
long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
if ( pid )
{
- wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() );
+ wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str());
- AddAsyncProcess(process);
+ AddPipedProcess(process);
}
else
{
void MyFrame::OnProcessTerminated(MyPipedProcess *process)
{
- RemoveAsyncProcess(process);
+ RemovePipedProcess(process);
+}
+
+void MyFrame::OnAsyncTermination(MyProcess *process)
+{
+ m_allAsync.Remove(process);
+
+ delete process;
+}
+
+void MyFrame::AddPipedProcess(MyPipedProcess *process)
+{
+ if ( m_running.IsEmpty() )
+ {
+ // we want to start getting the timer events to ensure that a
+ // steady stream of idle events comes in -- otherwise we
+ // wouldn't be able to poll the child process input
+ m_timerIdleWakeUp.Start(100);
+ }
+ //else: the timer is already running
+
+ m_running.Add(process);
+ m_allAsync.Add(process);
}
+void MyFrame::RemovePipedProcess(MyPipedProcess *process)
+{
+ m_running.Remove(process);
+
+ if ( m_running.IsEmpty() )
+ {
+ // we don't need to get idle events all the time any more
+ m_timerIdleWakeUp.Stop();
+ }
+}
void MyFrame::ShowOutput(const wxString& cmd,
const wxArrayString& output,
wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."),
pid, m_cmd.c_str(), status);
- // we're not needed any more
- delete this;
+ m_parent->OnAsyncTermination(this);
}
// ----------------------------------------------------------------------------
#include "wx/process.h"
#include "wx/thread.h"
#include "wx/apptrait.h"
+#include "wx/vector.h"
#include "wx/msw/private.h"
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 HANDLE gs_heventShutdown = NULL;
+
+// handles of all threads monitoring the execution of asynchronously running
+// processes
+static wxVector<HANDLE> gs_asyncThreads;
+
// ----------------------------------------------------------------------------
// private types
// ----------------------------------------------------------------------------
virtual bool OnInit() { return true; }
virtual void OnExit()
{
+ if ( gs_heventShutdown )
+ {
+ // stop any threads waiting for the termination of asynchronously
+ // running processes
+ if ( !::SetEvent(gs_heventShutdown) )
+ {
+ wxLogDebug(_T("Failed to set shutdown event in wxExecuteModule"));
+ }
+
+ ::CloseHandle(gs_heventShutdown);
+ gs_heventShutdown = NULL;
+
+ // 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(_T("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()) )
DECLARE_DYNAMIC_CLASS(wxExecuteModule)
};
+IMPLEMENT_DYNAMIC_CLASS(wxExecuteModule, wxModule)
+
#if wxUSE_STREAMS && !defined(__WXWINCE__)
// ----------------------------------------------------------------------------
{
wxExecuteData * const data = (wxExecuteData *)arg;
- if ( ::WaitForSingleObject(data->hProcess, INFINITE) != WAIT_OBJECT_0 )
+ // create the shutdown event if we're the first thread starting to wait
+ if ( !gs_heventShutdown )
{
- wxLogDebug(_T("Waiting for the process termination failed!"));
+ // create a manual initially non-signalled event object
+ gs_heventShutdown = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ if ( !gs_heventShutdown )
+ {
+ wxLogDebug(_T("CreateEvent() in wxExecuteThread failed"));
+ }
}
- // get the exit code
- if ( !::GetExitCodeProcess(data->hProcess, &data->dwExitCode) )
+ HANDLE handles[2] = { data->hProcess, gs_heventShutdown };
+ switch ( ::WaitForMultipleObjects(2, handles, FALSE, INFINITE) )
{
- wxLogLastError(wxT("GetExitCodeProcess"));
- }
+ 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") );
- 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;
- // send a message indicating process termination to the window
- ::SendMessage(data->hWnd, wxWM_PROC_TERMINATED, 0, (LPARAM)data);
+ default:
+ wxLogDebug(_T("Waiting for the process termination failed!"));
+ }
return 0;
}
{
// we're executing synchronously, tell the waiting thread
// that the process finished
- data->state = 0;
+ data->state = false;
}
else
{
return pi.dwProcessId;
}
- ::CloseHandle(hThread);
+ gs_asyncThreads.push_back(hThread);
#if wxUSE_IPC && !defined(__WXWINCE__)
// second part of DDE hack: now establish the DDE conversation with the