Chromium Code Reviews| Index: base/test/test_process_killer_win.cc |
| diff --git a/base/test/test_process_killer_win.cc b/base/test/test_process_killer_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bc6e17ef436a2259e463834a87a29c58dad29440 |
| --- /dev/null |
| +++ b/base/test/test_process_killer_win.cc |
| @@ -0,0 +1,165 @@ |
| +// Copyright (c) 2013 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/test/test_process_killer_win.h" |
| + |
| +#include <windows.h> |
| +#include <winternl.h> |
| + |
| +#include <algorithm> |
| + |
| +#include "base/logging.h" |
| +#include "base/process_util.h" |
| +#include "base/string_util.h" |
| +#include "base/win/scoped_handle.h" |
| + |
| +namespace { |
| + |
| +typedef LONG WINAPI |
| +NtQueryInformationProcess( |
| + IN HANDLE ProcessHandle, |
| + IN PROCESSINFOCLASS ProcessInformationClass, |
| + OUT PVOID ProcessInformation, |
| + IN ULONG ProcessInformationLength, |
| + OUT PULONG ReturnLength OPTIONAL |
| +); |
| + |
| +// Get the function pointer to NtQueryInformationProcess in NTDLL.DLL |
| +static bool GetQIP(NtQueryInformationProcess** qip_func_ptr) { |
| + static NtQueryInformationProcess* qip_func = |
| + reinterpret_cast<NtQueryInformationProcess*>( |
| + GetProcAddress(GetModuleHandle(L"ntdll.dll"), |
| + "NtQueryInformationProcess")); |
| + DCHECK(qip_func) << "Could not get pointer to NtQueryInformationProcess."; |
| + *qip_func_ptr = qip_func; |
| + return qip_func != NULL; |
| +} |
| + |
| +// Get the command line of a process |
| +bool GetCommandLineForProcess(uint32 process_id, string16* cmd_line) { |
| + DCHECK(process_id != 0); |
| + DCHECK(cmd_line); |
| + |
| + // Open the process |
| + base::win::ScopedHandle process_handle(::OpenProcess( |
| + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, |
| + false, |
| + process_id)); |
| + if (!process_handle) { |
| + DLOG(ERROR) << "Failed to open process " << process_id << ", last error = " |
| + << GetLastError(); |
| + } |
| + |
| + // Obtain Process Environment Block |
| + NtQueryInformationProcess* qip_func = NULL; |
| + if (process_handle) { |
| + GetQIP(&qip_func); |
| + } |
| + |
| + // Read the address of the process params from the peb. |
| + DWORD process_params_address = 0; |
| + if (qip_func) { |
| + PROCESS_BASIC_INFORMATION info = { 0 }; |
| + // NtQueryInformationProcess returns an NTSTATUS for whom negative values |
| + // are negative. Just check for that instead of pulling in DDK macros. |
| + if ((qip_func(process_handle.Get(), |
| + ProcessBasicInformation, |
| + &info, |
| + sizeof(info), |
| + NULL)) < 0) { |
| + DLOG(ERROR) << "Failed to invoke NtQueryProcessInformation, last error = " |
| + << GetLastError(); |
| + } else { |
| + BYTE* peb = reinterpret_cast<BYTE*>(info.PebBaseAddress); |
| + |
| + // The process command line parameters are (or were once) located at |
| + // the base address of the PEB + 0x10 for 32 bit processes. 64 bit |
| + // processes have a different PEB struct as per |
| + // http://msdn.microsoft.com/en-us/library/aa813706(VS.85).aspx. |
| + // TODO(robertshield): See about doing something about this. |
| + SIZE_T bytes_read = 0; |
| + if (!::ReadProcessMemory(process_handle.Get(), |
| + peb + 0x10, |
| + &process_params_address, |
| + sizeof(process_params_address), |
| + &bytes_read)) { |
| + DLOG(ERROR) << "Failed to read process params address, last error = " |
| + << GetLastError(); |
| + } |
| + } |
| + } |
| + |
| + // Copy all the process parameters into a buffer. |
| + bool success = false; |
| + string16 buffer; |
| + if (process_params_address) { |
| + SIZE_T bytes_read; |
| + RTL_USER_PROCESS_PARAMETERS params = { 0 }; |
| + if (!::ReadProcessMemory(process_handle.Get(), |
| + reinterpret_cast<void*>(process_params_address), |
| + ¶ms, |
| + sizeof(params), |
| + &bytes_read)) { |
| + DLOG(ERROR) << "Failed to read RTL_USER_PROCESS_PARAMETERS, " |
| + << "last error = " << GetLastError(); |
| + } else { |
| + // Read the command line parameter |
| + const int max_cmd_line_len = std::min( |
| + static_cast<int>(params.CommandLine.MaximumLength), |
| + 4096); |
| + buffer.resize(max_cmd_line_len + 1); |
| + if (!::ReadProcessMemory(process_handle.Get(), |
| + params.CommandLine.Buffer, |
| + &buffer[0], |
| + max_cmd_line_len, |
| + &bytes_read)) { |
| + DLOG(ERROR) << "Failed to copy process command line, " |
| + << "last error = " << GetLastError(); |
| + } else { |
| + *cmd_line = buffer; |
| + success = true; |
| + } |
| + } |
| + } |
| + |
| + return success; |
| +} |
| + |
| +// Used to filter processes by process ID. |
| +class ArgumentFilter : public base::ProcessFilter { |
| + public: |
| + explicit ArgumentFilter(const string16& argument) |
| + : argument_to_find_(argument) {} |
| + |
| + // Returns true to indicate set-inclusion and false otherwise. This method |
| + // should not have side-effects and should be idempotent. |
| + virtual bool Includes(const base::ProcessEntry& entry) const { |
| + bool found = false; |
| + string16 command_line; |
| + if (GetCommandLineForProcess(entry.pid(), &command_line)) { |
| + string16::const_iterator it = |
| + std::search(command_line.begin(), |
| + command_line.end(), |
| + argument_to_find_.begin(), |
| + argument_to_find_.end(), |
| + base::CaseInsensitiveCompareASCII<wchar_t>()); |
| + found = (it != command_line.end()); |
| + } |
| + return found; |
| + } |
| + |
| + protected: |
| + string16 argument_to_find_; |
| +}; |
| + |
| +} // namespace |
| + |
| +namespace base { |
| + |
| +bool KillAllNamedProcessesWithArgument(const string16& process_name, |
| + const string16& argument) { |
| + return base::KillProcesses(process_name, 0, &ArgumentFilter(argument)); |
|
Timur Iskhodzhanov
2013/03/18 22:28:47
I think this is a buggy line:
http://crbug.com/219
|
| +} |
| + |
| +} // namespace base |