|  | // 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 |