comparison 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
comparison
equal deleted inserted replaced
151:54744a48a7d7 152:8700806c2cc2
1 #include "animia/win/win32.h"
2 #include "animia/util/win32.h"
3 #include "animia/win.h"
4 #include "animia.h"
5
6 #include <set>
7 #include <string>
8
9 #include <windows.h>
10
11 namespace animia::internal::win32 {
12
13 static std::wstring GetWindowClassName(HWND hwnd) {
14 // The maximum size for lpszClassName, according to the documentation of
15 // WNDCLASSEX structure
16 constexpr int kMaxSize = 256;
17
18 std::wstring buffer(kMaxSize, L'\0');
19 const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length());
20 return buffer;
21 }
22
23 static std::wstring GetWindowText(HWND hwnd) {
24 const int size = ::GetWindowTextLengthW(hwnd);
25
26 std::wstring buffer(size, L'\0');
27 ::GetWindowTextW(hwnd, &buffer.front(), buffer.length());
28 return buffer;
29 }
30
31 static DWORD GetWindowProcessId(HWND hwnd) {
32 DWORD process_id = 0;
33 ::GetWindowThreadProcessId(hwnd, &process_id);
34 return process_id;
35 }
36
37 static std::wstring GetProcessPath(DWORD process_id) {
38 // If we try to open a SYSTEM process, this function fails and the last error
39 // code is ERROR_ACCESS_DENIED.
40 //
41 // Note that if we requested PROCESS_QUERY_INFORMATION access right instead
42 // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used
43 // to open an elevated process.
44 Handle process_handle(::OpenProcess(
45 PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));
46
47 if (!process_handle)
48 return std::wstring();
49
50 std::wstring buffer(MAX_PATH, L'\0');
51 DWORD buf_size = buffer.length();
52
53 // Note that this function requires Windows Vista or above. You may use
54 // GetProcessImageFileName or GetModuleFileNameEx on earlier versions.
55 if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size))
56 return std::wstring();
57
58 return buffer.substr(0, buf_size + 1);
59 }
60
61 ////////////////////////////////////////////////////////////////////////////////
62
63 static bool VerifyWindowStyle(HWND hwnd) {
64 const auto window_style = ::GetWindowLong(hwnd, GWL_STYLE);
65 const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
66
67 auto has_style = [&window_style](DWORD style) {
68 return (window_style & style) != 0;
69 };
70 auto has_ex_style = [&window_ex_style](DWORD ex_style) {
71 return (window_ex_style & ex_style) != 0;
72 };
73
74 // Toolbars, tooltips and similar topmost windows
75 if (has_style(WS_POPUP) && has_ex_style(WS_EX_TOOLWINDOW))
76 return false;
77 if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW))
78 return false;
79
80 return true;
81 }
82
83 static bool VerifyClassName(const std::wstring& name) {
84 static const std::set<std::wstring> invalid_names = {
85 // System classes
86 L"#32770", // Dialog box
87 L"CabinetWClass", // Windows Explorer
88 L"ComboLBox",
89 L"DDEMLEvent",
90 L"DDEMLMom",
91 L"DirectUIHWND",
92 L"GDI+ Hook Window Class",
93 L"IME",
94 L"Internet Explorer_Hidden",
95 L"MSCTFIME UI",
96 L"tooltips_class32",
97 };
98
99 return !name.empty() && !invalid_names.count(name);
100 }
101
102 static bool VerifyProcessPath(const std::wstring& path) {
103 return !path.empty() && !IsSystemDirectory(path);
104 }
105
106 static bool VerifyProcessFileName(const std::wstring& name) {
107 static const std::set<std::wstring> invalid_names = {
108 // System files
109 L"explorer", // Windows Explorer
110 L"taskeng", // Task Scheduler Engine
111 L"taskhost", // Host Process for Windows Tasks
112 L"taskhostex", // Host Process for Windows Tasks
113 L"Taskmgr", // Task Manager
114 };
115
116 return !name.empty() && !invalid_names.count(name);
117 }
118
119 ////////////////////////////////////////////////////////////////////////////////
120
121 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) {
122 if (!::IsWindowVisible(hwnd))
123 return TRUE;
124
125 if (!VerifyWindowStyle(hwnd))
126 return TRUE;
127
128 Window window;
129 window.id = static_cast<unsigned int>(reinterpret_cast<ULONG_PTR>(hwnd));
130 window.text = ToUtf8String(GetWindowText(hwnd));
131
132 {
133 std::wstring class_name = GetWindowClassName(hwnd);
134 window.class_name = ToUtf8String(class_name);
135 if (!VerifyClassName(class_name))
136 return TRUE;
137 }
138
139 Process process;
140 process.pid = GetWindowProcessId(hwnd);
141
142 const auto path = GetProcessPath(process.pid);
143 if (!VerifyProcessPath(path))
144 return TRUE;
145
146 {
147 std::wstring name = GetFileNameWithoutExtension(GetFileNameFromPath(path));
148 process.name = ToUtf8String(name);
149 if (!VerifyProcessFileName(name))
150 return TRUE;
151 }
152
153 auto& window_proc = *reinterpret_cast<window_proc_t*>(param);
154 if (!window_proc(process, window))
155 return FALSE;
156
157 return TRUE;
158 }
159
160 bool Win32WinTools::EnumerateWindows(window_proc_t window_proc) {
161 if (!window_proc)
162 return false;
163
164 const auto param = reinterpret_cast<LPARAM>(&window_proc);
165
166 // Note that EnumWindows enumerates only top-level windows of desktop apps
167 // (as opposed to UWP apps) on Windows 8 and above.
168 return ::EnumWindows(EnumWindowsProc, param) != FALSE;
169 }
170
171 } // namespace animia::win::detail