Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Toolhelp32Snapshot helpers #438

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,4 @@ ASALocalRun/

# CMake/Build output
build/
/CMakeSettings.json
337 changes: 337 additions & 0 deletions include/wil/toolhelp32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TOOLHELP32_INCLUDED
#define __WIL_TOOLHELP32_INCLUDED
#include <TlHelp32.h>
#include <processthreadsapi.h>
#include "resource.h"
namespace wil
{
namespace details
{
template <typename TEntry, typename TEnumApi, typename TCallback>
auto do_enum_snapshot(HANDLE handle, TEntry& entry, TEnumApi&& enumApiFirst, TEnumApi&& enumApiNext, TCallback&& callback)
{
if (handle == INVALID_HANDLE_VALUE)
return E_HANDLE;

using result_t = decltype(callback(TEntry{}));
bool enumResult = enumApiFirst(handle, &entry);
if (!enumResult)
return E_ABORT;

do
{
if constexpr (wistd::is_void_v<result_t>)
{
callback(entry);
}
else if constexpr (wistd::is_same_v<result_t, bool>)
{
if (callback(entry))
return S_OK;
}
else
{
static_assert(
[] {
return false;
}(),
"Callback must return void or bool");
}
enumResult = enumApiNext(handle, &entry);
} while (enumResult);
return S_OK;
}
} // namespace details

#pragma region Process
template <typename TCallback>
auto for_each_process_nothrow(TCallback&& callback)
{
PROCESSENTRY32 entry{};
entry.dwSize = sizeof(entry);
return details::do_enum_snapshot(
unique_handle{CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)}.get(),
HO-COOH marked this conversation as resolved.
Show resolved Hide resolved
entry,
&Process32First,
&Process32Next,
wistd::forward<TCallback>(callback));
}

template <typename TCallback>
void for_each_process_failfast(TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_process_nothrow(callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TCallback>
void for_each_process(TCallback&& callback)
{
THROW_IF_FAILED(for_each_process_nothrow(callback));
}
#endif
#pragma endregion

#pragma region Thread
template <typename TCallback>
auto for_each_system_thread_nothrow(TCallback&& callback)
{
THREADENTRY32 entry{};
entry.dwSize = sizeof(entry);
return details::do_enum_snapshot(
unique_handle{CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)}.get(),
entry,
&Thread32First,
&Thread32Next,
wistd::forward<TCallback>(callback));
}

template <typename TCallback>
void for_each_system_thread_failfast(TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_system_thread_nothrow(callback));
}

template <typename TCallback>
auto for_each_process_thread_nothrow(DWORD pid, TCallback&& callback)
{
return for_each_system_thread_nothrow([&](THREADENTRY32 const& entry) {
if (entry.th32OwnerProcessID == pid)
callback(entry);
});
}

template <typename TCallback>
auto for_each_process_thread_nothrow(TCallback&& callback)
{
return for_each_process_thread_nothrow(GetCurrentProcessId(), callback);
}

template <typename TCallback>
void for_each_process_thread_failfast(DWORD pid, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_process_thread_nothrow(pid, callback));
}

template <typename TCallback>
void for_each_process_thread_failfast(TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_process_thread_nothrow(callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TCallback>
void for_each_system_thread(TCallback&& callback)
{
THROW_IF_FAILED(for_each_system_thread_nothrow(callback));
}

template <typename TCallback>
void for_each_process_thread(DWORD pid, TCallback&& callback)
{
THROW_IF_FAILED(for_each_process_thread_nothrow(pid, callback));
}

template <typename TCallback>
void for_each_process_thread(TCallback&& callback)
{
THROW_IF_FAILED(for_each_process_thread_nothrow(callback));
}

#endif
#pragma endregion

#pragma region Module
template <typename TCallback>
auto for_each_module_nothrow(DWORD pid, bool include32BitModule, TCallback&& callback)
{
MODULEENTRY32 entry{};
entry.dwSize = sizeof(entry);
return details::do_enum_snapshot(
unique_handle{CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | (include32BitModule ? TH32CS_SNAPMODULE32 : 0), pid)}.get(),
entry,
&Module32First,
&Module32Next,
wistd::forward<TCallback>(callback));
}

template <typename TCallback>
auto for_each_module_nothrow(bool include32BitModule, TCallback&& callback)
{
return for_each_module_nothrow(0, include32BitModule, callback);
}

template <typename TCallback>
auto for_each_module_nothrow(TCallback&& callback)
{
return for_each_module_nothrow(true, callback);
}

template <typename TCallback>
void for_each_module_failfast(DWORD pid, bool include32BitModule, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_module_nothrow(pid, include32BitModule, callback));
}

template <typename TCallback>
void for_each_module_failfast(bool include32BitModule, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_module_nothrow(0, include32BitModule, callback));
}

