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