aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--include/winapi/cmd_line.hpp9
-rw-r--r--include/winapi/error.hpp9
-rw-r--r--include/winapi/process.hpp25
-rw-r--r--src/process.cpp77
-rw-r--r--test/unit_tests/process.cpp55
6 files changed, 173 insertions, 4 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 991f6b9..1cbd5cd 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -45,7 +45,7 @@ after_build:
- appveyor.exe PushArtifact "%APPVEYOR_PROJECT_NAME%-%PLATFORM%-%CONFIGURATION%.zip"
test_script:
- - '"%install_dir%\bin\winapi-common-unit-tests.exe"'
+ - '"%install_dir%\bin\winapi-common-unit-tests.exe" -- "--test_exe=%install_dir%\bin\winapi-common-test-args.exe"'
for:
# Build Release on master only to speed things up:
diff --git a/include/winapi/cmd_line.hpp b/include/winapi/cmd_line.hpp
index ac88286..60bc56a 100644
--- a/include/winapi/cmd_line.hpp
+++ b/include/winapi/cmd_line.hpp
@@ -21,6 +21,12 @@ public:
CommandLine() = default;
+ CommandLine(const std::string& argv0, const std::vector<std::string>& args = {})
+ : argv0(argv0), args(args) {}
+
+ CommandLine(std::string&& argv0, std::vector<std::string>&& args = {})
+ : argv0(std::move(argv0)), args(std::move(args)) {}
+
bool has_argv0() const { return !argv0.empty(); }
std::string get_argv0() const { return argv0; }
@@ -50,9 +56,6 @@ private:
CommandLine(std::vector<std::string>&& args) : args(std::move(args)) {}
- CommandLine(std::string&& argv0, std::vector<std::string>&& args = {})
- : argv0(std::move(argv0)), args(std::move(args)) {}
-
const std::string argv0;
const std::vector<std::string> args;
};
diff --git a/include/winapi/error.hpp b/include/winapi/error.hpp
index 4b3f1e0..89bcfed 100644
--- a/include/winapi/error.hpp
+++ b/include/winapi/error.hpp
@@ -9,6 +9,8 @@
#include <windows.h>
+#include <sstream>
+#include <stdexcept>
#include <string>
#include <system_error>
@@ -31,5 +33,12 @@ inline const CategoryWindows& category_windows() {
std::system_error windows(DWORD code, const char* function);
+template <typename Ret>
+std::runtime_error custom(Ret ret, const char* function) {
+ std::ostringstream oss;
+ oss << "Function " << function << " failed with error code " << ret;
+ return std::runtime_error{oss.str()};
+}
+
} // namespace error
} // namespace winapi
diff --git a/include/winapi/process.hpp b/include/winapi/process.hpp
new file mode 100644
index 0000000..d20ac38
--- /dev/null
+++ b/include/winapi/process.hpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include "cmd_line.hpp"
+#include "handle.hpp"
+
+#include <utility>
+
+namespace winapi {
+
+class Process {
+public:
+ static Process create(const CommandLine&);
+
+ void wait();
+
+private:
+ explicit Process(Handle&& handle) : m_handle{std::move(handle)} {}
+
+ Handle m_handle;
+};
+
+} // namespace winapi
diff --git a/src/process.cpp b/src/process.cpp
new file mode 100644
index 0000000..1771328
--- /dev/null
+++ b/src/process.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/cmd_line.hpp>
+#include <winapi/error.hpp>
+#include <winapi/process.hpp>
+#include <winapi/utf8.hpp>
+
+#include <boost/config.hpp>
+
+#include <windows.h>
+
+#include <cstring>
+#include <utility>
+#include <vector>
+
+namespace winapi {
+namespace {
+
+typedef std::vector<wchar_t> EscapedCommandLine;
+
+Handle create_process(EscapedCommandLine cmd_line) {
+ BOOST_STATIC_CONSTEXPR DWORD flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
+
+ STARTUPINFOW startup_info;
+ std::memset(&startup_info, 0, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+
+ PROCESS_INFORMATION child_info;
+ std::memset(&child_info, 0, sizeof(child_info));
+
+ const auto ret = ::CreateProcessW(
+ NULL, cmd_line.data(), NULL, NULL, FALSE, flags, NULL, NULL, &startup_info, &child_info);
+
+ if (!ret) {
+ throw error::windows(GetLastError(), "CreateProcessW");
+ }
+
+ Handle h_process{child_info.hProcess};
+ Handle h_thread{child_info.hThread};
+
+ return std::move(h_process);
+}
+
+EscapedCommandLine escape_command_line(const CommandLine& cmd_line) {
+ const auto whole = widen(cmd_line.join());
+ return {whole.cbegin(), whole.cend()};
+}
+
+Handle create_process(const CommandLine& cmd_line) {
+ return create_process(escape_command_line(cmd_line));
+}
+
+} // namespace
+
+Process Process::create(const CommandLine& cmd_line) {
+ return Process{create_process(cmd_line)};
+}
+
+void Process::wait() {
+ const auto ret = ::WaitForSingleObject(static_cast<HANDLE>(m_handle), INFINITE);
+
+ switch (ret) {
+ case WAIT_OBJECT_0:
+ m_handle = Handle{};
+ return;
+ case WAIT_FAILED:
+ throw error::windows(GetLastError(), "WaitForSingleObject");
+ default:
+ // Shouldn't happen.
+ throw error::custom(ret, "WaitForSingleObject");
+ }
+}
+
+} // namespace winapi
diff --git a/test/unit_tests/process.cpp b/test/unit_tests/process.cpp
new file mode 100644
index 0000000..69327e1
--- /dev/null
+++ b/test/unit_tests/process.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/cmd_line.hpp>
+#include <winapi/process.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include <stdexcept>
+#include <string>
+
+namespace {
+
+class WhereTestExe {
+public:
+ WhereTestExe() : m_test_exe(find_test_exe()) {}
+
+ const std::string& get_test_exe() { return m_test_exe; }
+
+private:
+ static std::string find_test_exe() {
+ static const std::string param_prefix{"--test_exe="};
+ const auto cmd_line = winapi::CommandLine::query();
+ const auto& args = cmd_line.get_args();
+ for (const auto& arg : args) {
+ if (arg.rfind(param_prefix, 0) == 0) {
+ return arg.substr(param_prefix.length());
+ }
+ }
+ throw std::runtime_error{"couldn't find parameter --test_exe"};
+ }
+
+ const std::string m_test_exe;
+};
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(process_tests)
+
+BOOST_AUTO_TEST_CASE(create_dir) {
+ const winapi::CommandLine cmd_line{"cmd.exe", {"/c", "dir"}};
+ auto process = winapi::Process::create(cmd_line);
+ process.wait();
+}
+
+BOOST_FIXTURE_TEST_CASE(create_test_exe, WhereTestExe) {
+ const winapi::CommandLine cmd_line{get_test_exe()};
+ auto process = winapi::Process::create(cmd_line);
+ process.wait();
+ BOOST_TEST(true, "Successfully created test process");
+}
+
+BOOST_AUTO_TEST_SUITE_END()