123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- //
- // 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 <boost/process/v2/cstring_ref.hpp>
- #include <boost/process/v2/detail/config.hpp>
- #include <boost/process/v2/detail/last_error.hpp>
- #include <boost/process/v2/detail/throw_error.hpp>
- #include <boost/process/v2/detail/utf8.hpp>
- #include <boost/process/v2/error.hpp>
- #include <numeric>
- #include <windows.h>
- #if defined(BOOST_PROCESS_V2_STANDALONE)
- #include <asio/execution/executor.hpp>
- #include <asio/is_executor.hpp>
- #include <asio/execution_context.hpp>
- #else
- #include <boost/asio/execution/executor.hpp>
- #include <boost/asio/is_executor.hpp>
- #include <boost/asio/execution_context.hpp>
- #endif
- BOOST_PROCESS_V2_BEGIN_NAMESPACE
- template<typename Executor>
- struct basic_process;
- namespace detail
- {
- struct base {};
- struct derived : base {};
- template<typename Launcher, typename Init>
- inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
- Init && init, base && )
- {
- return error_code{};
- }
- template<typename Launcher, typename Init>
- 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<typename Launcher, typename Init>
- inline std::false_type probe_on_setup(
- Launcher & launcher, Init && init, base && );
- template<typename Launcher, typename Init>
- inline auto probe_on_setup(Launcher & launcher, Init && init, derived && )
- -> std::is_same<error_code, decltype(init.on_setup(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
- template<typename Launcher, typename Init>
- using has_on_setup = decltype(probe_on_setup(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
- template<typename Launcher>
- inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line)
- {
- return error_code{};
- }
- template<typename Launcher, typename Init1, typename ... Inits>
- 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<typename Launcher, typename Init>
- inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
- const error_code & ec, Init && init, base && )
- {
- }
- template<typename Launcher, typename Init>
- 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<typename Launcher, typename Init>
- inline std::false_type probe_on_error(
- Launcher & launcher, Init && init, base && );
- template<typename Launcher, typename Init>
- inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
- -> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<std::error_code&>()))>;
- template<typename Launcher, typename Init>
- using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
- template<typename Launcher>
- inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
- const error_code & ec)
- {
- }
- template<typename Launcher, typename Init1, typename ... Inits>
- 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<typename Launcher, typename Init>
- inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
- Init && init, base && )
- {
- }
- template<typename Launcher, typename Init>
- 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<typename Launcher, typename Init>
- inline std::false_type probe_on_success(
- Launcher & launcher, Init && init, base && );
- template<typename Launcher, typename Init>
- inline auto probe_on_success(Launcher & launcher, Init && init, derived && )
- -> std::is_same<error_code, decltype(init.on_success(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
- template<typename Launcher, typename Init>
- using has_on_success = decltype(probe_on_success(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
- template<typename Launcher>
- inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line)
- {
- }
- template<typename Launcher, typename Init1, typename ... Inits>
- 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<typename Launcher, typename Init>
- struct is_initializer : std::integral_constant<bool,
- has_on_setup<Launcher, Init>::value ||
- has_on_error<Launcher, Init>::value ||
- has_on_success<Launcher, Init>::value>
- {
- };
- template<typename Launcher, typename ... Inits>
- struct all_are_initializers;
- template<typename Launcher>
- struct all_are_initializers<Launcher> : std::true_type {};
- template<typename Launcher, typename Init>
- struct all_are_initializers<Launcher, Init> : is_initializer<Launcher, Init> {};
- template<typename Launcher, typename Init, typename ... Tail>
- struct all_are_initializers<Launcher, Init, Tail...>
- : std::integral_constant<bool, is_initializer<Launcher, Init>::value && all_are_initializers<Launcher, Tail...>::value>
- {
- };
- }
- template<typename Executor>
- 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<typename Executor, typename ... Inits>
- using enable_init = typename std::enable_if<
- detail::all_are_initializers<default_launcher, Inits...>::value,
- basic_process<Executor>>::type;
- default_launcher() = default;
- template<typename ExecutionContext, typename Args, typename ... Inits>
- auto operator()(ExecutionContext & context,
- const typename std::enable_if<std::is_convertible<
- ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
- filesystem::path >::type & executable,
- Args && args,
- Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
- {
- error_code ec;
- auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
- if (ec)
- v2::detail::throw_error(ec, "default_launcher");
- return proc;
- }
- template<typename ExecutionContext, typename Args, typename ... Inits>
- auto operator()(ExecutionContext & context,
- error_code & ec,
- const typename std::enable_if<std::is_convertible<
- ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
- filesystem::path >::type & executable,
- Args && args,
- Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
- {
- return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
- }
- template<typename Executor, typename Args, typename ... Inits>
- auto operator()(Executor exec,
- const typename std::enable_if<
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value
- || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
- filesystem::path >::type & executable,
- Args && args,
- Inits && ... inits ) -> enable_init<Executor, Inits...>
- {
- error_code ec;
- auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
- if (ec)
- detail::throw_error(ec, "default_launcher");
- return proc;
- }
- template<typename Executor, typename Args, typename ... Inits>
- auto operator()(Executor exec,
- error_code & ec,
- const typename std::enable_if<
- BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
- BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
- filesystem::path >::type & executable,
- Args && args,
- Inits && ... inits ) -> enable_init<Executor, Inits...>
- {
- auto command_line = this->build_command_line(executable, std::forward<Args>(args));
- ec = detail::on_setup(*this, executable, command_line, inits...);
- if (ec)
- {
- detail::on_error(*this, executable, command_line, ec, inits...);
- return basic_process<Executor>(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);
- if (ok == 0)
- {
- BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
- 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<Executor>(exec);
- }
- else
- {
- detail::on_success(*this, executable, command_line, inits...);
- if (process_information.hThread != INVALID_HANDLE_VALUE)
- ::CloseHandle(process_information.hThread);
- return basic_process<Executor>(exec,
- this->process_information.dwProcessId,
- this->process_information.hProcess);
- }
- }
-
- BOOST_PROCESS_V2_DECL static
- std::size_t escaped_argv_length(basic_string_view<wchar_t> ws);
- BOOST_PROCESS_V2_DECL static
- std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
- basic_string_view<wchar_t> ws);
-
- template<typename Argv>
- static std::wstring build_command_line_impl(
- const filesystem::path & pt,
- const Argv & argv,
- basic_string_view<wchar_t> 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<wchar_t> 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<typename Argv>
- static std::wstring build_command_line_impl(
- const filesystem::path & pt,
- const Argv & argv,
- basic_string_view<char> args)
- {
- std::vector<std::wstring> argw;
- argw.resize(std::distance(std::begin(argv), std::end(argv)));
- std::transform(std::begin(argv), std::end(argv), argw.begin(),
- [](basic_string_view <char> arg)
- {
- return detail::conv_string<wchar_t>(arg.data(), arg.size());
- });
- return build_command_line_impl(pt, argw, L"");
- }
- template<typename Args,
- typename Char = decltype(*std::begin(std::declval<Args>()))>
- 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));
- }
- static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
- {
- return args;
- }
- };
- }
- BOOST_PROCESS_V2_END_NAMESPACE
- #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|