default_launcher.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //
  2. // boost/process/v2/windows/default_launcher.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
  11. #define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
  12. #include <boost/process/v2/cstring_ref.hpp>
  13. #include <boost/process/v2/detail/config.hpp>
  14. #include <boost/process/v2/detail/last_error.hpp>
  15. #include <boost/process/v2/detail/throw_error.hpp>
  16. #include <boost/process/v2/detail/utf8.hpp>
  17. #include <boost/process/v2/error.hpp>
  18. #include <numeric>
  19. #include <windows.h>
  20. #if defined(BOOST_PROCESS_V2_STANDALONE)
  21. #include <asio/execution/executor.hpp>
  22. #include <asio/is_executor.hpp>
  23. #include <asio/execution_context.hpp>
  24. #else
  25. #include <boost/asio/execution/executor.hpp>
  26. #include <boost/asio/is_executor.hpp>
  27. #include <boost/asio/execution_context.hpp>
  28. #endif
  29. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  30. template<typename Executor>
  31. struct basic_process;
  32. namespace detail
  33. {
  34. struct base {};
  35. struct derived : base {};
  36. template<typename Launcher, typename Init>
  37. inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  38. Init && init, base && )
  39. {
  40. return error_code{};
  41. }
  42. template<typename Launcher, typename Init>
  43. inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  44. Init && init, derived && )
  45. -> decltype(init.on_setup(launcher, executable, cmd_line))
  46. {
  47. return init.on_setup(launcher, executable, cmd_line);
  48. }
  49. template<typename Launcher, typename Init>
  50. inline std::false_type probe_on_setup(
  51. Launcher & launcher, Init && init, base && );
  52. template<typename Launcher, typename Init>
  53. inline auto probe_on_setup(Launcher & launcher, Init && init, derived && )
  54. -> std::is_same<error_code, decltype(init.on_setup(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
  55. template<typename Launcher, typename Init>
  56. using has_on_setup = decltype(probe_on_setup(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  57. template<typename Launcher>
  58. inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line)
  59. {
  60. return error_code{};
  61. }
  62. template<typename Launcher, typename Init1, typename ... Inits>
  63. inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  64. Init1 && init1, Inits && ... inits)
  65. {
  66. auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
  67. if (ec)
  68. return ec;
  69. else
  70. return on_setup(launcher, executable, cmd_line, inits...);
  71. }
  72. template<typename Launcher, typename Init>
  73. inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  74. const error_code & ec, Init && init, base && )
  75. {
  76. }
  77. template<typename Launcher, typename Init>
  78. inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  79. const error_code & ec, Init && init, derived && )
  80. -> decltype(init.on_error(launcher, ec, executable, cmd_line, ec))
  81. {
  82. init.on_error(launcher, executable, cmd_line, ec);
  83. }
  84. template<typename Launcher, typename Init>
  85. inline std::false_type probe_on_error(
  86. Launcher & launcher, Init && init, base && );
  87. template<typename Launcher, typename Init>
  88. inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
  89. -> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<std::error_code&>()))>;
  90. template<typename Launcher, typename Init>
  91. using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  92. template<typename Launcher>
  93. inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  94. const error_code & ec)
  95. {
  96. }
  97. template<typename Launcher, typename Init1, typename ... Inits>
  98. inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  99. const error_code & ec,
  100. Init1 && init1,
  101. Inits && ... inits)
  102. {
  103. invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
  104. on_error(launcher, executable, cmd_line, ec, inits...);
  105. }
  106. template<typename Launcher, typename Init>
  107. inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  108. Init && init, base && )
  109. {
  110. }
  111. template<typename Launcher, typename Init>
  112. inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  113. Init && init, derived && )
  114. -> decltype(init.on_success(launcher, executable, cmd_line))
  115. {
  116. init.on_success(launcher, executable, cmd_line);
  117. }
  118. template<typename Launcher, typename Init>
  119. inline std::false_type probe_on_success(
  120. Launcher & launcher, Init && init, base && );
  121. template<typename Launcher, typename Init>
  122. inline auto probe_on_success(Launcher & launcher, Init && init, derived && )
  123. -> std::is_same<error_code, decltype(init.on_success(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
  124. template<typename Launcher, typename Init>
  125. using has_on_success = decltype(probe_on_success(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  126. template<typename Launcher>
  127. inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line)
  128. {
  129. }
  130. template<typename Launcher, typename Init1, typename ... Inits>
  131. inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  132. Init1 && init1, Inits && ... inits)
  133. {
  134. invoke_on_success(launcher, executable, cmd_line, init1, derived{});
  135. on_success(launcher, executable, cmd_line, inits...);
  136. }
  137. template<typename Launcher, typename Init>
  138. struct is_initializer : std::integral_constant<bool,
  139. has_on_setup<Launcher, Init>::value ||
  140. has_on_error<Launcher, Init>::value ||
  141. has_on_success<Launcher, Init>::value>
  142. {
  143. };
  144. template<typename Launcher, typename ... Inits>
  145. struct all_are_initializers;
  146. template<typename Launcher>
  147. struct all_are_initializers<Launcher> : std::true_type {};
  148. template<typename Launcher, typename Init>
  149. struct all_are_initializers<Launcher, Init> : is_initializer<Launcher, Init> {};
  150. template<typename Launcher, typename Init, typename ... Tail>
  151. struct all_are_initializers<Launcher, Init, Tail...>
  152. : std::integral_constant<bool, is_initializer<Launcher, Init>::value && all_are_initializers<Launcher, Tail...>::value>
  153. {
  154. };
  155. }
  156. template<typename Executor>
  157. struct basic_process;
  158. namespace windows
  159. {
  160. /// The default launcher for processes on windows.
  161. struct default_launcher
  162. {
  163. //// The process_attributes passed to CreateProcess
  164. SECURITY_ATTRIBUTES * process_attributes = nullptr;
  165. //// The thread_attributes passed to CreateProcess
  166. SECURITY_ATTRIBUTES * thread_attributes = nullptr;
  167. /// The bInheritHandles option. Needs to be set to true by any initializers using handles.
  168. bool inherit_handles = false;
  169. /// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed.
  170. DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT};
  171. /// A pointer to the subprocess environment.
  172. void * environment = nullptr;
  173. /// The startup director. An empty path will get ignored.
  174. filesystem::path current_directory{};
  175. /// The full startup info passed to CreateProcess
  176. STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr,
  177. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
  178. INVALID_HANDLE_VALUE,
  179. INVALID_HANDLE_VALUE,
  180. INVALID_HANDLE_VALUE},
  181. nullptr};
  182. /// The process_information that gets assigned after a call to CreateProcess
  183. PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
  184. template<typename Executor, typename ... Inits>
  185. using enable_init = typename std::enable_if<
  186. detail::all_are_initializers<default_launcher, Inits...>::value,
  187. basic_process<Executor>>::type;
  188. default_launcher() = default;
  189. template<typename ExecutionContext, typename Args, typename ... Inits>
  190. auto operator()(ExecutionContext & context,
  191. const typename std::enable_if<std::is_convertible<
  192. ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
  193. filesystem::path >::type & executable,
  194. Args && args,
  195. Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
  196. {
  197. error_code ec;
  198. auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  199. if (ec)
  200. v2::detail::throw_error(ec, "default_launcher");
  201. return proc;
  202. }
  203. template<typename ExecutionContext, typename Args, typename ... Inits>
  204. auto operator()(ExecutionContext & context,
  205. error_code & ec,
  206. const typename std::enable_if<std::is_convertible<
  207. ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
  208. filesystem::path >::type & executable,
  209. Args && args,
  210. Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
  211. {
  212. return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  213. }
  214. template<typename Executor, typename Args, typename ... Inits>
  215. auto operator()(Executor exec,
  216. const typename std::enable_if<
  217. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value
  218. || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
  219. filesystem::path >::type & executable,
  220. Args && args,
  221. Inits && ... inits ) -> enable_init<Executor, Inits...>
  222. {
  223. error_code ec;
  224. auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  225. if (ec)
  226. detail::throw_error(ec, "default_launcher");
  227. return proc;
  228. }
  229. template<typename Executor, typename Args, typename ... Inits>
  230. auto operator()(Executor exec,
  231. error_code & ec,
  232. const typename std::enable_if<
  233. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
  234. BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
  235. filesystem::path >::type & executable,
  236. Args && args,
  237. Inits && ... inits ) -> enable_init<Executor, Inits...>
  238. {
  239. auto command_line = this->build_command_line(executable, std::forward<Args>(args));
  240. ec = detail::on_setup(*this, executable, command_line, inits...);
  241. if (ec)
  242. {
  243. detail::on_error(*this, executable, command_line, ec, inits...);
  244. return basic_process<Executor>(exec);
  245. }
  246. auto ok = ::CreateProcessW(
  247. executable.empty() ? nullptr : executable.c_str(),
  248. command_line.empty() ? nullptr : &command_line.front(),
  249. process_attributes,
  250. thread_attributes,
  251. inherit_handles ? TRUE : FALSE,
  252. creation_flags,
  253. environment,
  254. current_directory.empty() ? nullptr : current_directory.c_str(),
  255. &startup_info.StartupInfo,
  256. &process_information);
  257. if (ok == 0)
  258. {
  259. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  260. detail::on_error(*this, executable, command_line, ec, inits...);
  261. if (process_information.hProcess != INVALID_HANDLE_VALUE)
  262. ::CloseHandle(process_information.hProcess);
  263. if (process_information.hThread != INVALID_HANDLE_VALUE)
  264. ::CloseHandle(process_information.hThread);
  265. return basic_process<Executor>(exec);
  266. }
  267. else
  268. {
  269. detail::on_success(*this, executable, command_line, inits...);
  270. if (process_information.hThread != INVALID_HANDLE_VALUE)
  271. ::CloseHandle(process_information.hThread);
  272. return basic_process<Executor>(exec,
  273. this->process_information.dwProcessId,
  274. this->process_information.hProcess);
  275. }
  276. }
  277. BOOST_PROCESS_V2_DECL static
  278. std::size_t escaped_argv_length(basic_string_view<wchar_t> ws);
  279. BOOST_PROCESS_V2_DECL static
  280. std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
  281. basic_string_view<wchar_t> ws);
  282. template<typename Argv>
  283. static std::wstring build_command_line_impl(
  284. const filesystem::path & pt,
  285. const Argv & argv,
  286. basic_string_view<wchar_t> args)
  287. {
  288. std::size_t req_size = std::accumulate(
  289. std::begin(argv), std::end(argv), escaped_argv_length(pt.native()),
  290. [](std::size_t sz, basic_string_view<wchar_t> arg) -> std::size_t
  291. {
  292. return sz + 1u + escaped_argv_length(arg);
  293. });
  294. std::wstring res;
  295. res.resize(req_size, L' ');
  296. wchar_t * itr = &res.front();
  297. itr += escape_argv_string(itr, res.size(), pt.native());
  298. for (const auto & a : argv)
  299. {
  300. itr++;
  301. itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a);
  302. }
  303. return res;
  304. }
  305. template<typename Argv>
  306. static std::wstring build_command_line_impl(
  307. const filesystem::path & pt,
  308. const Argv & argv,
  309. basic_string_view<char> args)
  310. {
  311. std::vector<std::wstring> argw;
  312. argw.resize(std::distance(std::begin(argv), std::end(argv)));
  313. std::transform(std::begin(argv), std::end(argv), argw.begin(),
  314. [](basic_string_view <char> arg)
  315. {
  316. return detail::conv_string<wchar_t>(arg.data(), arg.size());
  317. });
  318. return build_command_line_impl(pt, argw, L"");
  319. }
  320. template<typename Args,
  321. typename Char = decltype(*std::begin(std::declval<Args>()))>
  322. static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
  323. {
  324. if (std::begin(args) == std::end(args))
  325. return pt.native();
  326. return build_command_line_impl(pt, args, *std::begin(args));
  327. }
  328. static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
  329. {
  330. return args;
  331. }
  332. };
  333. }
  334. BOOST_PROCESS_V2_END_NAMESPACE
  335. #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP