\membersection{::wxKill}\label{wxkill}
-\func{int}{wxKill}{\param{long}{ pid}, \param{int}{ sig}}
+\func{int}{wxKill}{\param{long}{ pid}, \param{int}{ sig = wxSIGTERM}, \param{wxKillError }{*rc = NULL}}
-Under Unix (the only supported platform), equivalent to the Unix kill function.
-Returns 0 on success, -1 on failure.
+Equivalent to the Unix kill function: send the given signal {\it sig} to the
+process with PID {\it pid}. The valud signal values are
-Tip: sending a signal of 0 to a process returns -1 if the process does not exist.
-It does not raise a signal in the receiving process.
+\begin{verbatim}
+enum wxSignal
+{
+ wxSIGNONE = 0, // verify if the process exists under Unix
+ wxSIGHUP,
+ wxSIGINT,
+ wxSIGQUIT,
+ wxSIGILL,
+ wxSIGTRAP,
+ wxSIGABRT,
+ wxSIGEMT,
+ wxSIGFPE,
+ wxSIGKILL, // forcefully kill, dangerous!
+ wxSIGBUS,
+ wxSIGSEGV,
+ wxSIGSYS,
+ wxSIGPIPE,
+ wxSIGALRM,
+ wxSIGTERM // terminate the process gently
+};
+\end{verbatim}
+
+{\tt wxSIGNONE}, {\tt wxSIGKILL} and {\tt wxSIGTERM} have the same meaning
+under both Unix and Windows but all the other signals are equivalent to
+{\tt wxSIGTERM} under Windows.
+
+Returns 0 on success, -1 on failure. If {\it rc} parameter is not NULL, it will
+be filled with an element of {\tt wxKillError} enum:
+
+\begin{verbatim}
+enum wxKillError
+{
+ wxKILL_OK, // no error
+ wxKILL_BAD_SIGNAL, // no such signal
+ wxKILL_ACCESS_DENIED, // permission denied
+ wxKILL_NO_PROCESS, // no such process
+ wxKILL_ERROR // another, unspecified error
+};
+\end{verbatim}
+
+\wxheading{See also}
+
+\helpref{wxProcess::Kill}{wxprocesskill},\rtfsp
+\helpref{wxProcess::Exists}{wxprocessexists},\rtfsp
+\helpref{Exec sample}{sampleexex}
\wxheading{Include files}
If it is NULL, you have not turned on the redirection.
See \helpref{wxProcess::Redirect}{wxprocessredirect}.
+\membersection{wxProcess::Kill}\label{wxprocesskill}
+
+\func{static wxKillError}{Kill}{\param{int}{ pid}, \param{wxSignal}{ signal = wxSIGNONE}}
+
+Send the specified signal to the given process. Possible signal values are:
+
+\begin{verbatim}
+enum wxSignal
+{
+ wxSIGNONE = 0, // verify if the process exists under Unix
+ wxSIGHUP,
+ wxSIGINT,
+ wxSIGQUIT,
+ wxSIGILL,
+ wxSIGTRAP,
+ wxSIGABRT,
+ wxSIGEMT,
+ wxSIGFPE,
+ wxSIGKILL, // forcefully kill, dangerous!
+ wxSIGBUS,
+ wxSIGSEGV,
+ wxSIGSYS,
+ wxSIGPIPE,
+ wxSIGALRM,
+ wxSIGTERM // terminate the process gently
+};
+\end{verbatim}
+
+{\tt wxSIGNONE}, {\tt wxSIGKILL} and {\tt wxSIGTERM} have the same meaning
+under both Unix and Windows but all the other signals are equivalent to
+{\tt wxSIGTERM} under Windows.
+
+Returns the element of {\tt wxKillError} enum:
+
+\begin{verbatim}
+enum wxKillError
+{
+ wxKILL_OK, // no error
+ wxKILL_BAD_SIGNAL, // no such signal
+ wxKILL_ACCESS_DENIED, // permission denied
+ wxKILL_NO_PROCESS, // no such process
+ wxKILL_ERROR // another, unspecified error
+};
+\end{verbatim}
+
+\wxheading{See also}
+
+\helpref{wxProcess::Exists}{wxprocessexists},\rtfsp
+\helpref{wxKill}{wxkill},\rtfsp
+\helpref{Exec sample}{sampleexex}
+
+\membersection{wxProcess::Kill}\label{wxprocessexists}
+
+\func{static bool}{Exists}{\param{int}{ pid}}
+
+Returns {\tt TRUE} if the given process exists in the system.
+
+\wxheading{See also}
+
+\helpref{wxProcess::Kill}{wxprocesskill},\rtfsp
+\helpref{Exec sample}{sampleexex}
+
\membersection{wxProcess::OnTerminate}\label{wxprocessonterminate}
\constfunc{void}{OnTerminate}{\param{int}{ pid}, \param{int}{ status}}
until the program terminates) or asynchronously (notification will come later).
It also shows how to capture the output of the child process in both
-synchronous and asynchronous cases.
+synchronous and asynchronous cases and how to kill the processes with
+\helpref{wxProcess::Kill}{wxprocesskill} and test for their existence with
+\helpref{wxProcess::Exists}{wxprocessexists}.
\subsection{Scroll subwindow sample}\label{samplescrollsub}
#pragma interface "process.h"
#endif
-#include "wx/defs.h"
-#include "wx/object.h"
#include "wx/event.h"
#if wxUSE_STREAMS
#include "wx/stream.h"
#endif
+#include "wx/utils.h" // for wxSignal
+
// ----------------------------------------------------------------------------
// A wxProcess object should be passed to wxExecute - than its OnTerminate()
// function will be called when the process terminates.
class WXDLLEXPORT wxProcess : public wxEvtHandler
{
-DECLARE_DYNAMIC_CLASS(wxProcess)
-
public:
wxProcess(wxEvtHandler *parent = (wxEvtHandler *) NULL, int id = -1)
{ Init(parent, id, FALSE); }
wxInputStream *errStream);
#endif // wxUSE_STREAMS
+ // kill the process with the given PID
+ static wxKillError Kill(int pid, wxSignal sig = wxSIGTERM);
+
+ // test if the given process exists
+ static bool Exists(int pid);
+
protected:
void Init(wxEvtHandler *parent, int id, bool redirect);
#endif // wxUSE_STREAMS
bool m_redirect;
+
+ DECLARE_DYNAMIC_CLASS(wxProcess)
};
// ----------------------------------------------------------------------------
// further signals are different in meaning between different Unix systems
};
-// the argument is ignored under Windows - the process is always killed
-WXDLLEXPORT int wxKill(long pid, wxSignal sig = wxSIGTERM);
+enum wxKillError
+{
+ wxKILL_OK, // no error
+ wxKILL_BAD_SIGNAL, // no such signal
+ wxKILL_ACCESS_DENIED, // permission denied
+ wxKILL_NO_PROCESS, // no such process
+ wxKILL_ERROR // another, unspecified error
+};
+
+// send the given signal to the process (only NONE and KILL are supported under
+// Windows, all others mean TERM), return 0 if ok and -1 on error
+//
+// return detailed error in rc if not NULL
+WXDLLEXPORT int wxKill(long pid,
+ wxSignal sig = wxSIGTERM,
+ wxKillError *rc = NULL);
// Execute a command in an interactive shell window (always synchronously)
// If no command then just the shell
#include "wx/textdlg.h"
#include "wx/listbox.h"
#include "wx/filedlg.h"
+ #include "wx/choicdlg.h"
#endif
#include "wx/txtstrm.h"
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
+ void OnKill(wxCommandEvent& event);
+
void OnClear(wxCommandEvent& event);
void OnSyncExec(wxCommandEvent& event);
void DoAsyncExec(const wxString& cmd);
+ // the PID of the last process we launched asynchronously
+ int m_pidLast;
+
// last command we executed
wxString m_cmdLast;
{
// menu items
Exec_Quit = 100,
+ Exec_Kill,
Exec_ClearLog,
Exec_SyncExec = 200,
Exec_AsyncExec,
// simple menu events like this the static method is much simpler.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Exec_Quit, MyFrame::OnQuit)
+ EVT_MENU(Exec_Kill, MyFrame::OnKill)
EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
+ m_pidLast = 0;
+
#ifdef __WXMAC__
// we need this in order to allow the about menu relocation, since ABOUT is
// not the default id of the about menu
// create a menu bar
wxMenu *menuFile = new wxMenu(_T(""), wxMENU_TEAROFF);
+ menuFile->Append(Exec_Kill, _T("&Kill process...\tCtrl-K"),
+ _T("Kill a process by PID"));
+ menuFile->AppendSeparator();
menuFile->Append(Exec_ClearLog, _T("&Clear log\tCtrl-C"),
_T("Clear the log window"));
menuFile->AppendSeparator();
#endif // wxUSE_STATUSBAR
}
-
-// event handlers
+// ----------------------------------------------------------------------------
+// event handlers: file and help menu
+// ----------------------------------------------------------------------------
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
- wxMessageBox(_T("Exec sample\n© 2000 Vadim Zeitlin"),
+ wxMessageBox(_T("Exec wxWindows Sample\n© 2000-2001 Vadim Zeitlin"),
_T("About Exec"), wxOK | wxICON_INFORMATION, this);
}
+void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
+{
+ long pid = wxGetNumberFromUser(_T("Please specify the process to kill"),
+ _T("Enter PID:"),
+ _T("Exec question"),
+ m_pidLast,
+ 1, INT_MAX,
+ this);
+ if ( pid == -1 )
+ {
+ // cancelled
+ return;
+ }
+
+ static const wxString signalNames[] =
+ {
+ _T("Just test (SIGNONE)"),
+ _T("Hangup (SIGHUP)"),
+ _T("Interrupt (SIGINT)"),
+ _T("Quit (SIGQUIT)"),
+ _T("Illegal instruction (SIGILL)"),
+ _T("Trap (SIGTRAP)"),
+ _T("Abort (SIGABRT)"),
+ _T("Emulated trap (SIGEMT)"),
+ _T("FP exception (SIGFPE)"),
+ _T("Kill (SIGKILL)"),
+ _T("Bus (SIGBUS)"),
+ _T("Segment violation (SIGSEGV)"),
+ _T("System (SIGSYS)"),
+ _T("Broken pipe (SIGPIPE)"),
+ _T("Alarm (SIGALRM)"),
+ _T("Terminate (SIGTERM)"),
+ };
+
+ int sig = wxGetSingleChoiceIndex(_T("How to kill the process?"),
+ _T("Exec question"),
+ WXSIZEOF(signalNames), signalNames,
+ this);
+ switch ( sig )
+ {
+ default:
+ wxFAIL_MSG( _T("unexpected return value") );
+ // fall through
+
+ case -1:
+ // cancelled
+ return;
+
+ case wxSIGNONE:
+ case wxSIGHUP:
+ case wxSIGINT:
+ case wxSIGQUIT:
+ case wxSIGILL:
+ case wxSIGTRAP:
+ case wxSIGABRT:
+ case wxSIGEMT:
+ case wxSIGFPE:
+ case wxSIGKILL:
+ case wxSIGBUS:
+ case wxSIGSEGV:
+ case wxSIGSYS:
+ case wxSIGPIPE:
+ case wxSIGALRM:
+ case wxSIGTERM:
+ break;
+ }
+
+ if ( sig == 0 )
+ {
+ if ( wxProcess::Exists(pid) )
+ wxLogStatus(_T("Process %d is running."), pid);
+ else
+ wxLogStatus(_T("No process with pid = %d."), pid);
+ }
+ else // not SIGNONE
+ {
+ wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
+ if ( rc == wxKILL_OK )
+ {
+ wxLogStatus(_T("Process %d killed with signal %d."), pid, sig);
+ }
+ else
+ {
+ static const wxChar *errorText[] =
+ {
+ _T(""), // no error
+ _T("signal not supported"),
+ _T("permission denied"),
+ _T("no such process"),
+ _T("unspecified error"),
+ };
+
+ wxLogStatus(_T("Failed to kill process %d with signal %d: %s"),
+ pid, sig, errorText[rc]);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// event handlers: exec menu
+// ----------------------------------------------------------------------------
+
void MyFrame::DoAsyncExec(const wxString& cmd)
{
wxProcess *process = new MyProcess(this, cmd);
- long pid = wxExecute(cmd, FALSE /* async */, process);
- if ( !pid )
+ m_pidLast = wxExecute(cmd, FALSE /* async */, process);
+ if ( !m_pidLast )
{
wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
}
else
{
- wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str());
+ wxLogStatus(_T("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
m_cmdLast = cmd;
}
DoAsyncExec(cmd);
}
+// ----------------------------------------------------------------------------
+// DDE stuff
+// ----------------------------------------------------------------------------
+
#ifdef __WINDOWS__
bool MyFrame::GetDDEServer()
#endif // __WINDOWS__
+// ----------------------------------------------------------------------------
+// various helpers
+// ----------------------------------------------------------------------------
+
// input polling
void MyFrame::OnIdle(wxIdleEvent& event)
{
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
#ifdef __GNUG__
#pragma implementation "process.h"
#endif
#pragma hdrstop
#endif
-#ifndef WX_PRECOMP
- #include "wx/defs.h"
-#endif
-
#include "wx/process.h"
+// ----------------------------------------------------------------------------
+// event tables and such
+// ----------------------------------------------------------------------------
+
DEFINE_EVENT_TYPE(wxEVT_END_PROCESS)
IMPLEMENT_DYNAMIC_CLASS(wxProcess, wxEvtHandler)
IMPLEMENT_DYNAMIC_CLASS(wxProcessEvent, wxEvent)
+// ============================================================================
+// wxProcess implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxProcess creation
+// ----------------------------------------------------------------------------
+
void wxProcess::Init(wxEvtHandler *parent, int id, bool redirect)
{
if ( parent )
#endif // wxUSE_STREAMS
}
+// ----------------------------------------------------------------------------
+// wxProcess termination
+// ----------------------------------------------------------------------------
+
wxProcess::~wxProcess()
{
#if wxUSE_STREAMS
SetNextHandler(NULL);
}
+// ----------------------------------------------------------------------------
+// process IO redirection
+// ----------------------------------------------------------------------------
+
#if wxUSE_STREAMS
void wxProcess::SetPipeStreams(wxInputStream *inputSstream,
}
#endif // wxUSE_STREAMS
+
+// ----------------------------------------------------------------------------
+// process killing
+// ----------------------------------------------------------------------------
+
+/* static */
+wxKillError wxProcess::Kill(int pid, wxSignal sig)
+{
+ wxKillError rc;
+ (void)wxKill(pid, sig, &rc);
+
+ return rc;
+}
+
+/* static */
+bool wxProcess::Exists(int pid)
+{
+ switch ( wxProcess::Kill(pid, wxSIGNONE) )
+ {
+ case wxKILL_OK:
+ case wxKILL_ACCESS_DENIED:
+ return TRUE;
+
+ default:
+ case wxKILL_ERROR:
+ case wxKILL_BAD_SIGNAL:
+ wxFAIL_MSG( _T("unexpected wxProcess::Kill() return code") );
+ // fall through
+
+ case wxKILL_NO_PROCESS:
+ return FALSE;
+ }
+}
+
// process management
// ----------------------------------------------------------------------------
-int wxKill(long pid, wxSignal sig)
+#ifdef __WIN32__
+
+// structure used to pass parameters from wxKill() to wxEnumFindByPidProc()
+struct wxFindByPidParams
{
-#ifndef __WIN32__
- return -1;
-#else
- // This in a work in progress. We need to eliminate the call to wxSleep,
- // deal with child processes, and also test it :-)
- HWND hHwnd;
- HANDLE hProcess;
- unsigned long code;
- bool terminateSuccess = TRUE;
-
- hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
- FALSE, (unsigned long)pid);
- if (hProcess == NULL)
- return -1;
+ wxFindByPidParams() { hwnd = 0; pid = 0; }
+
+ // the HWND used to return the result
+ HWND hwnd;
- if (sig == wxSIGKILL)
- terminateSuccess = (TerminateProcess(hProcess, 0) != 0);
- else if (sig != wxSIGNONE)
+ // the PID we're looking from
+ DWORD pid;
+};
+
+// wxKill helper: EnumWindows() callback which is used to find the first (top
+// level) window belonging to the given process
+BOOL CALLBACK wxEnumFindByPidProc(HWND hwnd, LPARAM lParam)
+{
+ DWORD pid;
+ (void)::GetWindowThreadProcessId(hwnd, &pid);
+
+ wxFindByPidParams *params = (wxFindByPidParams *)lParam;
+ if ( pid == params->pid )
{
- hHwnd = ::FindWindow(NULL, NULL);
- while (hHwnd != 0)
+ // remember the window we found
+ params->hwnd = hwnd;
+
+ // return FALSE to stop the enumeration
+ return FALSE;
+ }
+
+ // continue enumeration
+ return TRUE;
+}
+
+#endif // __WIN32__
+
+int wxKill(long pid, wxSignal sig, wxKillError *krc)
+{
+#ifdef __WIN32__
+ // get the process handle to operate on
+ HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION,
+ FALSE, // not inheritable
+ (DWORD)pid);
+ if ( hProcess == NULL )
+ {
+ if ( krc )
{
- if (::GetParent(hHwnd) == 0)
+ if ( ::GetLastError() == ERROR_ACCESS_DENIED )
+ {
+ *krc = wxKILL_ACCESS_DENIED;
+ }
+ else
+ {
+ *krc = wxKILL_NO_PROCESS;
+ }
+ }
+
+ return -1;
+ }
+
+ bool ok = TRUE;
+ switch ( sig )
+ {
+ case wxSIGKILL:
+ // kill the process forcefully returning -1 as error code
+ if ( !::TerminateProcess(hProcess, (UINT)-1) )
+ {
+ wxLogSysError(_("Failed to kill process %d"), pid);
+
+ if ( krc )
+ {
+ // this is not supposed to happen if we could open the
+ // process
+ *krc = wxKILL_ERROR;
+ }
+
+ ok = FALSE;
+ }
+ break;
+
+ case wxSIGNONE:
+ // do nothing, we just want to test for process existence
+ break;
+
+ default:
+ // any other signal means "terminate"
{
- unsigned long testpid = 0;
- GetWindowThreadProcessId(hHwnd, &testpid);
- if ((unsigned long)pid == testpid)
+ wxFindByPidParams params;
+ params.pid = (DWORD)pid;
+
+ // EnumWindows() has nice semantics: it returns 0 if it found
+ // something or if an error occured and non zero if it
+ // enumerated all the window
+ if ( !::EnumWindows(wxEnumFindByPidProc, (LPARAM)¶ms) )
{
- PostMessage(hHwnd, WM_QUIT, 0, 0);
- // How to make this better?
- // If we don't wait, the return value is wrong.
- wxSleep(1);
- break;
+ // did we find any window?
+ if ( params.hwnd )
+ {
+ // tell the app to close
+ //
+ // NB: this is the harshest way, the app won't have
+ // opportunity to save any files, for example, but
+ // this is probably what we want here. If not we
+ // can also use SendMesageTimeout(WM_CLOSE)
+ if ( !::PostMessage(params.hwnd, WM_QUIT, 0, 0) )
+ {
+ wxLogLastError(_T("PostMessage(WM_QUIT)"));
+ }
+ }
+ else // it was an error then
+ {
+ wxLogLastError(_T("EnumWindows"));
+
+ ok = FALSE;
+ }
+ }
+ else // no windows for this PID
+ {
+ if ( krc )
+ {
+ *krc = wxKILL_ERROR;
+ }
+
+ ok = FALSE;
}
}
- hHwnd = GetWindow(hHwnd, GW_HWNDNEXT);
+ }
+
+ // the return code
+ DWORD rc;
+
+ if ( ok )
+ {
+ // as we wait for a short time, we can use just WaitForSingleObject()
+ // and not MsgWaitForMultipleObjects()
+ switch ( ::WaitForSingleObject(hProcess, 500 /* msec */) )
+ {
+ case WAIT_OBJECT_0:
+ // process terminated
+ if ( !::GetExitCodeProcess(hProcess, &rc) )
+ {
+ wxLogLastError(_T("GetExitCodeProcess"));
+ }
+ break;
+
+ default:
+ wxFAIL_MSG( _T("unexpected WaitForSingleObject() return") );
+ // fall through
+
+ case WAIT_FAILED:
+ wxLogLastError(_T("WaitForSingleObject"));
+ // fall through
+
+ case WAIT_TIMEOUT:
+ if ( krc )
+ {
+ *krc = wxKILL_ERROR;
+ }
+
+ rc = STILL_ACTIVE;
+ break;
}
}
+ else // !ok
+ {
+ // just to suppress the warnings about uninitialized variable
+ rc = 0;
+ }
- GetExitCodeProcess(hProcess, &code);
- CloseHandle(hProcess);
+ ::CloseHandle(hProcess);
- if (sig == wxSIGNONE)
+ // the return code is the same as from Unix kill(): 0 if killed
+ // successfully or -1 on error
+ if ( sig == wxSIGNONE )
{
- if (code == STILL_ACTIVE)
+ if ( ok && rc == STILL_ACTIVE )
+ {
+ // there is such process => success
return 0;
- else
- return -1;
+ }
}
- else
+ else // not SIGNONE
{
- if (!terminateSuccess || code == STILL_ACTIVE)
- return -1;
- else
+ if ( ok && rc != STILL_ACTIVE )
+ {
+ // killed => success
return 0;
+ }
}
-#endif
+#else // Win15
+ wxFAIL_MSG( _T("not implemented") );
+#endif // Win32/Win16
+
+ // error
+ return -1;
}
// Execute a program in an Interactive Shell
// process management
// ----------------------------------------------------------------------------
-int wxKill(long pid, wxSignal sig)
+int wxKill(long pid, wxSignal sig, wxKillError *rc)
{
- return kill((pid_t)pid, (int)sig);
+ int err = kill((pid_t)pid, (int)sig);
+ if ( rc )
+ {
+ switch ( err )
+ {
+ case 0:
+ *rc = wxKILL_OK;
+ break;
+
+ case EINVAL:
+ *rc = wxKILL_BAD_SIGNAL;
+ break;
+
+ case EPERM:
+ *rc = wxKILL_ACCESS_DENIED;
+ break;
+
+ case ESRCH:
+ *rc = wxKILL_NO_PROCESS;
+ break;
+
+ default:
+ // this goes against Unix98 docs so log it
+ wxLogDebug(_T("unexpected kill(2) return value %d"), err);
+
+ // something else...
+ *rc = wxKILL_ERROR;
+ }
+ }
+
+ return err;
}
#define WXEXECUTE_NARGS 127