add for_each_window and for_each_thread_window helpers (#403)
* fix RegistryTests (warning as errors breaking on signed/unsigned comparison) * add for_each_window and for_each_thread_window helpers * . * simplify wrappers, add child window, can work w/mutable lambdas * format * only expose throwing versions when WIL_ENABLE_EXCEPTIONS is set * 'format' * . * . * unused result_t * Update include/wil/windowing.h Co-authored-by: Duncan Horn <40036384+dunhor@users.noreply.github.com> * Update include/wil/windowing.h Co-authored-by: Duncan Horn <40036384+dunhor@users.noreply.github.com> * pr fb --------- Co-authored-by: Duncan Horn <40036384+dunhor@users.noreply.github.com>
This commit is contained in:
Родитель
ce27eed3a9
Коммит
e2a0e70632
|
@ -0,0 +1,164 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// 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_WINDOWING_INCLUDED
|
||||
#define __WIL_WINDOWING_INCLUDED
|
||||
|
||||
#include <WinUser.h>
|
||||
#include <exception>
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
namespace wil
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct always_false : wistd::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename TEnumApi, typename TCallback>
|
||||
void DoEnumWindowsNoThrow(TEnumApi&& enumApi, TCallback&& callback) noexcept
|
||||
{
|
||||
auto enumproc = [](HWND hwnd, LPARAM lParam) -> BOOL {
|
||||
auto pCallback = reinterpret_cast<TCallback*>(lParam);
|
||||
#ifdef __cpp_if_constexpr
|
||||
using result_t = decltype((*pCallback)(hwnd));
|
||||
if constexpr (wistd::is_void_v<result_t>)
|
||||
{
|
||||
(*pCallback)(hwnd);
|
||||
return TRUE;
|
||||
}
|
||||
else if constexpr (wistd::is_same_v<result_t, HRESULT>)
|
||||
{
|
||||
// NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0
|
||||
return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, bool>)
|
||||
{
|
||||
return (*pCallback)(hwnd) ? TRUE : FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(details::always_false<TCallback>::value, "Callback must return void, bool, or HRESULT");
|
||||
}
|
||||
#else
|
||||
return (*pCallback)(hwnd);
|
||||
#endif
|
||||
};
|
||||
enumApi(enumproc, reinterpret_cast<LPARAM>(&callback));
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TEnumApi, typename TCallback>
|
||||
void DoEnumWindows(TEnumApi&& enumApi, TCallback&& callback)
|
||||
{
|
||||
struct
|
||||
{
|
||||
std::exception_ptr exception;
|
||||
TCallback* pCallback;
|
||||
} callbackData = {nullptr, &callback};
|
||||
auto enumproc = [](HWND hwnd, LPARAM lParam) -> BOOL {
|
||||
auto pCallbackData = reinterpret_cast<decltype(&callbackData)>(lParam);
|
||||
try
|
||||
{
|
||||
auto pCallback = pCallbackData->pCallback;
|
||||
#ifdef __cpp_if_constexpr
|
||||
using result_t = decltype((*pCallback)(hwnd));
|
||||
if constexpr (std::is_void_v<result_t>)
|
||||
{
|
||||
(*pCallback)(hwnd);
|
||||
return TRUE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, HRESULT>)
|
||||
{
|
||||
// NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0
|
||||
return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<result_t, bool>)
|
||||
{
|
||||
return (*pCallback)(hwnd) ? TRUE : FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(details::always_false<TCallback>::value, "Callback must return void, bool, or HRESULT");
|
||||
}
|
||||
#else
|
||||
return (*pCallback)(hwnd);
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pCallbackData->exception = std::current_exception();
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
enumApi(enumproc, reinterpret_cast<LPARAM>(&callbackData));
|
||||
if (callbackData.exception)
|
||||
{
|
||||
std::rethrow_exception(callbackData.exception);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace details
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_window_nothrow(TCallback&& callback) noexcept
|
||||
{
|
||||
details::DoEnumWindowsNoThrow(&EnumWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_thread_window_nothrow(_In_ DWORD threadId, TCallback&& callback) noexcept
|
||||
{
|
||||
auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumThreadWindows(threadId, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindowsNoThrow(boundEnumThreadWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_child_window_nothrow(_In_ HWND hwndParent, TCallback&& callback) noexcept
|
||||
{
|
||||
auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumChildWindows(hwndParent, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindowsNoThrow(boundEnumChildWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TCallback>
|
||||
void for_each_window(TCallback&& callback)
|
||||
{
|
||||
details::DoEnumWindows(&EnumWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_thread_window(_In_ DWORD threadId, TCallback&& callback)
|
||||
{
|
||||
auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumThreadWindows(threadId, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindows(boundEnumThreadWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
|
||||
template <typename TCallback>
|
||||
void for_each_child_window(_In_ HWND hwndParent, TCallback&& callback)
|
||||
{
|
||||
auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL {
|
||||
return EnumChildWindows(hwndParent, enumproc, lParam);
|
||||
};
|
||||
details::DoEnumWindows(boundEnumChildWindows, wistd::forward<TCallback>(callback));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace wil
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#endif // __WIL_WINDOWING_INCLUDED
|
|
@ -63,6 +63,7 @@ set(COMMON_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/SafeCastTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests_PartB.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WindowingTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WistdTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/wiTest.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../natvis/wil.natvis
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
#include "common.h"
|
||||
#include <wil/windowing.h>
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WIL_HAS_CXX_17
|
||||
TEST_CASE("EnumWindows", "[windowing]")
|
||||
{
|
||||
// lambda can return a bool
|
||||
wil::for_each_window_nothrow([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
|
||||
// or not return anything (we'll stop if we get an exception)
|
||||
wil::for_each_window_nothrow([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
|
||||
// or return an HRESULT and we'll stop if it's not S_OK
|
||||
wil::for_each_window_nothrow([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
|
||||
// mutable lambda
|
||||
std::vector<HWND> windows;
|
||||
wil::for_each_window_nothrow([&windows](HWND hwnd) {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_window_nothrow([windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
|
||||
// With captures
|
||||
const auto pid = GetCurrentProcessId();
|
||||
wil::for_each_window_nothrow([pid](HWND hwnd) {
|
||||
if (pid == GetWindowThreadProcessId(hwnd, nullptr))
|
||||
{
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
};
|
||||
return true;
|
||||
});
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// throwing version
|
||||
wil::for_each_window([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
wil::for_each_window([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
wil::for_each_window([](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
windows.clear();
|
||||
wil::for_each_window([&windows](HWND hwnd) {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_window([windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
REQUIRE_THROWS_AS(
|
||||
wil::for_each_window([](HWND) {
|
||||
throw std::exception();
|
||||
}),
|
||||
std::exception);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("EnumThreadWindows", "[windowing]")
|
||||
{
|
||||
// find any window
|
||||
DWORD thread_id{};
|
||||
wil::for_each_window_nothrow([&thread_id](HWND hwnd) {
|
||||
if (IsWindow(hwnd) && IsWindowVisible(hwnd))
|
||||
{
|
||||
thread_id = GetWindowThreadProcessId(hwnd, nullptr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// nothrow version
|
||||
{
|
||||
wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
|
||||
wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
|
||||
// lambda can also return an HRESULT and we'll stop if it's not S_OK
|
||||
wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
|
||||
// mutable lambda
|
||||
std::vector<HWND> windows;
|
||||
wil::for_each_thread_window_nothrow(thread_id, [&windows, thread_id](HWND hwnd) {
|
||||
REQUIRE(GetWindowThreadProcessId(hwnd, nullptr) == thread_id);
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_thread_window_nothrow(thread_id, [windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// throwing version
|
||||
{
|
||||
wil::for_each_thread_window(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
|
||||
wil::for_each_thread_window(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
|
||||
// lambda can also return an HRESULT and we'll stop if it's not S_OK
|
||||
wil::for_each_thread_window(thread_id, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
|
||||
// mutable lambda
|
||||
std::vector<HWND> windows;
|
||||
wil::for_each_thread_window(thread_id, [&windows, thread_id](HWND hwnd) {
|
||||
REQUIRE(GetWindowThreadProcessId(hwnd, nullptr) == thread_id);
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_thread_window(thread_id, [windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
|
||||
// with exceptions
|
||||
REQUIRE_THROWS_AS(
|
||||
wil::for_each_thread_window(
|
||||
thread_id,
|
||||
[](HWND) {
|
||||
throw std::exception();
|
||||
}),
|
||||
std::exception);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("EnumChildWindows", "[windowing]")
|
||||
{
|
||||
// find any window
|
||||
HWND parent{};
|
||||
wil::for_each_window_nothrow([&parent](HWND hwnd) {
|
||||
if (IsWindow(hwnd) && IsWindowVisible(hwnd))
|
||||
{
|
||||
parent = hwnd;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// nothrow version
|
||||
{
|
||||
wil::for_each_child_window_nothrow(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
|
||||
wil::for_each_child_window_nothrow(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
|
||||
// lambda can also return an HRESULT and we'll stop if it's not S_OK
|
||||
wil::for_each_child_window_nothrow(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
|
||||
// mutable lambda
|
||||
std::vector<HWND> windows;
|
||||
wil::for_each_child_window_nothrow(parent, [&windows](HWND hwnd) {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_child_window_nothrow(parent, [windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// throwing version
|
||||
{
|
||||
wil::for_each_child_window(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return true;
|
||||
});
|
||||
|
||||
wil::for_each_child_window(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
});
|
||||
|
||||
// lambda can also return an HRESULT and we'll stop if it's not S_OK
|
||||
wil::for_each_child_window(parent, [](HWND hwnd) {
|
||||
REQUIRE(IsWindow(hwnd));
|
||||
return S_FALSE;
|
||||
});
|
||||
|
||||
// mutable lambda
|
||||
std::vector<HWND> windows;
|
||||
wil::for_each_child_window(parent, [&windows](HWND hwnd) {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
wil::for_each_child_window(parent, [windows = std::vector<HWND>{}](HWND hwnd) mutable {
|
||||
windows.push_back(hwnd);
|
||||
});
|
||||
|
||||
// with exceptions
|
||||
REQUIRE_THROWS_AS(
|
||||
wil::for_each_child_window(
|
||||
parent,
|
||||
[](HWND) {
|
||||
throw std::exception();
|
||||
}),
|
||||
std::exception);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче