blob: 634a538eece513e8218a47a9ae72fb36bb54b005 [file] [log] [blame]
// 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