diff options
-rw-r--r-- | .appveyor.yml | 2 | ||||
-rw-r--r-- | include/winapi/cmd_line.hpp | 9 | ||||
-rw-r--r-- | include/winapi/error.hpp | 9 | ||||
-rw-r--r-- | include/winapi/process.hpp | 25 | ||||
-rw-r--r-- | src/process.cpp | 77 | ||||
-rw-r--r-- | test/unit_tests/process.cpp | 55 |
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() |