libcarla/include/system/boost/process/v2/windows/default_launcher.hpp
2024-10-18 13:19:59 +08:00

411 lines
15 KiB
C++

//
// 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/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)
asio::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)
asio::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);
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<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));
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/windows/impl/default_launcher.ipp>
#endif
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP