// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
#include <string>
// List all thread names in a process specified.
BOOL ListProcessThreadNames(DWORD owner_pid);
// Print the error message.
void printError(TCHAR* msg);
// The GetThreadDescription API is available since Windows 10, version 1607.
// The reason why this API is bound in this way rather than just using the
// Windows SDK, is that this API isn't yet available in the SDK that Chrome
// builds with.
// Binding SetThreadDescription API in Chrome can only be done by
// GetProcAddress, rather than the import library.
typedef HRESULT(WINAPI* GETTHREADDESCRIPTION)(HANDLE hThread,
PWSTR* threadDescription);
int main(void) {
DWORD process_Id;
std::string user_input;
while (true) {
std::cout
<< "\nPlease enter the process Id, or \"quit\" to end the program : ";
std::getline(std::cin, user_input);
// Convert the user input to lower case.
std::transform(user_input.begin(), user_input.end(), user_input.begin(),
::tolower);
if (user_input == "quit")
break;
std::cout << std::endl;
std::stringstream ss(user_input);
if (ss >> process_Id) {
ListProcessThreadNames(process_Id);
} else {
std::cout << "Input is invalid" << std::endl;
}
std::cout << std::endl;
}
return 0;
}
BOOL ListProcessThreadNames(DWORD owner_pid) {
auto get_thread_description_func =
reinterpret_cast<GETTHREADDESCRIPTION>(::GetProcAddress(
::GetModuleHandle(L"Kernel32.dll"), "GetThreadDescription"));
if (!get_thread_description_func) {
printError(TEXT("GetThreadDescription"));
return (FALSE);
}
HANDLE thread_snapshot = INVALID_HANDLE_VALUE;
// Take a snapshot of all running threads.
thread_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (thread_snapshot == INVALID_HANDLE_VALUE) {
printError(TEXT("CreateToolhelp32Snapshot"));
return (FALSE);
}
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread, and exit if unsuccessful.
if (!Thread32First(thread_snapshot, &te32)) {
printError(TEXT("Thread32First"));
CloseHandle(thread_snapshot);
return (FALSE);
}
// Walk the thread list of the system, and display ID and name about each
// thread associated with the process specified.
std::cout << "thread_ID thread_name" << std::endl;
std::multimap<std::wstring, DWORD> name_id_map;
do {
if (te32.th32OwnerProcessID == owner_pid) {
HANDLE thread_handle =
OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID);
if (thread_handle) {
PWSTR data;
HRESULT hr = get_thread_description_func(thread_handle, &data);
if (SUCCEEDED(hr)) {
std::wstring thread_name(data);
LocalFree(data);
name_id_map.insert(std::make_pair(thread_name, te32.th32ThreadID));
} else {
printError(TEXT("GetThreadDescription"));
}
CloseHandle(thread_handle);
} else {
printError(TEXT("OpenThread"));
}
}
} while (Thread32Next(thread_snapshot, &te32));
// Clean up the snapshot object.
CloseHandle(thread_snapshot);
// Show all thread ID/name pairs.
for (auto name_id_pair : name_id_map) {
std::cout << name_id_pair.second << "\t";
std::wcout << name_id_pair.first << std::endl;
}
return (TRUE);
}
void printError(TCHAR* msg) {
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), sysMsg,
256, NULL);
// Trim the end of the line and terminate it with a null.
p = sysMsg;
while ((*p > 31) || (*p == 9))
++p;
do {
*p-- = 0;
} while ((p >= sysMsg) && ((*p == '.') || (*p < 33)));
// Display the message.
_tprintf(TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum,
sysMsg);
}