From def5c50960eea4c112647f88361a3ae7155901a8 Mon Sep 17 00:00:00 2001 From: egor-tensin Date: Tue, 4 Jul 2023 00:48:00 +0000 Subject: =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20egor-tensin/wina?= =?UTF-8?q?pi-common@0c196cbe8b4927c78c02b2c7312fc69a507db845=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process_8cpp_source.html | 503 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 process_8cpp_source.html (limited to 'process_8cpp_source.html') diff --git a/process_8cpp_source.html b/process_8cpp_source.html new file mode 100644 index 0000000..575b3c2 --- /dev/null +++ b/process_8cpp_source.html @@ -0,0 +1,503 @@ + + + + + + + +winapi_common: src/process.cpp Source File + + + + + + + + + +
+
+ + + + + + +
+
winapi_common +
+
+
+ + + + + + + + +
+
+ + +
+ +
+ + +
+
+
+
process.cpp
+
+
+
1 // Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+
2 // This file is part of the "winapi-common" project.
+
3 // For details, see https://github.com/egor-tensin/winapi-common.
+
4 // Distributed under the MIT License.
+
5 
+
6 #include <winapi/cmd_line.hpp>
+
7 #include <winapi/error.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>
+
13 
+
14 // clang-format off
+
15 #include <windows.h>
+
16 #include <shellapi.h>
+
17 // clang-format on
+
18 
+
19 #include <cstddef>
+
20 #include <cstring>
+
21 #include <limits>
+
22 #include <sstream>
+
23 #include <stdexcept>
+
24 #include <string>
+
25 #include <utility>
+
26 #include <vector>
+
27 
+
28 namespace winapi {
+
29 namespace {
+
30 
+
31 using EscapedCommandLine = std::vector<wchar_t>;
+
32 
+
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');
+
39  return buffer;
+
40 }
+
41 
+
42 Handle create_process(ProcessParameters& params) {
+
43  /*
+
44  * When creating a new console process, the options are:
+
45  * 1) inherit the parent console (the default),
+
46  * 2) CREATE_NO_WINDOW,
+
47  * 3) CREATE_NEW_CONSOLE,
+
48  * 4) DETACHED_PROCESS.
+
49  *
+
50  * Child processes can inherit the console.
+
51  * By that I mean they will display their output in the same window.
+
52  * If both the child process and the parent process read from stdin, there
+
53  * is no way to say which process will read any given input byte.
+
54  *
+
55  * There's an excellent guide into all the intricacies of the CreateProcess
+
56  * system call at
+
57  *
+
58  * https://github.com/rprichard/win32-console-docs/blob/master/README.md
+
59  *
+
60  * Another useful link is https://ikriv.com/dev/cpp/ConsoleProxy/flags.
+
61  */
+
62  static constexpr DWORD default_dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
+
63 
+
64  STARTUPINFOW startup_info;
+
65  std::memset(&startup_info, 0, sizeof(startup_info));
+
66  startup_info.cb = sizeof(startup_info);
+
67 
+
68  if (params.io) {
+
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);
+
73  }
+
74 
+
75  auto dwCreationFlags = default_dwCreationFlags;
+
76 
+
77  switch (params.console_mode) {
+
78  case ProcessParameters::ConsoleNone:
+
79  dwCreationFlags |= CREATE_NO_WINDOW;
+
80  break;
+
81  case ProcessParameters::ConsoleInherit:
+
82  // This is the default.
+
83  break;
+
84  case ProcessParameters::ConsoleNew:
+
85  dwCreationFlags |= CREATE_NEW_CONSOLE;
+
86  break;
+
87  }
+
88 
+
89  PROCESS_INFORMATION child_info;
+
90  std::memset(&child_info, 0, sizeof(child_info));
+
91 
+
92  {
+
93  auto cmd_line = escape_command_line(params.cmd_line);
+
94 
+
95  const auto ret = ::CreateProcessW(NULL,
+
96  cmd_line.data(),
+
97  NULL,
+
98  NULL,
+
99  TRUE,
+
100  dwCreationFlags,
+
101  NULL,
+
102  NULL,
+
103  &startup_info,
+
104  &child_info);
+
105 
+
106  if (!ret) {
+
107  throw error::windows(GetLastError(), "CreateProcessW");
+
108  }
+
109  }
+
110 
+
111  if (params.io) {
+
112  params.io->close();
+
113  }
+
114 
+
115  Handle process{child_info.hProcess};
+
116  Handle thread{child_info.hThread};
+
117 
+
118  return process;
+
119 }
+
120 
+
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());
+
125 
+
126  static constexpr uint32_t default_fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
+
127 
+
128  auto fMask = default_fMask;
+
129  auto nShow = SW_SHOWDEFAULT;
+
130 
+
131  switch (params.console_mode) {
+
132  case ProcessParameters::ConsoleNone:
+
133  nShow = SW_HIDE;
+
134  break;
+
135  case ProcessParameters::ConsoleInherit:
+
136  fMask |= SEE_MASK_NO_CONSOLE;
+
137  break;
+
138  case ProcessParameters::ConsoleNew:
+
139  // This is the default.
+
140  break;
+
141  }
+
142 
+
143  SHELLEXECUTEINFOW info;
+
144  std::memset(&info, 0, sizeof(info));
+
145  info.cbSize = sizeof(info);
+
146  info.fMask = fMask;
+
147  info.lpVerb = lpVerb.c_str();
+
148  info.lpFile = lpFile.c_str();
+
149  if (!lpParameters.empty())
+
150  info.lpParameters = lpParameters.c_str();
+
151  info.nShow = nShow;
+
152 
+
153  if (!::ShellExecuteExW(&info)) {
+
154  throw error::windows(GetLastError(), "ShellExecuteExW");
+
155  }
+
156 
+
157  return Handle{info.hProcess};
+
158 }
+
159 
+
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");
+
164  }
+
165  return process;
+
166 }
+
167 
+
168 class PathBuffer {
+
169 public:
+
170  PathBuffer() : m_size{min_size} { m_data.resize(m_size); }
+
171 
+
172  DWORD get_size() const { return m_size; }
+
173 
+
174  wchar_t* get_data() { return m_data.data(); }
+
175 
+
176  void grow() {
+
177  if (m_size < min_size) {
+
178  m_size = min_size;
+
179  } else {
+
180  // Check if we can still multiply by two.
+
181  if (std::numeric_limits<decltype(m_size)>::max() - m_size < m_size)
+
182  throw std::range_error{"Path buffer is too large"};
+
183  m_size *= 2;
+
184  }
+
185  m_data.resize(m_size);
+
186  }
+
187 
+
188 private:
+
189  static constexpr DWORD min_size = MAX_PATH;
+
190 
+
191  DWORD m_size;
+
192  std::vector<wchar_t> m_data;
+
193 };
+
194 
+
195 std::string get_current_exe_path(PathBuffer& buffer) {
+
196  SetLastError(ERROR_SUCCESS);
+
197 
+
198  const auto ec = ::GetModuleFileNameW(NULL, buffer.get_data(), buffer.get_size());
+
199 
+
200  if (ec == 0) {
+
201  throw error::windows(GetLastError(), "GetModuleFileNameW");
+
202  }
+
203 
+
204  if (ec == buffer.get_size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+
205  buffer.grow();
+
206  return get_current_exe_path(buffer);
+
207  }
+
208 
+
209  return narrow(buffer.get_data());
+
210 }
+
211 
+
212 std::string get_current_exe_path() {
+
213  PathBuffer buffer;
+
214  return get_current_exe_path(buffer);
+
215 }
+
216 
+
217 std::string get_exe_path(const Handle& process, PathBuffer& buffer) {
+
218  auto size = buffer.get_size();
+
219 
+
220  const auto ec = ::QueryFullProcessImageNameW(process.get(), 0, buffer.get_data(), &size);
+
221 
+
222  if (ec != 0) {
+
223  return narrow(buffer.get_data());
+
224  }
+
225 
+
226  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+
227  buffer.grow();
+
228  return get_exe_path(process, buffer);
+
229  }
+
230 
+
231  throw error::windows(GetLastError(), "QueryFullProcessImageNameW");
+
232 }
+
233 
+
234 std::string get_exe_path(const Handle& process) {
+
235  PathBuffer buffer;
+
236  return get_exe_path(process, buffer);
+
237 }
+
238 
+
239 } // namespace
+
240 
+ +
242  return Process{create_process(params)};
+
243 }
+
244 
+ +
246  ProcessParameters params{cmd_line};
+
247  return create(std::move(params));
+
248 }
+
249 
+ +
251  ProcessParameters params{cmd_line};
+
252  params.io = std::move(io);
+
253  return create(std::move(params));
+
254 }
+
255 
+ +
257  return Process{shell_execute(params)};
+
258 }
+
259 
+ +
261  ShellParameters params{cmd_line};
+
262  return shell(params);
+
263 }
+
264 
+ +
266  return Process{::GetCurrentProcessId(), Handle{::GetCurrentProcess()}};
+
267 }
+
268 
+
269 Process Process::open(DWORD id, DWORD permissions) {
+
270  return Process{id, open_process(id, permissions)};
+
271 }
+
272 
+ +
274  return open(id, read_permissions());
+
275 }
+
276 
+ +
278  return PROCESS_QUERY_INFORMATION;
+
279 }
+
280 
+ +
282  return default_permissions() | PROCESS_VM_READ;
+
283 }
+
284 
+
285 bool Process::is_running() const {
+
286  const auto ret = ::WaitForSingleObject(static_cast<HANDLE>(m_handle), 0);
+
287 
+
288  switch (ret) {
+
289  case WAIT_OBJECT_0:
+
290  return false;
+
291  case WAIT_TIMEOUT:
+
292  return true;
+
293  case WAIT_FAILED:
+
294  throw error::windows(GetLastError(), "WaitForSingleObject");
+
295  default:
+
296  // Shouldn't happen.
+
297  throw error::custom(ret, "WaitForSingleObject");
+
298  }
+
299 }
+
300 
+
301 void Process::wait() const {
+
302  const auto ret = ::WaitForSingleObject(static_cast<HANDLE>(m_handle), INFINITE);
+
303 
+
304  switch (ret) {
+
305  case WAIT_OBJECT_0:
+
306  return;
+
307  case WAIT_FAILED:
+
308  throw error::windows(GetLastError(), "WaitForSingleObject");
+
309  default:
+
310  // Shouldn't happen.
+
311  throw error::custom(ret, "WaitForSingleObject");
+
312  }
+
313 }
+
314 
+
315 void Process::terminate(int ec) const {
+
316  if (!::TerminateProcess(static_cast<HANDLE>(m_handle), static_cast<UINT>(ec))) {
+
317  throw error::windows(GetLastError(), "TerminateProcess");
+
318  }
+
319 }
+
320 
+
321 void Process::shut_down(int ec) const {
+
322  terminate(ec);
+
323  wait();
+
324 }
+
325 
+ +
327  DWORD ec = 0;
+
328 
+
329  const auto ret = ::GetExitCodeProcess(static_cast<HANDLE>(m_handle), &ec);
+
330 
+
331  if (!ret) {
+
332  throw error::windows(GetLastError(), "GetExitCodeProcess");
+
333  }
+
334 
+
335  if (ec == STILL_ACTIVE) {
+
336  throw std::runtime_error{"Attempted to query the exit code of a running process"};
+
337  }
+
338 
+
339  return static_cast<int>(ec);
+
340 }
+
341 
+
342 std::string Process::get_exe_path() const {
+
343  if (m_handle.get() == ::GetCurrentProcess()) {
+
344  return get_current_exe_path();
+
345  } else {
+
346  return winapi::get_exe_path(m_handle);
+
347  }
+
348 }
+
349 
+
350 HMODULE Process::get_exe_module() {
+
351  const auto module = ::GetModuleHandleW(NULL);
+
352  if (module == NULL) {
+
353  throw error::windows(GetLastError(), "GetModuleHandleW");
+
354  }
+
355  return module;
+
356 }
+
357 
+
358 std::string Process::get_resource_string(uint32_t id) {
+
359  wchar_t* s = nullptr;
+
360 
+
361  const auto nch = ::LoadStringW(get_exe_module(), id, reinterpret_cast<wchar_t*>(&s), 0);
+
362 
+
363  if (nch <= 0) {
+
364  throw error::windows(GetLastError(), "LoadStringW");
+
365  }
+
366 
+
367  return narrow(s, nch * sizeof(wchar_t));
+
368 }
+
369 
+ +
371  const auto module = get_exe_module();
+
372 
+
373  const auto src = ::FindResourceA(module, MAKEINTRESOURCEA(id), RT_RCDATA);
+
374 
+
375  if (src == NULL) {
+
376  throw error::windows(GetLastError(), "FindResourceA");
+
377  }
+
378 
+
379  const auto resource = ::LoadResource(module, src);
+
380 
+
381  if (resource == NULL) {
+
382  throw error::windows(GetLastError(), "LoadResource");
+
383  }
+
384 
+
385  const auto data = ::LockResource(resource);
+
386 
+
387  if (data == NULL) {
+
388  std::ostringstream oss;
+
389  oss << "Couldn't get data pointer for resource with ID " << id;
+
390  throw std::runtime_error{oss.str()};
+
391  }
+
392 
+
393  const auto nb = ::SizeofResource(module, src);
+
394 
+
395  return {data, nb};
+
396 }
+
397 
+
398 Process::Process(Handle&& handle) : Process{::GetProcessId(handle.get()), std::move(handle)} {}
+
399 
+
400 Process::Process(ID id, Handle&& handle) : m_id{id}, m_handle{std::move(handle)} {}
+
401 
+
402 } // namespace winapi
+
Command line for the current process or for launching new processes.
Definition: cmd_line.hpp:21
+
HANDLE wrapper.
Definition: handle.hpp:25
+
Create a new process or open an existing process.
Definition: process.hpp:54
+
void terminate(int ec=0) const
Definition: process.cpp:315
+
void shut_down(int ec=0) const
Definition: process.cpp:321
+
void wait() const
Definition: process.cpp:301
+
int get_exit_code() const
Definition: process.cpp:326
+
static Process open(ID id, DWORD permissions=default_permissions())
Definition: process.cpp:269
+
static Resource get_resource(uint32_t id)
Definition: process.cpp:370
+
static Process current()
Definition: process.cpp:265
+
static DWORD read_permissions()
Definition: process.cpp:281
+
static DWORD default_permissions()
Definition: process.cpp:277
+
bool is_running() const
Definition: process.cpp:285
+
static Process create(ProcessParameters)
Definition: process.cpp:241
+
std::string get_exe_path() const
Definition: process.cpp:342
+
static Process shell(const ShellParameters &)
Definition: process.cpp:256
+
static std::string get_resource_string(uint32_t id)
Definition: process.cpp:358
+
static Process open_r(ID)
Definition: process.cpp:273
+
Make std::system_error work with GetLastError().
+
Process parameters for Process::create().
Definition: process.hpp:24
+
Resources embedded in a PE (Portable Executable).
Definition: resource.hpp:15
+
Process parameters for Process::shell().
Definition: process.hpp:39
+
Child process IO settings.
Definition: process_io.hpp:61
+
+ + + + -- cgit v1.2.3