258
+ − 1 /*
+ − 2 * win/win32.cc: support for Windows
+ − 3 *
+ − 4 * Surprisingly, this is the one time where Microsoft actually
+ − 5 * does it fairly OK. Everything has a pretty simple API, despite
+ − 6 * the stupid wide string stuff.
+ − 7 */
+ − 8 #include "animone/win/win32.h"
+ − 9 #include "animone.h"
+ − 10 #include "animone/util/win32.h"
+ − 11 #include "animone/win.h"
+ − 12
+ − 13 #include <set>
+ − 14 #include <string>
+ − 15
+ − 16 #include <windows.h>
+ − 17
+ − 18 namespace animone::internal::win32 {
+ − 19
+ − 20 static std::wstring GetWindowClassName(HWND hwnd) {
+ − 21 static constexpr int kMaxSize = 256;
+ − 22
+ − 23 std::wstring buffer(kMaxSize, L'\0');
+ − 24 const auto size = ::GetClassNameW(hwnd, &buffer.front(), buffer.length());
+ − 25 buffer.resize(size);
+ − 26 return buffer;
+ − 27 }
+ − 28
+ − 29 static std::wstring GetWindowText(HWND hwnd) {
+ − 30 const auto estimated_size = ::GetWindowTextLengthW(hwnd);
+ − 31 std::wstring buffer(estimated_size + 1, L'\0');
+ − 32
+ − 33 const auto size = ::GetWindowTextW(hwnd, &buffer.front(), buffer.length());
+ − 34 /* GetWindowTextLength docs:
+ − 35 * "Under certain conditions, the GetWindowTextLength function may return a value
+ − 36 * that is larger than the actual length of the text." */
+ − 37 buffer.resize(size);
+ − 38 return buffer;
+ − 39 }
+ − 40
+ − 41 static DWORD GetWindowProcessId(HWND hwnd) {
+ − 42 DWORD process_id = 0;
+ − 43 ::GetWindowThreadProcessId(hwnd, &process_id);
+ − 44 return process_id;
+ − 45 }
+ − 46
+ − 47 ////////////////////////////////////////////////////////////////////////////////
+ − 48
+ − 49 static bool VerifyWindowStyle(HWND hwnd) {
+ − 50 const auto window_style = ::GetWindowLong(hwnd, GWL_STYLE);
+ − 51 const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
+ − 52
+ − 53 auto has_style = [&window_style](DWORD style) { return (window_style & style) != 0; };
+ − 54 auto has_ex_style = [&window_ex_style](DWORD ex_style) { return (window_ex_style & ex_style) != 0; };
+ − 55
+ − 56 // Toolbars, tooltips and similar topmost windows
+ − 57 if (has_style(WS_POPUP) && has_ex_style(WS_EX_TOOLWINDOW))
+ − 58 return false;
+ − 59 if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW))
+ − 60 return false;
+ − 61
+ − 62 return true;
+ − 63 }
+ − 64
+ − 65 static bool VerifyClassName(const std::wstring& name) {
+ − 66 static const std::set<std::wstring> invalid_names = {
+ − 67 // System classes
+ − 68 L"#32770", // Dialog box
+ − 69 L"CabinetWClass", // Windows Explorer
+ − 70 L"ComboLBox",
+ − 71 L"DDEMLEvent",
+ − 72 L"DDEMLMom",
+ − 73 L"DirectUIHWND",
+ − 74 L"GDI+ Hook Window Class",
+ − 75 L"IME",
+ − 76 L"Internet Explorer_Hidden",
+ − 77 L"MSCTFIME UI",
+ − 78 L"tooltips_class32",
+ − 79 };
+ − 80
+ − 81 return !name.empty() && !invalid_names.count(name);
+ − 82 }
+ − 83
+ − 84 static bool VerifyProcessPath(const std::wstring& path) {
+ − 85 return !path.empty() && !IsSystemDirectory(path);
+ − 86 }
+ − 87
+ − 88 static bool VerifyProcessFileName(const std::wstring& name) {
+ − 89 static const std::set<std::wstring> invalid_names = {
+ − 90 // System files
+ − 91 L"explorer", // Windows Explorer
+ − 92 L"taskeng", // Task Scheduler Engine
+ − 93 L"taskhost", // Host Process for Windows Tasks
+ − 94 L"taskhostex", // Host Process for Windows Tasks
+ − 95 L"Taskmgr", // Task Manager
+ − 96 };
+ − 97
+ − 98 return !name.empty() && !invalid_names.count(name);
+ − 99 }
+ − 100
+ − 101 ////////////////////////////////////////////////////////////////////////////////
+ − 102
+ − 103 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) {
+ − 104 if (!::IsWindowVisible(hwnd))
+ − 105 return TRUE;
+ − 106
+ − 107 if (!VerifyWindowStyle(hwnd))
+ − 108 return TRUE;
+ − 109
+ − 110 Window window;
+ − 111 window.id = static_cast<unsigned int>(reinterpret_cast<ULONG_PTR>(hwnd));
+ − 112 window.text = ToUtf8String(GetWindowText(hwnd));
+ − 113
+ − 114 {
+ − 115 std::wstring class_name = GetWindowClassName(hwnd);
+ − 116 window.class_name = ToUtf8String(class_name);
+ − 117 if (!VerifyClassName(class_name))
+ − 118 return TRUE;
+ − 119 }
+ − 120
+ − 121 Process process;
+ − 122 process.pid = GetWindowProcessId(hwnd);
+ − 123 process.name = fd::GetProcessName(process.pid)
+ − 124
+ − 125 auto& window_proc = *reinterpret_cast<window_proc_t*>(param);
+ − 126 if (!window_proc(process, window))
+ − 127 return FALSE;
+ − 128
+ − 129 return TRUE;
+ − 130 }
+ − 131
+ − 132 bool EnumerateWindows(window_proc_t window_proc) {
+ − 133 if (!window_proc)
+ − 134 return false;
+ − 135
+ − 136 const auto param = reinterpret_cast<LPARAM>(&window_proc);
+ − 137
+ − 138 // Note that EnumWindows enumerates only top-level windows of desktop apps
+ − 139 // (as opposed to UWP apps) on Windows 8 and above.
+ − 140 return ::EnumWindows(EnumWindowsProc, param) != FALSE;
+ − 141 }
+ − 142
+ − 143 } // namespace animone::internal::win32