6 #include <winapi/cmd_line.hpp>
8 #include <winapi/handle.hpp>
9 #include <winapi/process.hpp>
10 #include <winapi/process_io.hpp>
11 #include <winapi/resource.hpp>
12 #include <winapi/utf8.hpp>
31 using EscapedCommandLine = std::vector<wchar_t>;
33 EscapedCommandLine escape_command_line(
const CommandLine& cmd_line) {
34 const auto unicode_cmd_line = widen(cmd_line.to_string());
35 EscapedCommandLine buffer;
36 buffer.reserve(unicode_cmd_line.size() + 1);
37 buffer.assign(unicode_cmd_line.cbegin(), unicode_cmd_line.cend());
38 buffer.emplace_back(L
'\0');
42 Handle create_process(ProcessParameters& params) {
62 static constexpr DWORD default_dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
64 STARTUPINFOW startup_info;
65 std::memset(&startup_info, 0,
sizeof(startup_info));
66 startup_info.cb =
sizeof(startup_info);
69 startup_info.dwFlags |= STARTF_USESTDHANDLES;
70 startup_info.hStdInput =
static_cast<HANDLE
>(params.io->std_in.handle);
71 startup_info.hStdOutput =
static_cast<HANDLE
>(params.io->std_out.handle);
72 startup_info.hStdError =
static_cast<HANDLE
>(params.io->std_err.handle);
75 auto dwCreationFlags = default_dwCreationFlags;
77 switch (params.console_mode) {
78 case ProcessParameters::ConsoleNone:
79 dwCreationFlags |= CREATE_NO_WINDOW;
81 case ProcessParameters::ConsoleInherit:
84 case ProcessParameters::ConsoleNew:
85 dwCreationFlags |= CREATE_NEW_CONSOLE;
89 PROCESS_INFORMATION child_info;
90 std::memset(&child_info, 0,
sizeof(child_info));
93 auto cmd_line = escape_command_line(params.cmd_line);
95 const auto ret = ::CreateProcessW(NULL,
107 throw error::windows(GetLastError(),
"CreateProcessW");
115 Handle process{child_info.hProcess};
116 Handle thread{child_info.hThread};
121 Handle shell_execute(
const ShellParameters& params) {
122 const auto lpVerb = params.verb ? widen(*params.verb) : L
"open";
123 const auto lpFile = widen(params.cmd_line.get_argv0());
124 const auto lpParameters = widen(params.cmd_line.args_to_string());
126 static constexpr uint32_t default_fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
128 auto fMask = default_fMask;
129 auto nShow = SW_SHOWDEFAULT;
131 switch (params.console_mode) {
132 case ProcessParameters::ConsoleNone:
135 case ProcessParameters::ConsoleInherit:
136 fMask |= SEE_MASK_NO_CONSOLE;
138 case ProcessParameters::ConsoleNew:
143 SHELLEXECUTEINFOW info;
144 std::memset(&info, 0,
sizeof(info));
145 info.cbSize =
sizeof(info);
147 info.lpVerb = lpVerb.c_str();
148 info.lpFile = lpFile.c_str();
149 if (!lpParameters.empty())
150 info.lpParameters = lpParameters.c_str();
153 if (!::ShellExecuteExW(&info)) {
154 throw error::windows(GetLastError(),
"ShellExecuteExW");
157 return Handle{info.hProcess};
160 Handle open_process(DWORD
id, DWORD permissions) {
161 Handle process{OpenProcess(permissions, FALSE,
id)};
162 if (!process.is_valid()) {
163 throw error::windows(GetLastError(),
"OpenProcess");
170 PathBuffer() : m_size{min_size} { m_data.resize(m_size); }
172 DWORD get_size()
const {
return m_size; }
174 wchar_t* get_data() {
return m_data.data(); }
177 if (m_size < min_size) {
181 if (std::numeric_limits<decltype(m_size)>::max() - m_size < m_size)
182 throw std::range_error{
"Path buffer is too large"};
185 m_data.resize(m_size);
189 static constexpr DWORD min_size = MAX_PATH;
192 std::vector<wchar_t> m_data;
195 std::string get_current_exe_path(PathBuffer& buffer) {
196 SetLastError(ERROR_SUCCESS);
198 const auto ec = ::GetModuleFileNameW(NULL, buffer.get_data(), buffer.get_size());
201 throw error::windows(GetLastError(),
"GetModuleFileNameW");
204 if (ec == buffer.get_size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
206 return get_current_exe_path(buffer);
209 return narrow(buffer.get_data());
212 std::string get_current_exe_path() {
214 return get_current_exe_path(buffer);
217 std::string get_exe_path(
const Handle& process, PathBuffer& buffer) {
218 auto size = buffer.get_size();
220 const auto ec = ::QueryFullProcessImageNameW(process.get(), 0, buffer.get_data(), &size);
223 return narrow(buffer.get_data());
226 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
228 return get_exe_path(process, buffer);
231 throw error::windows(GetLastError(),
"QueryFullProcessImageNameW");
234 std::string get_exe_path(
const Handle& process) {
236 return get_exe_path(process, buffer);
242 return Process{create_process(params)};
247 return create(std::move(params));
252 params.io = std::move(io);
253 return create(std::move(params));
257 return Process{shell_execute(params)};
262 return shell(params);
266 return Process{::GetCurrentProcessId(),
Handle{::GetCurrentProcess()}};
270 return Process{id, open_process(
id, permissions)};
278 return PROCESS_QUERY_INFORMATION;
286 const auto ret = ::WaitForSingleObject(
static_cast<HANDLE
>(m_handle), 0);
294 throw error::windows(GetLastError(),
"WaitForSingleObject");
297 throw error::custom(ret,
"WaitForSingleObject");
302 const auto ret = ::WaitForSingleObject(
static_cast<HANDLE
>(m_handle), INFINITE);
308 throw error::windows(GetLastError(),
"WaitForSingleObject");
311 throw error::custom(ret,
"WaitForSingleObject");
316 if (!::TerminateProcess(
static_cast<HANDLE
>(m_handle),
static_cast<UINT
>(ec))) {
317 throw error::windows(GetLastError(),
"TerminateProcess");
329 const auto ret = ::GetExitCodeProcess(
static_cast<HANDLE
>(m_handle), &ec);
332 throw error::windows(GetLastError(),
"GetExitCodeProcess");
335 if (ec == STILL_ACTIVE) {
336 throw std::runtime_error{
"Attempted to query the exit code of a running process"};
339 return static_cast<int>(ec);
343 if (m_handle.get() == ::GetCurrentProcess()) {
344 return get_current_exe_path();
346 return winapi::get_exe_path(m_handle);
350 HMODULE Process::get_exe_module() {
351 const auto module = ::GetModuleHandleW(NULL);
352 if (module == NULL) {
353 throw error::windows(GetLastError(),
"GetModuleHandleW");
359 wchar_t* s =
nullptr;
361 const auto nch = ::LoadStringW(get_exe_module(),
id,
reinterpret_cast<wchar_t*
>(&s), 0);
364 throw error::windows(GetLastError(),
"LoadStringW");
367 return narrow(s, nch *
sizeof(
wchar_t));
371 const auto module = get_exe_module();
373 const auto src = ::FindResourceA(module, MAKEINTRESOURCEA(
id), RT_RCDATA);
376 throw error::windows(GetLastError(),
"FindResourceA");
379 const auto resource = ::LoadResource(module, src);
381 if (resource == NULL) {
382 throw error::windows(GetLastError(),
"LoadResource");
385 const auto data = ::LockResource(resource);
388 std::ostringstream oss;
389 oss <<
"Couldn't get data pointer for resource with ID " << id;
390 throw std::runtime_error{oss.str()};
393 const auto nb = ::SizeofResource(module, src);
398 Process::Process(
Handle&& handle) :
Process{::GetProcessId(handle.get()), std::move(handle)} {}
400 Process::Process(ID
id, Handle&& handle) : m_id{id}, m_handle{std::move(handle)} {}
Command line for the current process or for launching new processes.
Create a new process or open an existing process.
void terminate(int ec=0) const
void shut_down(int ec=0) const
int get_exit_code() const
static Process open(ID id, DWORD permissions=default_permissions())
static Resource get_resource(uint32_t id)
static DWORD read_permissions()
static DWORD default_permissions()
static Process create(ProcessParameters)
std::string get_exe_path() const
static Process shell(const ShellParameters &)
static std::string get_resource_string(uint32_t id)
static Process open_r(ID)
Make std::system_error work with GetLastError().
Process parameters for Process::create().
Resources embedded in a PE (Portable Executable).
Process parameters for Process::shell().
Child process IO settings.