template <typename TCallback>
void for_each_module_failfast(TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_module_nothrow(callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TCallback>
void for_each_module(DWORD pid, bool include32BitModule, TCallback&& callback)
{
THROW_IF_FAILED(for_each_module_nothrow(pid, include32BitModule, callback));
}

template <typename TCallback>
void for_each_module(bool include32BitModule, TCallback&& callback)
{
THROW_IF_FAILED(for_each_module_nothrow(0, include32BitModule, callback));
}

template <typename TCallback>
void for_each_module(TCallback&& callback)
{
THROW_IF_FAILED(for_each_module_nothrow(callback));
}
#endif
#pragma endregion

#pragma region HeapList
template <typename TCallback>
auto for_each_heap_list_nothrow(DWORD pid, TCallback&& callback)
{
HEAPLIST32 entry{};
entry.dwSize = sizeof(entry);
return details::do_enum_snapshot(
unique_handle{CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, pid)}.get(),
entry,
&Heap32ListFirst,
&Heap32ListNext,
wistd::forward<TCallback>(callback));
}

template <typename TCallback>
auto for_each_heap_list_nothrow(TCallback&& callback)
{
return for_each_heap_list_nothrow(0, callback);
}

template <typename TCallback>
void for_each_heap_list_failfast(DWORD pid, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_heap_list_nothrow(pid, callback));
}

template <typename TCallback>
void for_each_heap_list_failfast(TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_heap_list_nothrow(callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TCallback>
void for_each_heap_list(DWORD pid, TCallback&& callback)
{
THROW_IF_FAILED(for_each_heap_list_nothrow(pid, callback));
}

template <typename TCallback>
void for_each_heap_list(TCallback&& callback)
{
THROW_IF_FAILED(for_each_heap_list_nothrow(callback));
}
#endif
#pragma endregion

#pragma region Heap
template <typename TCallback>
auto for_each_heap_nothrow(DWORD pid, ULONG_PTR heapId, TCallback&& callback)
{
using result_t = decltype(callback(HEAPENTRY32{}));

HEAPENTRY32 entry{};
entry.dwSize = sizeof(entry);

if (!Heap32First(&entry, pid, heapId))
return E_ABORT;

bool enumResult = true;
do
{
if constexpr (wistd::is_void_v<result_t>)
{
callback(entry);
}
else if constexpr (wistd::is_same_v<result_t, bool>)
{
if (callback(entry))
return S_OK;
}
else
{
static_assert(
[] {
return false;
}(),
"Callback must return void or bool");
}
enumResult = Heap32Next(&entry);
} while (enumResult);
return S_OK;
}

template <typename TCallback>
auto for_each_heap_nothrow(ULONG_PTR heapId, TCallback&& callback)
{
return for_each_heap_nothrow(GetCurrentProcessId(), heapId, callback);
}

template <typename TCallback>
void for_each_heap_failfast(DWORD pid, ULONG_PTR heapId, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_heap_nothrow(pid, heapId, callback));
}

template <typename TCallback>
void for_each_heap_failfast(ULONG_PTR heapId, TCallback&& callback)
{
FAIL_FAST_IF_FAILED(for_each_heap_nothrow(heapId, callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TCallback>
void for_each_heap(DWORD pid, ULONG_PTR heapId, TCallback&& callback)
{
THROW_IF_FAILED(for_each_heap_nothrow(pid, heapId, callback));
}

template <typename TCallback>
void for_each_heap(ULONG_PTR heapId, TCallback&& callback)
{
THROW_IF_FAILED(for_each_heap_nothrow(heapId, callback));
}
#endif
#pragma endregion
} // namespace wil

#endif
46 changes: 46 additions & 0 deletions tests/Toolhelp32Tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "common.h"
#include <WinUser.h>
#include <wil/toolhelp32.h>
#include <cstring>

TEST_CASE("Toolhelp32", "[EnumProcesses]")
{
wil::for_each_process([](PROCESSENTRY32 const& entry) {
REQUIRE_FALSE(std::strlen(entry.szExeFile) == 0);
});
}

TEST_CASE("Toolhelp32", "[EnumModules]")
{
wil::for_each_module([](MODULEENTRY32 const& entry) {
REQUIRE_FALSE(std::strlen(entry.szExePath) == 0);
});
}

TEST_CASE("Toolhelp32", "[EnumThreads]")
{
wil::for_each_system_thread([pid = GetCurrentProcessId()](THREADENTRY32 const& entry) {
if (entry.th32OwnerProcessID == pid)
{
REQUIRE_FALSE(entry.th32ThreadID == 0);
}
});
}

TEST_CASE("Toolhelp32", "[EnumHeapLists]")
{
wil::for_each_heap_list([](HEAPLIST32 const& entry) {
REQUIRE_FALSE(entry.th32HeapID == 0);
});
}

TEST_CASE("Toolhelp32", "[EnumHeap]")
{
wil::for_each_heap_list([](HEAPLIST32 const& heapListEntry) {
REQUIRE_FALSE(heapListEntry.th32HeapID == 0);
wil::for_each_heap(heapListEntry.th32HeapID, [](HEAPENTRY32 const& heapEntry) {
REQUIRE_FALSE(heapEntry.dwAddress == 0);
});
return false;
});
}
1 change: 1 addition & 0 deletions tests/cpplatest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ target_sources(witest.cpplatest PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Toolhelp32Tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/app.manifest
)
Loading
Loading