From 50567b69d60faa5a3575a077bb4f98530813c80d Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Sat, 16 Jun 2001 00:59:07 +0000
Subject: [PATCH] 1. improved wxKill() implementation for Win32 2. added
 wxKillError output parameter to wxKill 3. added wxProcess::Kill() and
 Exists() 4. documented all the new stuff 5. updated the sample to show it

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@10592 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/function.tex |  53 ++++++++-
 docs/latex/wx/process.tex  |  62 +++++++++++
 docs/latex/wx/tsamples.tex |   4 +-
 include/wx/process.h       |  14 ++-
 include/wx/utils.h         |  18 ++-
 samples/exec/exec.cpp      | 136 ++++++++++++++++++++++-
 src/common/process.cpp     |  66 ++++++++++-
 src/msw/utils.cpp          | 221 ++++++++++++++++++++++++++++++-------
 src/unix/utilsunx.cpp      |  34 +++++-
 9 files changed, 543 insertions(+), 65 deletions(-)

diff --git a/docs/latex/wx/function.tex b/docs/latex/wx/function.tex
index 1bbd53512e..e35b960e45 100644
--- a/docs/latex/wx/function.tex
+++ b/docs/latex/wx/function.tex
@@ -1755,13 +1755,56 @@ this default behaviour.
 
 \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}
 
diff --git a/docs/latex/wx/process.tex b/docs/latex/wx/process.tex
index 425930487f..4f7777e6eb 100644
--- a/docs/latex/wx/process.tex
+++ b/docs/latex/wx/process.tex
@@ -108,6 +108,68 @@ It returns an output stream correspoding to the input stream of the subprocess.
 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}}
diff --git a/docs/latex/wx/tsamples.tex b/docs/latex/wx/tsamples.tex
index a476c6c1a1..9bc339c8a7 100644
--- a/docs/latex/wx/tsamples.tex
+++ b/docs/latex/wx/tsamples.tex
@@ -162,7 +162,9 @@ external programs and the sample shows how to do this synchronously (waiting
 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}
 
diff --git a/include/wx/process.h b/include/wx/process.h
index ddf87b3a04..571bf65c29 100644
--- a/include/wx/process.h
+++ b/include/wx/process.h
@@ -16,14 +16,14 @@
     #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.
@@ -31,8 +31,6 @@
 
 class WXDLLEXPORT wxProcess : public wxEvtHandler
 {
-DECLARE_DYNAMIC_CLASS(wxProcess)
-
 public:
     wxProcess(wxEvtHandler *parent = (wxEvtHandler *) NULL, int id = -1)
         { Init(parent, id, FALSE); }
@@ -69,6 +67,12 @@ public:
                         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);
 
@@ -81,6 +85,8 @@ protected:
 #endif // wxUSE_STREAMS
 
     bool m_redirect;
+
+    DECLARE_DYNAMIC_CLASS(wxProcess)
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/wx/utils.h b/include/wx/utils.h
index 4c83498890..519c5ada1a 100644
--- a/include/wx/utils.h
+++ b/include/wx/utils.h
@@ -180,8 +180,22 @@ enum wxSignal
     // 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
diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp
index 80f4f4335d..1634f47fb7 100644
--- a/samples/exec/exec.cpp
+++ b/samples/exec/exec.cpp
@@ -40,6 +40,7 @@
     #include "wx/textdlg.h"
     #include "wx/listbox.h"
     #include "wx/filedlg.h"
+    #include "wx/choicdlg.h"
 #endif
 
 #include "wx/txtstrm.h"
@@ -83,6 +84,8 @@ public:
     // event handlers (these functions should _not_ be virtual)
     void OnQuit(wxCommandEvent& event);
 
+    void OnKill(wxCommandEvent& event);
+
     void OnClear(wxCommandEvent& event);
 
     void OnSyncExec(wxCommandEvent& event);
@@ -109,6 +112,9 @@ private:
 
     void DoAsyncExec(const wxString& cmd);
 
+    // the PID of the last process we launched asynchronously
+    int m_pidLast;
+
     // last command we executed
     wxString m_cmdLast;
 
@@ -192,6 +198,7 @@ enum
 {
     // menu items
     Exec_Quit = 100,
+    Exec_Kill,
     Exec_ClearLog,
     Exec_SyncExec = 200,
     Exec_AsyncExec,
@@ -215,6 +222,7 @@ static const wxChar *DIALOG_TITLE = _T("Exec sample");
 // 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)
@@ -273,6 +281,8 @@ bool MyApp::OnInit()
 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
@@ -281,6 +291,9 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
 
     // 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();
@@ -334,8 +347,9 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
 #endif // wxUSE_STATUSBAR
 }
 
-
-// event handlers
+// ----------------------------------------------------------------------------
+// event handlers: file and help menu
+// ----------------------------------------------------------------------------
 
 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
 {
@@ -350,15 +364,117 @@ void MyFrame::OnClear(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());
 
@@ -366,7 +482,7 @@ void MyFrame::DoAsyncExec(const wxString& cmd)
     }
     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;
     }
@@ -543,6 +659,10 @@ void MyFrame::OnFileExec(wxCommandEvent& event)
     DoAsyncExec(cmd);
 }
 
+// ----------------------------------------------------------------------------
+// DDE stuff
+// ----------------------------------------------------------------------------
+
 #ifdef __WINDOWS__
 
 bool MyFrame::GetDDEServer()
@@ -623,6 +743,10 @@ void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
 
 #endif // __WINDOWS__
 
+// ----------------------------------------------------------------------------
+// various helpers
+// ----------------------------------------------------------------------------
+
 // input polling
 void MyFrame::OnIdle(wxIdleEvent& event)
 {
diff --git a/src/common/process.cpp b/src/common/process.cpp
index 7d9cdbec72..e57f66688b 100644
--- a/src/common/process.cpp
+++ b/src/common/process.cpp
@@ -9,6 +9,14 @@
 // Licence:     wxWindows license
 /////////////////////////////////////////////////////////////////////////////
 
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
 #ifdef __GNUG__
     #pragma implementation "process.h"
 #endif
@@ -20,17 +28,25 @@
     #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 )
@@ -46,6 +62,10 @@ void wxProcess::Init(wxEvtHandler *parent, int id, bool redirect)
 #endif // wxUSE_STREAMS
 }
 
+// ----------------------------------------------------------------------------
+// wxProcess termination
+// ----------------------------------------------------------------------------
+
 wxProcess::~wxProcess()
 {
 #if wxUSE_STREAMS
@@ -70,6 +90,10 @@ void wxProcess::Detach()
     SetNextHandler(NULL);
 }
 
+// ----------------------------------------------------------------------------
+// process IO redirection
+// ----------------------------------------------------------------------------
+
 #if wxUSE_STREAMS
 
 void wxProcess::SetPipeStreams(wxInputStream *inputSstream,
@@ -82,3 +106,37 @@ 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;
+    }
+}
+
diff --git a/src/msw/utils.cpp b/src/msw/utils.cpp
index 215806852d..f5efb921f0 100644
--- a/src/msw/utils.cpp
+++ b/src/msw/utils.cpp
@@ -513,65 +513,204 @@ bool wxSetEnv(const wxString& var, const wxChar *value)
 // 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)&params) )
                 {
-                    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
diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp
index deeaa836ba..23658ace65 100644
--- a/src/unix/utilsunx.cpp
+++ b/src/unix/utilsunx.cpp
@@ -135,9 +135,39 @@ void wxUsleep(unsigned long milliseconds)
 // 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
-- 
2.50.0

