#include "pch.h" #include "common.h" #include "hwnd_data_cache.h" #include #pragma comment(lib, "dwmapi.lib") #include #include #include "version.h" std::optional get_button_pos(HWND hwnd) { RECT button; if (DwmGetWindowAttribute(hwnd, DWMWA_CAPTION_BUTTON_BOUNDS, &button, sizeof(RECT)) == S_OK) { return button; } else { return {}; } } std::optional get_window_pos(HWND hwnd) { RECT window; if (DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &window, sizeof(window)) == S_OK) { return window; } else { return {}; } } std::optional get_mouse_pos() { POINT point; if (GetCursorPos(&point) == 0) { return {}; } else { return point; } } WindowAndProcPath get_filtered_base_window_and_path(HWND window) { return hwnd_cache.get_window_and_path(window); } HWND get_filtered_active_window() { return hwnd_cache.get_window(GetForegroundWindow()); } int width(const RECT& rect) { return rect.right - rect.left; } int height(const RECT& rect) { return rect.bottom - rect.top; } bool operator<(const RECT& lhs, const RECT& rhs) { auto lhs_tuple = std::make_tuple(lhs.left, lhs.right, lhs.top, lhs.bottom); auto rhs_tuple = std::make_tuple(rhs.left, rhs.right, rhs.top, rhs.bottom); return lhs_tuple < rhs_tuple; } RECT keep_rect_inside_rect(const RECT& small_rect, const RECT& big_rect) { RECT result = small_rect; if ((result.right - result.left) > (big_rect.right - big_rect.left)) { // small_rect is too big horizontally. resize it. result.right = big_rect.right; result.left = big_rect.left; } else { if (result.right > big_rect.right) { // move the rect left. result.left -= result.right-big_rect.right; result.right -= result.right-big_rect.right; } if (result.left < big_rect.left) { // move the rect right. result.right += big_rect.left-result.left; result.left += big_rect.left-result.left; } } if ((result.bottom - result.top) > (big_rect.bottom - big_rect.top)) { // small_rect is too big vertically. resize it. result.bottom = big_rect.bottom; result.top = big_rect.top; } else { if (result.bottom > big_rect.bottom) { // move the rect up. result.top -= result.bottom-big_rect.bottom; result.bottom -= result.bottom-big_rect.bottom; } if (result.top < big_rect.top) { // move the rect down. result.bottom += big_rect.top-result.top; result.top += big_rect.top-result.top; } } return result; } int run_message_loop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return static_cast(msg.wParam); } void show_last_error_message(LPCWSTR lpszFunction, DWORD dw) { // Retrieve the system error message for the error code LPWSTR lpMsgBuf = NULL; if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpMsgBuf, 0, NULL) > 0) { // Display the error message and exit the process LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (lstrlenW(lpMsgBuf) + lstrlenW(lpszFunction) + 40) * sizeof(WCHAR)); if (lpDisplayBuf != NULL) { StringCchPrintfW(lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR), L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf); MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, L"Error", MB_OK); LocalFree(lpDisplayBuf); } LocalFree(lpMsgBuf); } } WindowState get_window_state(HWND hwnd) { WINDOWPLACEMENT placement; placement.length = sizeof(WINDOWPLACEMENT); if (GetWindowPlacement(hwnd, &placement) == 0) { return UNKNONW; } if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED || IsIconic(hwnd)) { return MINIMIZED; } if (placement.showCmd == SW_MAXIMIZE || placement.showCmd == SW_SHOWMAXIMIZED) { return MAXIMIZED; } auto rectp = get_window_pos(hwnd); if (!rectp) { return UNKNONW; } auto rect = *rectp; MONITORINFO monitor; monitor.cbSize = sizeof(MONITORINFO); auto h_monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); GetMonitorInfo(h_monitor, &monitor); bool top_left = monitor.rcWork.top == rect.top && monitor.rcWork.left == rect.left; bool bottom_left = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.left == rect.left; bool top_right = monitor.rcWork.top == rect.top && monitor.rcWork.right == rect.right; bool bottom_right = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.right == rect.right; if (top_left && bottom_left) return SNAPED_LEFT; if (top_left) return SNAPED_TOP_LEFT; if (bottom_left) return SNAPED_BOTTOM_LEFT; if (top_right && bottom_right) return SNAPED_RIGHT; if (top_right) return SNAPED_TOP_RIGHT; if (bottom_right) return SNAPED_BOTTOM_RIGHT; return RESTORED; } bool is_process_elevated() { HANDLE token = nullptr; bool elevated = false; if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { TOKEN_ELEVATION elevation; DWORD size; if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size)) { elevated = (elevation.TokenIsElevated != 0); } } if (token) { CloseHandle(token); } return elevated; } bool drop_elevated_privileges() { HANDLE token = nullptr; LPCTSTR lpszPrivilege = SE_SECURITY_NAME; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT | WRITE_OWNER, &token)) { return false; } PSID medium_sid = NULL; if (!::ConvertStringSidToSid(SDDL_ML_MEDIUM, &medium_sid)) { return false; } TOKEN_MANDATORY_LABEL label = { 0 }; label.Label.Attributes = SE_GROUP_INTEGRITY; label.Label.Sid = medium_sid; DWORD size = (DWORD)sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid); BOOL result = SetTokenInformation(token, TokenIntegrityLevel, &label, size); LocalFree(medium_sid); CloseHandle(token); return result; } std::wstring get_process_path(DWORD pid) noexcept { auto process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, TRUE, pid); std::wstring name; if (process != INVALID_HANDLE_VALUE) { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) { name_length = 0; } name.resize(name_length); CloseHandle(process); } return name; } std::wstring get_process_path(HWND window) noexcept { const static std::wstring app_frame_host = L"ApplicationFrameHost.exe"; DWORD pid{}; GetWindowThreadProcessId(window, &pid); auto name = get_process_path(pid); if (name.length() >= app_frame_host.length() && name.compare(name.length() - app_frame_host.length(), app_frame_host.length(), app_frame_host) == 0) { // It is a UWP app. We will enumarate the windows and look for one created // by something with a different PID DWORD new_pid = pid; EnumChildWindows(window, [](HWND hwnd, LPARAM param) -> BOOL { auto new_pid_ptr = reinterpret_cast(param); DWORD pid; GetWindowThreadProcessId(hwnd, &pid); if (pid != *new_pid_ptr) { *new_pid_ptr = pid; return FALSE; } else { return TRUE; } }, reinterpret_cast(&new_pid)); // If we have a new pid, get the new name. if (new_pid != pid) { return get_process_path(new_pid); } } return name; } std::wstring get_product_version() { static std::wstring version = std::to_wstring(VERSION_MAJOR) + L"." + std::to_wstring(VERSION_MINOR) + L"." + std::to_wstring(VERSION_REVISION) + L"." + std::to_wstring(VERSION_BUILD); return version; }