aboutsummaryrefslogtreecommitdiff
path: root/src/base/platform/win/Win32SubProcess.cpp
blob: c97f3d666da190de537bf89328cf974a0f0fc660 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "cru/base/platform/win/Win32SubProcess.h"
#include <processthreadsapi.h>
#include <synchapi.h>
#include "cru/base/StringUtil.h"
#include "cru/base/SubProcess.h"

#include <memory>
#include <string_view>

namespace cru::platform::win {
using cru::string::ToUtf16;

Win32SubProcessImpl::Win32SubProcessImpl() : exit_code_(0) {}

Win32SubProcessImpl::~Win32SubProcessImpl() {}

void Win32SubProcessImpl::PlatformCreateProcess(
    const SubProcessStartInfo& start_info) {
  auto check_error = [](int error, std::string message) {
    if (error == 0) return;
    std::unique_ptr<ErrnoException> inner(new ErrnoException(error));
    throw SubProcessFailedToStartException(std::move(message),
                                           std::move(inner));
  };

  auto app = ToUtf16(start_info.program);
  // TODO: Space and quoting problem.
  auto command_line =
      app + L" " + ToUtf16(cru::string::Join(" ", start_info.arguments));

  std::wstring env_str;
  for (const auto& [key, value] : start_info.environments) {
    env_str += ToUtf16(key) + L"=" + ToUtf16(value) + L"\0";
  }
  env_str += L"\0";

  STARTUPINFO startup_info;
  ZeroMemory(&startup_info, sizeof(startup_info));
  startup_info.cb = sizeof(startup_info);

  auto my_stdin = OpenUniDirectionalPipe(Win32PipeFlags::ReadInheritable);
  auto my_stdout = OpenUniDirectionalPipe(Win32PipeFlags::WriteInheritable);
  auto my_stderr = OpenUniDirectionalPipe(Win32PipeFlags::WriteInheritable);
  startup_info.dwFlags = STARTF_USESTDHANDLES;
  startup_info.hStdInput = my_stdin.read.Get();
  startup_info.hStdOutput = my_stdout.write.Get();
  startup_info.hStdError = my_stderr.write.Get();

  CheckWinReturn(::CreateProcessW(app.c_str(), command_line.data(), nullptr,
                                  nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT,
                                  static_cast<LPVOID>(env_str.data()), nullptr,
                                  &startup_info, &process_));

  stdin_stream_ = std::make_unique<Win32HandleStream>(std::move(my_stdin.write),
                                                      false, false, true);
  stdout_stream_ = std::make_unique<Win32HandleStream>(
      std::move(my_stdout.read), false, true, false);
  stderr_stream_ = std::make_unique<Win32HandleStream>(
      std::move(my_stderr.read), false, true, false);

  stdout_buffer_stream_ =
      std::make_unique<io::AutoReadStream>(stdout_stream_.get(), true, false);
  stderr_buffer_stream_ =
      std::make_unique<io::AutoReadStream>(stderr_stream_.get(), true, false);
}

SubProcessExitResult Win32SubProcessImpl::PlatformWaitForProcess() {
  ::WaitForSingleObject(process_.hProcess, INFINITE);

  DWORD exit_code;
  CheckWinReturn(::GetExitCodeProcess(process_.hProcess, &exit_code));

  return SubProcessExitResult::Normal(exit_code);
}

void Win32SubProcessImpl::PlatformKillProcess() {
  CheckWinReturn(TerminateProcess(process_.hProcess, -1));
}

io::Stream* Win32SubProcessImpl::GetStdinStream() {
  return stdin_stream_.get();
}

io::Stream* Win32SubProcessImpl::GetStdoutStream() {
  return stdout_buffer_stream_.get();
}

io::Stream* Win32SubProcessImpl::GetStderrStream() {
  return stderr_buffer_stream_.get();
}
}  // namespace cru::platform::win