// // boost/process/v2/windows/default_launcher.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP #define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP #include #include #include #include #include #include #include #if defined(BOOST_PROCESS_V2_STANDALONE) #include #include #include #else #include #include #include #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE template struct basic_process; namespace detail { struct base {}; struct derived : base {}; template inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, base && ) { return error_code{}; } template inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, derived && ) -> decltype(init.on_setup(launcher, executable, cmd_line)) { return init.on_setup(launcher, executable, cmd_line); } template inline std::false_type probe_on_setup( Launcher & launcher, Init && init, base && ); template inline auto probe_on_setup(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval()))>; template using has_on_setup = decltype(probe_on_setup(std::declval(), std::declval(), derived{})); template inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line) { return error_code{}; } template inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init1 && init1, Inits && ... inits) { auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{}); if (ec) return ec; else return on_setup(launcher, executable, cmd_line, inits...); } template inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec, Init && init, base && ) { } template inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec, Init && init, derived && ) -> decltype(init.on_error(launcher, ec, executable, cmd_line, ec)) { init.on_error(launcher, executable, cmd_line, ec); } template inline std::false_type probe_on_error( Launcher & launcher, Init && init, base && ); template inline auto probe_on_error(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval(), std::declval()))>; template using has_on_error = decltype(probe_on_error(std::declval(), std::declval(), derived{})); template inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec) { } template inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec, Init1 && init1, Inits && ... inits) { invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{}); on_error(launcher, executable, cmd_line, ec, inits...); } template inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, base && ) { } template inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, derived && ) -> decltype(init.on_success(launcher, executable, cmd_line)) { init.on_success(launcher, executable, cmd_line); } template inline std::false_type probe_on_success( Launcher & launcher, Init && init, base && ); template inline auto probe_on_success(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval()))>; template using has_on_success = decltype(probe_on_success(std::declval(), std::declval(), derived{})); template inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line) { } template inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init1 && init1, Inits && ... inits) { invoke_on_success(launcher, executable, cmd_line, init1, derived{}); on_success(launcher, executable, cmd_line, inits...); } template struct is_initializer : std::integral_constant::value || has_on_error::value || has_on_success::value> { }; template struct all_are_initializers; template struct all_are_initializers : std::true_type {}; template struct all_are_initializers : is_initializer {}; template struct all_are_initializers : std::integral_constant::value && all_are_initializers::value> { }; } template struct basic_process; namespace windows { /// The default launcher for processes on windows. struct default_launcher { //// The process_attributes passed to CreateProcess SECURITY_ATTRIBUTES * process_attributes = nullptr; //// The thread_attributes passed to CreateProcess SECURITY_ATTRIBUTES * thread_attributes = nullptr; /// The bInheritHandles option. Needs to be set to true by any initializers using handles. bool inherit_handles = false; /// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed. DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT}; /// A pointer to the subprocess environment. void * environment = nullptr; /// The startup director. An empty path will get ignored. filesystem::path current_directory{}; /// The full startup info passed to CreateProcess STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, nullptr}; /// The process_information that gets assigned after a call to CreateProcess PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; template using enable_init = typename std::enable_if< detail::all_are_initializers::value, basic_process>::type; default_launcher() = default; template auto operator()(ExecutionContext & context, const typename std::enable_if::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "default_launcher"); return proc; } template auto operator()(ExecutionContext & context, error_code & ec, const typename std::enable_if::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); } template auto operator()(Executor exec, const typename std::enable_if< BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "default_launcher"); return proc; } template auto operator()(Executor exec, error_code & ec, const typename std::enable_if< BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { auto command_line = this->build_command_line(executable, std::forward(args)); ec = detail::on_setup(*this, executable, command_line, inits...); if (ec) { detail::on_error(*this, executable, command_line, ec, inits...); return basic_process(exec); } auto ok = ::CreateProcessW( executable.empty() ? nullptr : executable.c_str(), command_line.empty() ? nullptr : &command_line.front(), process_attributes, thread_attributes, inherit_handles ? TRUE : FALSE, creation_flags, environment, current_directory.empty() ? nullptr : current_directory.c_str(), &startup_info.StartupInfo, &process_information); auto ec__ = detail::get_last_error(); if (ok == 0) { ec = detail::get_last_error(); detail::on_error(*this, executable, command_line, ec, inits...); if (process_information.hProcess != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hProcess); if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec); } else { detail::on_success(*this, executable, command_line, inits...); if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec, this->process_information.dwProcessId, this->process_information.hProcess); } } BOOST_PROCESS_V2_DECL static std::size_t escaped_argv_length(basic_string_view ws); BOOST_PROCESS_V2_DECL static std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size, basic_string_view ws); template static std::wstring build_command_line_impl( const filesystem::path & pt, const Argv & argv, basic_string_view args) { std::size_t req_size = std::accumulate( std::begin(argv), std::end(argv), escaped_argv_length(pt.native()), [](std::size_t sz, basic_string_view arg) -> std::size_t { return sz + 1u + escaped_argv_length(arg); }); std::wstring res; res.resize(req_size, L' '); wchar_t * itr = &res.front(); itr += escape_argv_string(itr, res.size(), pt.native()); for (const auto & a : argv) { itr++; itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a); } return res; } template static std::wstring build_command_line_impl( const filesystem::path & pt, const Argv & argv, basic_string_view args) { std::vector argw; argw.resize(std::distance(std::begin(argv), std::end(argv))); std::transform(std::begin(argv), std::end(argv), argw.begin(), [](basic_string_view arg) { return detail::conv_string(arg.data(), arg.size()); }); return build_command_line_impl(pt, argw, L""); } template()))> static std::wstring build_command_line(const filesystem::path & pt, const Args & args) { if (std::begin(args) == std::end(args)) return pt.native(); return build_command_line_impl(pt, args, *std::begin(args)); } }; } BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_HEADER_ONLY) #include #endif #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP