Mercurial > minori
diff dep/animia/src/win/win32.cc @ 152:8700806c2cc2
dep/animia: awesome new breaking changes!
I'm so tired
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Wed, 15 Nov 2023 02:34:59 -0500 |
parents | |
children | bd439dd6ffc5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win/win32.cc Wed Nov 15 02:34:59 2023 -0500 @@ -0,0 +1,171 @@ +#include "animia/win/win32.h" +#include "animia/util/win32.h" +#include "animia/win.h" +#include "animia.h" + +#include <set> +#include <string> + +#include <windows.h> + +namespace animia::internal::win32 { + +static std::wstring GetWindowClassName(HWND hwnd) { + // The maximum size for lpszClassName, according to the documentation of + // WNDCLASSEX structure + constexpr int kMaxSize = 256; + + std::wstring buffer(kMaxSize, L'\0'); + const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length()); + return buffer; +} + +static std::wstring GetWindowText(HWND hwnd) { + const int size = ::GetWindowTextLengthW(hwnd); + + std::wstring buffer(size, L'\0'); + ::GetWindowTextW(hwnd, &buffer.front(), buffer.length()); + return buffer; +} + +static DWORD GetWindowProcessId(HWND hwnd) { + DWORD process_id = 0; + ::GetWindowThreadProcessId(hwnd, &process_id); + return process_id; +} + +static std::wstring GetProcessPath(DWORD process_id) { + // If we try to open a SYSTEM process, this function fails and the last error + // code is ERROR_ACCESS_DENIED. + // + // Note that if we requested PROCESS_QUERY_INFORMATION access right instead + // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used + // to open an elevated process. + Handle process_handle(::OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id)); + + if (!process_handle) + return std::wstring(); + + std::wstring buffer(MAX_PATH, L'\0'); + DWORD buf_size = buffer.length(); + + // Note that this function requires Windows Vista or above. You may use + // GetProcessImageFileName or GetModuleFileNameEx on earlier versions. + if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size)) + return std::wstring(); + + return buffer.substr(0, buf_size + 1); +} + +//////////////////////////////////////////////////////////////////////////////// + +static bool VerifyWindowStyle(HWND hwnd) { + const auto window_style = ::GetWindowLong(hwnd, GWL_STYLE); + const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + + auto has_style = [&window_style](DWORD style) { + return (window_style & style) != 0; + }; + auto has_ex_style = [&window_ex_style](DWORD ex_style) { + return (window_ex_style & ex_style) != 0; + }; + + // Toolbars, tooltips and similar topmost windows + if (has_style(WS_POPUP) && has_ex_style(WS_EX_TOOLWINDOW)) + return false; + if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW)) + return false; + + return true; +} + +static bool VerifyClassName(const std::wstring& name) { + static const std::set<std::wstring> invalid_names = { + // System classes + L"#32770", // Dialog box + L"CabinetWClass", // Windows Explorer + L"ComboLBox", + L"DDEMLEvent", + L"DDEMLMom", + L"DirectUIHWND", + L"GDI+ Hook Window Class", + L"IME", + L"Internet Explorer_Hidden", + L"MSCTFIME UI", + L"tooltips_class32", + }; + + return !name.empty() && !invalid_names.count(name); +} + +static bool VerifyProcessPath(const std::wstring& path) { + return !path.empty() && !IsSystemDirectory(path); +} + +static bool VerifyProcessFileName(const std::wstring& name) { + static const std::set<std::wstring> invalid_names = { + // System files + L"explorer", // Windows Explorer + L"taskeng", // Task Scheduler Engine + L"taskhost", // Host Process for Windows Tasks + L"taskhostex", // Host Process for Windows Tasks + L"Taskmgr", // Task Manager + }; + + return !name.empty() && !invalid_names.count(name); +} + +//////////////////////////////////////////////////////////////////////////////// + +static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) { + if (!::IsWindowVisible(hwnd)) + return TRUE; + + if (!VerifyWindowStyle(hwnd)) + return TRUE; + + Window window; + window.id = static_cast<unsigned int>(reinterpret_cast<ULONG_PTR>(hwnd)); + window.text = ToUtf8String(GetWindowText(hwnd)); + + { + std::wstring class_name = GetWindowClassName(hwnd); + window.class_name = ToUtf8String(class_name); + if (!VerifyClassName(class_name)) + return TRUE; + } + + Process process; + process.pid = GetWindowProcessId(hwnd); + + const auto path = GetProcessPath(process.pid); + if (!VerifyProcessPath(path)) + return TRUE; + + { + std::wstring name = GetFileNameWithoutExtension(GetFileNameFromPath(path)); + process.name = ToUtf8String(name); + if (!VerifyProcessFileName(name)) + return TRUE; + } + + auto& window_proc = *reinterpret_cast<window_proc_t*>(param); + if (!window_proc(process, window)) + return FALSE; + + return TRUE; +} + +bool Win32WinTools::EnumerateWindows(window_proc_t window_proc) { + if (!window_proc) + return false; + + const auto param = reinterpret_cast<LPARAM>(&window_proc); + + // Note that EnumWindows enumerates only top-level windows of desktop apps + // (as opposed to UWP apps) on Windows 8 and above. + return ::EnumWindows(EnumWindowsProc, param) != FALSE; +} + +} // namespace animia::win::detail \ No newline at end of file