From 1abf2fbac63c90c315cf6e87d6bce0c285bd4019 Mon Sep 17 00:00:00 2001
From: Egor Tensin <Egor.Tensin@gmail.com>
Date: Sun, 25 Oct 2020 01:57:29 +0300
Subject: Process: add termination methods

---
 include/winapi/process.hpp  |  4 +++-
 src/process.cpp             | 27 +++++++++++++++++++++++++++
 test/unit_tests/process.cpp | 17 +++++++++++++++++
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/include/winapi/process.hpp b/include/winapi/process.hpp
index 05411cd..5c9e3c9 100644
--- a/include/winapi/process.hpp
+++ b/include/winapi/process.hpp
@@ -26,8 +26,10 @@ public:
 
     static Process runas(const CommandLine&);
 
+    bool is_running() const;
     void wait() const;
-
+    void terminate(int ec = 0) const;
+    void shut_down(int ec = 0) const;
     int get_exit_code() const;
 
     static std::string get_exe_path();
diff --git a/src/process.cpp b/src/process.cpp
index a9f24cc..c5e68d7 100644
--- a/src/process.cpp
+++ b/src/process.cpp
@@ -110,6 +110,22 @@ Process Process::runas(const CommandLine& cmd_line) {
     return Process{shell_execute(cmd_line)};
 }
 
+bool Process::is_running() const {
+    const auto ret = ::WaitForSingleObject(static_cast<HANDLE>(m_handle), 0);
+
+    switch (ret) {
+        case WAIT_OBJECT_0:
+            return false;
+        case WAIT_TIMEOUT:
+            return true;
+        case WAIT_FAILED:
+            throw error::windows(GetLastError(), "WaitForSingleObject");
+        default:
+            // Shouldn't happen.
+            throw error::custom(ret, "WaitForSingleObject");
+    }
+}
+
 void Process::wait() const {
     const auto ret = ::WaitForSingleObject(static_cast<HANDLE>(m_handle), INFINITE);
 
@@ -124,6 +140,17 @@ void Process::wait() const {
     }
 }
 
+void Process::terminate(int ec) const {
+    if (!::TerminateProcess(static_cast<HANDLE>(m_handle), static_cast<UINT>(ec))) {
+        throw error::windows(GetLastError(), "TerminateProcess");
+    }
+}
+
+void Process::shut_down(int ec) const {
+    terminate(ec);
+    wait();
+}
+
 int Process::get_exit_code() const {
     DWORD ec = 0;
 
diff --git a/test/unit_tests/process.cpp b/test/unit_tests/process.cpp
index 715364f..cfc50f4 100644
--- a/test/unit_tests/process.cpp
+++ b/test/unit_tests/process.cpp
@@ -15,6 +15,8 @@
 
 #include <boost/test/unit_test.hpp>
 
+#include <chrono>
+#include <thread>
 #include <utility>
 
 using namespace winapi;
@@ -89,4 +91,19 @@ BOOST_FIXTURE_TEST_CASE(echo_runas, WithEchoExe) {
     BOOST_TEST(process.get_exit_code() == 0);
 }
 
+BOOST_FIXTURE_TEST_CASE(echo_terminate, WithEchoExe) {
+    const CommandLine cmd_line{get_echo_exe()};
+    const auto process = Process::create(cmd_line);
+
+    // echo.exe is stuck trying to read stdin.
+    BOOST_TEST(process.is_running());
+    std::this_thread::sleep_for(std::chrono::seconds{3});
+    BOOST_TEST(process.is_running());
+
+    process.shut_down(123);
+
+    BOOST_TEST(!process.is_running());
+    BOOST_TEST(process.get_exit_code() == 123);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
-- 
cgit v1.2.3