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