411 lines
15 KiB
C++
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
|