summaryrefslogtreecommitdiff
path: root/base/win/scoped_process_information.cc
blob: 634a538eece513e8218a47a9ae72fb36bb54b005 (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/win/scoped_process_information.h"

#include "base/logging.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"

namespace base {
namespace win {

namespace {

// Duplicates source into target, returning true upon success. |target| is
// guaranteed to be untouched in case of failure. Succeeds with no side-effects
// if source is NULL.
bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) {
  if (!source)
    return true;

  HANDLE temp = NULL;

  // TODO(shrikant): Remove following code as soon as we gather some
  // information regarding AppContainer related DuplicateHandle failures that
  // only seem to happen on certain machine and only random launches (normally
  // renderer launches seem to succeed even on those machines.)
  if (base::win::GetVersion() == base::win::VERSION_WIN8 ||
      base::win::GetVersion() == base::win::VERSION_WIN8_1) {
    typedef LONG (WINAPI *NtDuplicateObject)(
        IN HANDLE SourceProcess,
        IN HANDLE SourceHandle,
        IN HANDLE TargetProcess,
        OUT PHANDLE TargetHandle,
        IN ACCESS_MASK DesiredAccess,
        IN ULONG Attributes,
        IN ULONG Options);

    typedef ULONG (WINAPI *RtlNtStatusToDosError)(IN LONG Status);

    NtDuplicateObject nt_duplicate_object =
        reinterpret_cast<NtDuplicateObject>(::GetProcAddress(
            GetModuleHandle(L"ntdll.dll"), "NtDuplicateObject"));
    if (nt_duplicate_object != NULL) {
      LONG status = nt_duplicate_object(::GetCurrentProcess(), source,
                                        ::GetCurrentProcess(), &temp,
                                        0, FALSE, DUPLICATE_SAME_ACCESS);
      if (status < 0) {
        DPLOG(ERROR) << "Failed to duplicate a handle.";
        RtlNtStatusToDosError ntstatus_to_doserror =
            reinterpret_cast<RtlNtStatusToDosError>(::GetProcAddress(
                GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError"));
        if (ntstatus_to_doserror != NULL) {
          ::SetLastError(ntstatus_to_doserror(status));
        }
        return false;
      }
    }
  } else {
    if (!::DuplicateHandle(::GetCurrentProcess(), source,
                           ::GetCurrentProcess(), &temp, 0, FALSE,
                           DUPLICATE_SAME_ACCESS)) {
      DPLOG(ERROR) << "Failed to duplicate a handle.";
      return false;
    }
  }
  target->Set(temp);
  return true;
}

}  // namespace

ScopedProcessInformation::ScopedProcessInformation()
    : process_id_(0), thread_id_(0) {
}

ScopedProcessInformation::ScopedProcessInformation(
    const PROCESS_INFORMATION& process_info) : process_id_(0), thread_id_(0) {
  Set(process_info);
}

ScopedProcessInformation::~ScopedProcessInformation() {
  Close();
}

bool ScopedProcessInformation::IsValid() const {
  return process_id_ || process_handle_.Get() ||
         thread_id_ || thread_handle_.Get();
}

void ScopedProcessInformation::Close() {
  process_handle_.Close();
  thread_handle_.Close();
  process_id_ = 0;
  thread_id_ = 0;
}

void ScopedProcessInformation::Set(const PROCESS_INFORMATION& process_info) {
  if (IsValid())
    Close();

  process_handle_.Set(process_info.hProcess);
  thread_handle_.Set(process_info.hThread);
  process_id_ = process_info.dwProcessId;
  thread_id_ = process_info.dwThreadId;
}

bool ScopedProcessInformation::DuplicateFrom(
    const ScopedProcessInformation& other) {
  DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
  DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";

  if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) &&
      CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) {
    process_id_ = other.process_id();
    thread_id_ = other.thread_id();
    return true;
  }

  return false;
}

PROCESS_INFORMATION ScopedProcessInformation::Take() {
  PROCESS_INFORMATION process_information = {};
  process_information.hProcess = process_handle_.Take();
  process_information.hThread = thread_handle_.Take();
  process_information.dwProcessId = process_id();
  process_information.dwThreadId = thread_id();
  process_id_ = 0;
  thread_id_ = 0;

  return process_information;
}

HANDLE ScopedProcessInformation::TakeProcessHandle() {
  process_id_ = 0;
  return process_handle_.Take();
}

HANDLE ScopedProcessInformation::TakeThreadHandle() {
  thread_id_ = 0;
  return thread_handle_.Take();
}

}  // namespace win
}  // namespace base