mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-11 09:49:22 +08:00
3443c73d0e
* [ARM64] Cleanup PowerToys solution configuration * [ARM64] Add Bootstrapper support known issues: - wix doesn't have arm platform -> using x86 instead, resulting in wrong default installation dir and inability to detect 605 - current winappsdk 1.0.3 installer is corrupted -> contains x64 packages - we're still using hardcoded vcredist for some modules * Remove duplicate Stylecop PackageReference * [ARM64] Cleanup StylesReportTool configuration * [ARM64] fix configurations for bootstrapper * fixes * [ARM64] VCM fix * [ARM64] Fix LinkIncremental conditional in StylesReportTool * fix #2 * perfect forwarding * [ARM64] Switch to call vformat to compile with later MSVC toolkits * Updated expect Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>
426 lines
15 KiB
C++
426 lines
15 KiB
C++
#include "pch.h"
|
|
#include "StylesReportTool.h"
|
|
|
|
#include <dwmapi.h>
|
|
#include <shlobj.h>
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <map>
|
|
#include <format>
|
|
|
|
inline std::optional<std::wstring> get_last_error_message(const DWORD dw)
|
|
{
|
|
std::optional<std::wstring> message;
|
|
try
|
|
{
|
|
const auto msg = std::system_category().message(dw);
|
|
message.emplace(begin(msg), end(msg));
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
return message;
|
|
}
|
|
|
|
inline std::wstring get_last_error_or_default(const DWORD dw)
|
|
{
|
|
auto message = get_last_error_message(dw);
|
|
return message.has_value() ? message.value() : L"";
|
|
}
|
|
|
|
std::filesystem::path get_desktop_path()
|
|
{
|
|
wchar_t* p;
|
|
if (S_OK != SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &p)) return "";
|
|
|
|
std::filesystem::path result = p;
|
|
CoTaskMemFree(p);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Get the executable path or module name for modern apps
|
|
inline std::wstring get_process_path(DWORD pid) noexcept
|
|
{
|
|
auto process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid);
|
|
std::wstring name;
|
|
if (process != INVALID_HANDLE_VALUE)
|
|
{
|
|
name.resize(MAX_PATH);
|
|
DWORD name_length = static_cast<DWORD>(name.length());
|
|
if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0)
|
|
{
|
|
name_length = 0;
|
|
}
|
|
name.resize(name_length);
|
|
CloseHandle(process);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
// Get the executable path or module name for modern apps
|
|
inline 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 enumerate 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<DWORD*>(param);
|
|
DWORD pid;
|
|
GetWindowThreadProcessId(hwnd, &pid);
|
|
if (pid != *new_pid_ptr)
|
|
{
|
|
*new_pid_ptr = pid;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
},
|
|
reinterpret_cast<LPARAM>(&new_pid));
|
|
|
|
// If we have a new pid, get the new name.
|
|
if (new_pid != pid)
|
|
{
|
|
return get_process_path(new_pid);
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
class Logger
|
|
{
|
|
private:
|
|
inline static std::wofstream logger;
|
|
|
|
public:
|
|
~Logger()
|
|
{
|
|
logger.close();
|
|
}
|
|
|
|
static void init(std::string loggerName)
|
|
{
|
|
std::filesystem::path rootFolder(get_desktop_path());
|
|
|
|
auto logsPath = rootFolder;
|
|
logsPath.append(L"window_styles.txt");
|
|
|
|
logger.open(logsPath.string(), std::ios_base::out | std::ios_base::app);
|
|
}
|
|
|
|
template<typename FormatString, typename... Args>
|
|
static void log(FormatString fmt, Args&&... args)
|
|
{
|
|
logger << std::vformat(fmt, std::make_wformat_args(args...)) << std::endl;
|
|
}
|
|
};
|
|
|
|
std::map<DWMWINDOWATTRIBUTE, std::wstring> dwmAttributesReadable = {
|
|
{DWMWINDOWATTRIBUTE::DWMWA_NCRENDERING_ENABLED, L"DWMWA_NCRENDERING_ENABLED"},
|
|
{DWMWINDOWATTRIBUTE::DWMWA_CAPTION_BUTTON_BOUNDS, L"DWMWA_CAPTION_BUTTON_BOUNDS"},
|
|
{DWMWINDOWATTRIBUTE::DWMWA_EXTENDED_FRAME_BOUNDS, L"DWMWA_EXTENDED_FRAME_BOUNDS"},
|
|
{DWMWINDOWATTRIBUTE::DWMWA_CLOAKED, L"DWMWA_CLOAKED"},
|
|
};
|
|
|
|
template <typename T>
|
|
void LogDwmInfo(HWND window, DWMWINDOWATTRIBUTE attr, T& value)
|
|
{
|
|
if (DwmGetWindowAttribute(window, attr, &value, sizeof(value)) == S_OK)
|
|
{
|
|
Logger::log(L"{}: {} ", dwmAttributesReadable[attr], value);
|
|
}
|
|
else
|
|
{
|
|
Logger::log(L"Failed to get {}", dwmAttributesReadable[attr]);
|
|
}
|
|
}
|
|
|
|
void LogDwmRect(HWND window, DWMWINDOWATTRIBUTE attr, RECT& value)
|
|
{
|
|
if (DwmGetWindowAttribute(window, attr, &value, sizeof(value)) >= 0)
|
|
{
|
|
Logger::log(L"{}: LT({},{}), RB({},{}), [{} x {}] ", dwmAttributesReadable[attr], value.left, value.top, value.right, value.bottom, value.right - value.left, value.bottom - value.top);
|
|
}
|
|
else
|
|
{
|
|
Logger::log(L"Failed to get {}", dwmAttributesReadable[attr]);
|
|
}
|
|
}
|
|
|
|
void LogStyles(HWND window)
|
|
{
|
|
auto style = GetWindowLong(window, GWL_STYLE);
|
|
|
|
Logger::log(L"------------------ Style --------------------- ");
|
|
Logger::log(L"");
|
|
|
|
Logger::log(L"WS_BORDER {}", ((style & WS_BORDER) == WS_BORDER));
|
|
Logger::log(L"WS_CAPTION {}", ((style & WS_CAPTION) == WS_CAPTION));
|
|
Logger::log(L"WS_CHILD {}", ((style & WS_CHILD) == WS_CHILD));
|
|
Logger::log(L"WS_CHILDWINDOW {}", ((style & WS_CHILDWINDOW) == WS_CHILDWINDOW));
|
|
Logger::log(L"WS_CLIPCHILDREN {}", ((style & WS_CLIPCHILDREN) == WS_CLIPCHILDREN));
|
|
Logger::log(L"WS_CLIPSIBLINGS {}", ((style & WS_CLIPSIBLINGS) == WS_CLIPSIBLINGS));
|
|
Logger::log(L"WS_DISABLED {}", ((style & WS_DISABLED) == WS_DISABLED));
|
|
Logger::log(L"WS_DLGFRAME {}", ((style & WS_DLGFRAME) == WS_DLGFRAME));
|
|
Logger::log(L"WS_GROUP {}", ((style & WS_GROUP) == WS_GROUP));
|
|
Logger::log(L"WS_HSCROLL {}", ((style & WS_HSCROLL) == WS_HSCROLL));
|
|
Logger::log(L"WS_ICONIC {}", ((style & WS_ICONIC) == WS_ICONIC));
|
|
Logger::log(L"WS_MAXIMIZE {}", ((style & WS_MAXIMIZE) == WS_MAXIMIZE));
|
|
Logger::log(L"WS_MAXIMIZEBOX {}", ((style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX));
|
|
Logger::log(L"WS_MINIMIZE {}", ((style & WS_MINIMIZE) == WS_MINIMIZE));
|
|
Logger::log(L"WS_MINIMIZEBOX {}", ((style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX));
|
|
Logger::log(L"WS_OVERLAPPED {}", ((style & WS_OVERLAPPED) == WS_OVERLAPPED));
|
|
Logger::log(L"WS_OVERLAPPEDWINDOW {}", ((style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW));
|
|
Logger::log(L"WS_POPUP {}", ((style & WS_POPUP) == WS_POPUP));
|
|
Logger::log(L"WS_POPUPWINDOW {}", ((style & WS_POPUPWINDOW) == WS_POPUPWINDOW));
|
|
Logger::log(L"WS_SIZEBOX {}", ((style & WS_SIZEBOX) == WS_SIZEBOX));
|
|
Logger::log(L"WS_SYSMENU {}", ((style & WS_SYSMENU) == WS_SYSMENU));
|
|
Logger::log(L"WS_TABSTOP {}", ((style & WS_TABSTOP) == WS_TABSTOP));
|
|
Logger::log(L"WS_THICKFRAME {}", ((style & WS_THICKFRAME) == WS_THICKFRAME));
|
|
Logger::log(L"WS_TILED {}", ((style & WS_TILED) == WS_TILED));
|
|
Logger::log(L"WS_TILEDWINDOW {}", ((style & WS_TILEDWINDOW) == WS_TILEDWINDOW));
|
|
Logger::log(L"WS_VISIBLE {}", ((style & WS_VISIBLE) == WS_VISIBLE));
|
|
Logger::log(L"WS_VSCROLL {}", ((style & WS_VSCROLL) == WS_VSCROLL));
|
|
|
|
Logger::log(L"");
|
|
}
|
|
|
|
void LogExStyles(HWND window)
|
|
{
|
|
auto exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
|
Logger::log(L"------------------ Exstyle --------------------- ");
|
|
Logger::log(L"");
|
|
|
|
Logger::log(L"WS_EX_ACCEPTFILES {}", (exStyle & WS_EX_ACCEPTFILES) == WS_EX_ACCEPTFILES);
|
|
Logger::log(L"WS_EX_APPWINDOW {}", (exStyle & WS_EX_APPWINDOW) == WS_EX_APPWINDOW);
|
|
Logger::log(L"WS_EX_CLIENTEDGE {}", (exStyle & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE);
|
|
Logger::log(L"WS_EX_COMPOSITED {}", (exStyle & WS_EX_COMPOSITED) == WS_EX_COMPOSITED);
|
|
Logger::log(L"WS_EX_CONTEXTHELP {}", (exStyle & WS_EX_CONTEXTHELP) == WS_EX_CONTEXTHELP);
|
|
Logger::log(L"WS_EX_CONTROLPARENT {}", (exStyle & WS_EX_CONTROLPARENT) == WS_EX_CONTROLPARENT);
|
|
Logger::log(L"WS_EX_DLGMODALFRAME {}", (exStyle & WS_EX_DLGMODALFRAME) == WS_EX_DLGMODALFRAME);
|
|
Logger::log(L"WS_EX_LAYERED {}", (exStyle & WS_EX_LAYERED) == WS_EX_LAYERED);
|
|
Logger::log(L"WS_EX_LAYOUTRTL {}", (exStyle & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL);
|
|
Logger::log(L"WS_EX_LEFT {}", (exStyle & WS_EX_LEFT) == WS_EX_LEFT);
|
|
Logger::log(L"WS_EX_LEFTSCROLLBAR {}", (exStyle & WS_EX_LEFTSCROLLBAR) == WS_EX_LEFTSCROLLBAR);
|
|
Logger::log(L"WS_EX_LTRREADING {}", (exStyle & WS_EX_LTRREADING) == WS_EX_LTRREADING);
|
|
Logger::log(L"WS_EX_MDICHILD {}", (exStyle & WS_EX_MDICHILD) == WS_EX_MDICHILD);
|
|
Logger::log(L"WS_EX_NOACTIVATE {}", (exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE);
|
|
Logger::log(L"WS_EX_NOINHERITLAYOUT {}", (exStyle & WS_EX_NOINHERITLAYOUT) == WS_EX_NOINHERITLAYOUT);
|
|
Logger::log(L"WS_EX_NOPARENTNOTIFY {}", (exStyle & WS_EX_NOPARENTNOTIFY) == WS_EX_NOPARENTNOTIFY);
|
|
Logger::log(L"WS_EX_NOREDIRECTIONBITMAP {}", (exStyle & WS_EX_NOREDIRECTIONBITMAP) == WS_EX_NOREDIRECTIONBITMAP);
|
|
Logger::log(L"WS_EX_OVERLAPPEDWINDOW {}", (exStyle & WS_EX_OVERLAPPEDWINDOW) == WS_EX_OVERLAPPEDWINDOW);
|
|
Logger::log(L"WS_EX_PALETTEWINDOW {}", (exStyle & WS_EX_PALETTEWINDOW) == WS_EX_PALETTEWINDOW);
|
|
Logger::log(L"WS_EX_RIGHT {}", (exStyle & WS_EX_RIGHT) == WS_EX_RIGHT);
|
|
Logger::log(L"WS_EX_RIGHTSCROLLBAR {}", (exStyle & WS_EX_RIGHTSCROLLBAR) == WS_EX_RIGHTSCROLLBAR);
|
|
Logger::log(L"WS_EX_RTLREADING {}", (exStyle & WS_EX_RTLREADING) == WS_EX_RTLREADING);
|
|
Logger::log(L"WS_EX_STATICEDGE {}", (exStyle & WS_EX_STATICEDGE) == WS_EX_STATICEDGE);
|
|
Logger::log(L"WS_EX_TOOLWINDOW {}", (exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW);
|
|
Logger::log(L"WS_EX_TOPMOST {}", (exStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST);
|
|
Logger::log(L"WS_EX_TRANSPARENT {}", (exStyle & WS_EX_TRANSPARENT) == WS_EX_TRANSPARENT);
|
|
Logger::log(L"WS_EX_WINDOWEDGE {}", (exStyle & WS_EX_WINDOWEDGE) == WS_EX_WINDOWEDGE);
|
|
|
|
Logger::log(L"");
|
|
}
|
|
|
|
void LogDwmAttributes(HWND window)
|
|
{
|
|
Logger::log(L"------------------ DwmAttributes --------------------- ");
|
|
Logger::log(L"");
|
|
|
|
int intValue{};
|
|
unsigned int uintValue{};
|
|
|
|
LogDwmInfo(window, DWMWINDOWATTRIBUTE::DWMWA_NCRENDERING_ENABLED, intValue);
|
|
LogDwmInfo(window, DWMWINDOWATTRIBUTE::DWMWA_CLOAKED, intValue);
|
|
|
|
RECT rectValue{};
|
|
LogDwmRect(window, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_BUTTON_BOUNDS, rectValue);
|
|
LogDwmRect(window, DWMWINDOWATTRIBUTE::DWMWA_EXTENDED_FRAME_BOUNDS, rectValue);
|
|
|
|
Logger::log(L"");
|
|
}
|
|
|
|
void LogVirtualDesktopInfo(HWND window)
|
|
{
|
|
Logger::log(L"------------------ VirtualDesktop info --------------------- ");
|
|
Logger::log(L"");
|
|
|
|
IVirtualDesktopManager* vdManager = nullptr;
|
|
auto res = CoCreateInstance(CLSID_VirtualDesktopManager, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&vdManager));
|
|
if (FAILED(res))
|
|
{
|
|
Logger::log(L"Failed to create VirtualDesktopManager instance");
|
|
Logger::log(L"");
|
|
return;
|
|
}
|
|
|
|
BOOL isWindowOnCurrentDesktop = false;
|
|
if (vdManager->IsWindowOnCurrentVirtualDesktop(window, &isWindowOnCurrentDesktop) == S_OK)
|
|
{
|
|
Logger::log(L"Window is on current virtual desktop: {}", isWindowOnCurrentDesktop);
|
|
}
|
|
|
|
GUID id{};
|
|
auto vdIdRes = vdManager->GetWindowDesktopId(window, &id);
|
|
if (vdIdRes == S_OK)
|
|
{
|
|
OLECHAR* guidString;
|
|
if (StringFromCLSID(id, &guidString) == S_OK)
|
|
{
|
|
Logger::log(L"Virtual desktop id: {}", guidString);
|
|
}
|
|
|
|
CoTaskMemFree(guidString);
|
|
}
|
|
else
|
|
{
|
|
Logger::log(L"GetWindowDesktopId error: {}", get_last_error_or_default(vdIdRes));
|
|
}
|
|
|
|
if (vdManager)
|
|
{
|
|
vdManager->Release();
|
|
}
|
|
|
|
Logger::log(L"");
|
|
}
|
|
|
|
void LogInfo(HWND window)
|
|
{
|
|
auto processPath = get_process_path(window);
|
|
auto app = processPath;
|
|
auto pos = processPath.find_last_of('\\');
|
|
if (pos != std::string::npos && pos + 1 < processPath.length())
|
|
{
|
|
app = processPath.substr(pos + 1);
|
|
}
|
|
|
|
Logger::log(L"Timestamp: {}", std::chrono::system_clock::now());
|
|
Logger::log(L"Window: {}", app);
|
|
|
|
WCHAR className[256];
|
|
auto classNameLength = GetClassName(window, className, sizeof(className));
|
|
if (classNameLength > 0)
|
|
{
|
|
Logger::log(L"Class: {}", className);
|
|
}
|
|
else
|
|
{
|
|
Logger::log(L"GetClassName error: {}", get_last_error_or_default(GetLastError()));
|
|
}
|
|
|
|
Logger::log(L"");
|
|
|
|
LogStyles(window);
|
|
LogExStyles(window);
|
|
LogDwmAttributes(window);
|
|
LogVirtualDesktopInfo(window);
|
|
|
|
Logger::log(L"=======================================");
|
|
Logger::log(L"");
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
|
_In_opt_ HINSTANCE hPrevInstance,
|
|
_In_ LPWSTR lpCmdLine,
|
|
_In_ int nCmdShow)
|
|
{
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
|
|
|
Logger::init("StylesReportTool");
|
|
|
|
WNDCLASSEXW wcex;
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = {};
|
|
wcex.lpfnWndProc = WndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSTYLESICON));
|
|
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wcex.lpszMenuName = L"";
|
|
wcex.lpszClassName = L"StylesReportTool";
|
|
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALLICON));
|
|
|
|
if (!RegisterClassExW(&wcex))
|
|
{
|
|
Logger::log(L"Register class error: {}", get_last_error_or_default(GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
HWND hWnd = CreateWindowW(L"StylesReportTool", L"Window Style Report Tool", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 600, 200, nullptr, nullptr, hInstance, nullptr);
|
|
if (!hWnd)
|
|
{
|
|
Logger::log(L"Window creation error: {}", get_last_error_or_default(GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!RegisterHotKey(hWnd, 1, MOD_ALT | MOD_CONTROL | MOD_NOREPEAT, 0x53)) // ctrl + alt + s
|
|
{
|
|
Logger::log(L"Failed to register hotkey: {}", get_last_error_or_default(GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
|
|
MSG msg{};
|
|
while (GetMessage(&msg, nullptr, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return (int) msg.wParam;
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_HOTKEY:
|
|
{
|
|
LogInfo(GetForegroundWindow());
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hWnd, &ps);
|
|
|
|
LPCWSTR text = L"Please select the target window (using a mouse or Alt+Tab), \r\nand press Ctrl+Alt+S to capture its styles. \r\nYou can find the output file \"window_styles.txt\" on your desktop.";
|
|
RECT rc{0,50,600,200};
|
|
DrawText(hdc, text, (int)wcslen(text), &rc, DT_CENTER | DT_WORDBREAK);
|
|
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|