File indexing completed on 2025-09-18 09:02:57
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
0012 #define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
0013
0014 #include <boost/process/v2/cstring_ref.hpp>
0015 #include <boost/process/v2/detail/config.hpp>
0016 #include <boost/process/v2/detail/last_error.hpp>
0017 #include <boost/process/v2/detail/throw_error.hpp>
0018 #include <boost/process/v2/detail/utf8.hpp>
0019 #include <boost/process/v2/error.hpp>
0020
0021 #include <numeric>
0022 #include <memory>
0023 #include <type_traits>
0024 #include <windows.h>
0025
0026 #if defined(BOOST_PROCESS_V2_STANDALONE)
0027 #include <asio/execution/executor.hpp>
0028 #include <asio/is_executor.hpp>
0029 #include <asio/execution_context.hpp>
0030 #else
0031 #include <boost/asio/execution/executor.hpp>
0032 #include <boost/asio/is_executor.hpp>
0033 #include <boost/asio/execution_context.hpp>
0034 #endif
0035
0036 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0037
0038 template<typename Executor>
0039 struct basic_process;
0040
0041 namespace detail
0042 {
0043
0044 struct base {};
0045 struct derived : base {};
0046
0047 template<typename Launcher, typename Init>
0048 inline error_code invoke_on_setup(Launcher & , const filesystem::path &executable, std::wstring &cmd_line,
0049 Init && , base && )
0050 {
0051 return error_code{};
0052 }
0053
0054 template<typename Launcher, typename Init>
0055 inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0056 Init && init, derived && )
0057 -> decltype(init.on_setup(launcher, executable, cmd_line))
0058 {
0059 return init.on_setup(launcher, executable, cmd_line);
0060 }
0061
0062 template<typename Launcher, typename Init>
0063 inline std::false_type probe_on_setup(
0064 Launcher & launcher, Init && init, base && );
0065
0066 template<typename Launcher, typename Init>
0067 inline auto probe_on_setup(Launcher & launcher, Init && init, derived && )
0068 -> std::is_same<error_code, decltype(init.on_setup(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
0069
0070 template<typename Launcher, typename Init>
0071 using has_on_setup = decltype(probe_on_setup(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
0072
0073 template<typename Launcher>
0074 inline error_code on_setup(Launcher & , const filesystem::path &, std::wstring &)
0075 {
0076 return error_code{};
0077 }
0078
0079 template<typename Launcher, typename Init1, typename ... Inits>
0080 inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0081 Init1 && init1, Inits && ... inits)
0082 {
0083 auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
0084 if (ec)
0085 return ec;
0086 else
0087 return on_setup(launcher, executable, cmd_line, inits...);
0088 }
0089
0090
0091 template<typename Launcher, typename Init>
0092 inline void invoke_on_error(Launcher & , const filesystem::path &, std::wstring &,
0093 const error_code & , Init && , base && )
0094 {
0095 }
0096
0097 template<typename Launcher, typename Init>
0098 inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0099 const error_code & ec, Init && init, derived && )
0100 -> decltype(init.on_error(launcher, executable, cmd_line, ec))
0101 {
0102 init.on_error(launcher, executable, cmd_line, ec);
0103 }
0104
0105
0106 template<typename Launcher, typename Init>
0107 inline std::false_type probe_on_error(
0108 Launcher & launcher, Init && init, base && );
0109
0110 template<typename Launcher, typename Init>
0111 inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
0112 -> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<std::error_code&>()))>;
0113
0114 template<typename Launcher, typename Init>
0115 using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
0116
0117
0118 template<typename Launcher>
0119 inline void on_error(Launcher & , const filesystem::path &, std::wstring &,
0120 const error_code & )
0121 {
0122 }
0123
0124 template<typename Launcher, typename Init1, typename ... Inits>
0125 inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0126 const error_code & ec,
0127 Init1 && init1,
0128 Inits && ... inits)
0129 {
0130 invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
0131 on_error(launcher, executable, cmd_line, ec, inits...);
0132 }
0133
0134 template<typename Launcher, typename Init>
0135 inline void invoke_on_success(Launcher & , const filesystem::path &, std::wstring &,
0136 Init && , base && )
0137 {
0138 }
0139
0140 template<typename Launcher, typename Init>
0141 inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0142 Init && init, derived && )
0143 -> decltype(init.on_success(launcher, executable, cmd_line))
0144 {
0145 init.on_success(launcher, executable, cmd_line);
0146 }
0147
0148 template<typename Launcher, typename Init>
0149 inline std::false_type probe_on_success(
0150 Launcher & launcher, Init && init, base && );
0151
0152 template<typename Launcher, typename Init>
0153 inline auto probe_on_success(Launcher & launcher, Init && init, derived && )
0154 -> std::is_same<error_code, decltype(init.on_success(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
0155
0156 template<typename Launcher, typename Init>
0157 using has_on_success = decltype(probe_on_success(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
0158
0159 template<typename Launcher>
0160 inline void on_success(Launcher & , const filesystem::path &, std::wstring &)
0161 {
0162 }
0163
0164 template<typename Launcher, typename Init1, typename ... Inits>
0165 inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
0166 Init1 && init1, Inits && ... inits)
0167 {
0168 invoke_on_success(launcher, executable, cmd_line, init1, derived{});
0169 on_success(launcher, executable, cmd_line, inits...);
0170 }
0171
0172 template<typename Launcher, typename Init>
0173 struct is_initializer : std::integral_constant<bool,
0174 has_on_setup<Launcher, Init>::value ||
0175 has_on_error<Launcher, Init>::value ||
0176 has_on_success<Launcher, Init>::value>
0177 {
0178 };
0179
0180 template<typename Launcher, typename ... Inits>
0181 struct all_are_initializers;
0182
0183 template<typename Launcher>
0184 struct all_are_initializers<Launcher> : std::true_type {};
0185
0186
0187 template<typename Launcher, typename Init>
0188 struct all_are_initializers<Launcher, Init> : is_initializer<Launcher, Init> {};
0189
0190 template<typename Launcher, typename Init, typename ... Tail>
0191 struct all_are_initializers<Launcher, Init, Tail...>
0192 : std::integral_constant<bool, is_initializer<Launcher, Init>::value && all_are_initializers<Launcher, Tail...>::value>
0193 {
0194 };
0195
0196
0197 }
0198
0199 template<typename Executor>
0200 struct basic_process;
0201
0202 namespace windows
0203 {
0204
0205
0206 struct default_launcher
0207 {
0208
0209 SECURITY_ATTRIBUTES * process_attributes = nullptr;
0210
0211 SECURITY_ATTRIBUTES * thread_attributes = nullptr;
0212
0213 std::vector<HANDLE> inherited_handles;
0214
0215 DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT};
0216
0217 void * environment = nullptr;
0218
0219 filesystem::path current_directory{};
0220
0221
0222 STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr,
0223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
0224 INVALID_HANDLE_VALUE,
0225 INVALID_HANDLE_VALUE,
0226 INVALID_HANDLE_VALUE},
0227 nullptr};
0228
0229 PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
0230
0231 template<typename Executor, typename ... Inits>
0232 using enable_init = typename std::enable_if<
0233 detail::all_are_initializers<default_launcher, Inits...>::value,
0234 basic_process<Executor>>::type;
0235
0236 default_launcher() = default;
0237
0238 template<typename ExecutionContext, typename Args, typename ... Inits>
0239 auto operator()(ExecutionContext & context,
0240 const typename std::enable_if<std::is_convertible<
0241 ExecutionContext&, net::execution_context&>::value,
0242 filesystem::path >::type & executable,
0243 Args && args,
0244 Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
0245 {
0246 error_code ec;
0247 auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0248
0249 if (ec)
0250 v2::detail::throw_error(ec, "default_launcher");
0251
0252 return proc;
0253 }
0254
0255
0256 template<typename ExecutionContext, typename Args, typename ... Inits>
0257 auto operator()(ExecutionContext & context,
0258 error_code & ec,
0259 const typename std::enable_if<std::is_convertible<
0260 ExecutionContext&, net::execution_context&>::value,
0261 filesystem::path >::type & executable,
0262 Args && args,
0263 Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
0264 {
0265 return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0266 }
0267
0268 template<typename Executor, typename Args, typename ... Inits>
0269 auto operator()(Executor exec,
0270 const typename std::enable_if<
0271 net::execution::is_executor<Executor>::value
0272 || net::is_executor<Executor>::value,
0273 filesystem::path >::type & executable,
0274 Args && args,
0275 Inits && ... inits ) -> enable_init<Executor, Inits...>
0276 {
0277 error_code ec;
0278 auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0279
0280 if (ec)
0281 detail::throw_error(ec, "default_launcher");
0282
0283 return proc;
0284 }
0285
0286 template<typename Executor, typename Args, typename ... Inits>
0287 auto operator()(Executor exec,
0288 error_code & ec,
0289 const typename std::enable_if<
0290 net::execution::is_executor<Executor>::value ||
0291 net::is_executor<Executor>::value,
0292 filesystem::path >::type & executable,
0293 Args && args,
0294 Inits && ... inits ) -> enable_init<Executor, Inits...>
0295 {
0296 auto command_line = this->build_command_line(executable, std::forward<Args>(args));
0297
0298 ec = detail::on_setup(*this, executable, command_line, inits...);
0299
0300 if (ec)
0301 {
0302 detail::on_error(*this, executable, command_line, ec, inits...);
0303 return basic_process<Executor>(exec);
0304 }
0305
0306 if (!inherited_handles.empty())
0307 {
0308 set_handle_list(ec);
0309 if (ec)
0310 return basic_process<Executor>(exec);
0311 }
0312
0313 auto ok = ::CreateProcessW(
0314 executable.empty() ? nullptr : executable.c_str(),
0315 command_line.empty() ? nullptr : &command_line.front(),
0316 process_attributes,
0317 thread_attributes,
0318 inherited_handles.empty() ? FALSE : TRUE,
0319 creation_flags,
0320 environment,
0321 current_directory.empty() ? nullptr : current_directory.c_str(),
0322 &startup_info.StartupInfo,
0323 &process_information);
0324
0325 if (ok == 0)
0326 {
0327 BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
0328 detail::on_error(*this, executable, command_line, ec, inits...);
0329
0330 if (process_information.hProcess != INVALID_HANDLE_VALUE)
0331 ::CloseHandle(process_information.hProcess);
0332 if (process_information.hThread != INVALID_HANDLE_VALUE)
0333 ::CloseHandle(process_information.hThread);
0334
0335 return basic_process<Executor>(exec);
0336 }
0337 else
0338 {
0339 detail::on_success(*this, executable, command_line, inits...);
0340
0341 if (process_information.hThread != INVALID_HANDLE_VALUE)
0342 ::CloseHandle(process_information.hThread);
0343
0344 return basic_process<Executor>(exec,
0345 this->process_information.dwProcessId,
0346 this->process_information.hProcess);
0347 }
0348 }
0349
0350 BOOST_PROCESS_V2_DECL static
0351 std::size_t escaped_argv_length(basic_string_view<wchar_t> ws);
0352 BOOST_PROCESS_V2_DECL static
0353 std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
0354 basic_string_view<wchar_t> ws);
0355
0356
0357
0358
0359 template<typename Argv>
0360 static std::wstring build_command_line_impl(
0361 const filesystem::path & pt,
0362 const Argv & argv,
0363 basic_string_view<wchar_t> args)
0364 {
0365 std::size_t req_size = std::accumulate(
0366 std::begin(argv), std::end(argv), escaped_argv_length(pt.native()),
0367 [](std::size_t sz, basic_string_view<wchar_t> arg) -> std::size_t
0368 {
0369 return sz + 1u + escaped_argv_length(arg);
0370 });
0371
0372 std::wstring res;
0373 res.resize(req_size, L' ');
0374
0375 wchar_t * itr = &res.front();
0376 itr += escape_argv_string(itr, res.size(), pt.native());
0377 for (const auto & a : argv)
0378 {
0379 itr++;
0380 itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a);
0381 }
0382 return res;
0383 }
0384
0385 template<typename Argv>
0386 static std::wstring build_command_line_impl(
0387 const filesystem::path & pt,
0388 const Argv & argv,
0389 basic_string_view<char> args)
0390 {
0391 std::vector<std::wstring> argw;
0392 argw.resize(std::distance(std::begin(argv), std::end(argv)));
0393 std::transform(std::begin(argv), std::end(argv), argw.begin(),
0394 [](basic_string_view <char> arg)
0395 {
0396 return detail::conv_string<wchar_t>(arg.data(), arg.size());
0397 });
0398 return build_command_line_impl(pt, argw, L"");
0399 }
0400
0401 template<typename Args,
0402 typename Char = decltype(*std::begin(std::declval<Args>()))>
0403 static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
0404 {
0405 if (std::begin(args) == std::end(args))
0406 return pt.native();
0407
0408 return build_command_line_impl(pt, args, *std::begin(args));
0409 }
0410
0411 static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
0412 {
0413 return args;
0414 }
0415
0416 struct lpproc_thread_closer
0417 {
0418 void operator()(::LPPROC_THREAD_ATTRIBUTE_LIST l)
0419 {
0420 ::DeleteProcThreadAttributeList(l);
0421 ::HeapFree(GetProcessHeap(), 0, l);
0422 }
0423 };
0424 std::unique_ptr<std::remove_pointer<LPPROC_THREAD_ATTRIBUTE_LIST>::type, lpproc_thread_closer> proc_attribute_list_storage;
0425
0426 BOOST_PROCESS_V2_DECL LPPROC_THREAD_ATTRIBUTE_LIST get_thread_attribute_list(error_code & ec);
0427 BOOST_PROCESS_V2_DECL void set_handle_list(error_code & ec);
0428 };
0429
0430
0431 }
0432 BOOST_PROCESS_V2_END_NAMESPACE
0433
0434
0435
0436 #endif