SnapshotUtils.cpp
1 #include "pch.h" 2 #include "SnapshotUtils.h" 3 4 #include <common/utils/elevation.h> 5 #include <common/utils/process_path.h> 6 #include <common/utils/resources.h> 7 #include <common/notifications/NotificationUtil.h> 8 9 #include <workspaces-common/WindowEnumerator.h> 10 #include <workspaces-common/WindowFilter.h> 11 12 #include <WorkspacesLib/AppUtils.h> 13 #include <WorkspacesLib/PwaHelper.h> 14 #include <WorkspacesLib/WindowUtils.h> 15 #include <WindowProperties/WorkspacesWindowPropertyUtils.h> 16 17 #include "Generated Files/resource.h" 18 19 #pragma comment(lib, "ntdll.lib") 20 21 namespace SnapshotUtils 22 { 23 namespace NonLocalizable 24 { 25 const std::wstring ApplicationFrameHost = L"ApplicationFrameHost.exe"; 26 } 27 28 bool IsProcessElevated(DWORD processID) 29 { 30 wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID) }; 31 wil::unique_handle token; 32 33 if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token)) 34 { 35 TOKEN_ELEVATION elevation; 36 DWORD size; 37 if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size)) 38 { 39 return elevation.TokenIsElevated != 0; 40 } 41 } 42 43 return false; 44 } 45 46 std::vector<WorkspacesData::WorkspacesProject::Application> GetApps(bool isGuidNeeded, const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle, const std::function<WorkspacesData::WorkspacesProject::Monitor::MonitorRect(unsigned int)> getMonitorRect) 47 { 48 Utils::PwaHelper pwaHelper{}; 49 std::vector<WorkspacesData::WorkspacesProject::Application> apps{}; 50 51 auto installedApps = Utils::Apps::GetAppsList(); 52 auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter); 53 54 for (const auto window : windows) 55 { 56 if (WindowFilter::FilterPopup(window)) 57 { 58 continue; 59 } 60 61 // filter by window rect size 62 RECT rect = WindowUtils::GetWindowRect(window); 63 if (rect.right - rect.left <= 0 || rect.bottom - rect.top <= 0) 64 { 65 continue; 66 } 67 68 // filter by window title 69 std::wstring title = WindowUtils::GetWindowTitle(window); 70 if (title.empty()) 71 { 72 continue; 73 } 74 75 Logger::info("Try to get window app:{}", reinterpret_cast<void*>(window)); 76 77 DWORD pid{}; 78 GetWindowThreadProcessId(window, &pid); 79 80 // filter by app path 81 std::wstring processPath = get_process_path(window); 82 if (processPath.empty()) 83 { 84 // When PT runs not as admin, it can't get the process path of the window of the elevated process. 85 // Notify the user that running as admin is required to process elevated windows. 86 if (!is_process_elevated() && IsProcessElevated(pid)) 87 { 88 auto notificationUtil = std::make_unique<notifications::NotificationUtil>(); 89 90 notificationUtil->WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_PROJECTS), 91 GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED), 92 GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE), 93 GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN)); 94 } 95 96 continue; 97 } 98 99 if (WindowUtils::IsExcludedByDefault(window, processPath)) 100 { 101 continue; 102 } 103 104 // fix for the packaged apps that are not caught when minimized, e.g. Settings, Microsoft ToDo, ... 105 if (processPath.ends_with(NonLocalizable::ApplicationFrameHost)) 106 { 107 for (auto otherWindow : windows) 108 { 109 DWORD otherPid{}; 110 GetWindowThreadProcessId(otherWindow, &otherPid); 111 112 // searching for the window with the same title but different PID 113 if (pid != otherPid && title == WindowUtils::GetWindowTitle(otherWindow)) 114 { 115 processPath = get_process_path(otherPid); 116 break; 117 } 118 } 119 } 120 121 auto data = Utils::Apps::GetApp(processPath, pid, installedApps); 122 if (!data.has_value() || data->name.empty()) 123 { 124 Logger::info(L"Installed app not found:{},{}", reinterpret_cast<void*>(window), processPath); 125 continue; 126 } 127 128 if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) 129 { 130 // Only care about steam games if it has no thick frame to remain consistent with 131 // the behavior as before. 132 continue; 133 } 134 135 Logger::info(L"Found app for window:{},{}", reinterpret_cast<void*>(window), processPath); 136 137 auto appData = data.value(); 138 139 bool isEdge = appData.IsEdge(); 140 bool isChrome = appData.IsChrome(); 141 if (isEdge || isChrome) 142 { 143 auto windowAumid = Utils::GetAUMIDFromWindow(window); 144 std::optional<std::wstring> pwaAppId{}; 145 146 if (isEdge) 147 { 148 pwaAppId = pwaHelper.GetEdgeAppId(windowAumid); 149 } 150 else if (isChrome) 151 { 152 pwaAppId = pwaHelper.GetChromeAppId(windowAumid); 153 } 154 155 if (pwaAppId.has_value()) 156 { 157 auto pwaName = pwaHelper.SearchPwaName(pwaAppId.value(), windowAumid); 158 Logger::info(L"Found {} PWA app with name {}, appId: {}", (isEdge ? L"Edge" : (isChrome ? L"Chrome" : L"unknown")), pwaName, pwaAppId.value()); 159 160 appData.pwaAppId = pwaAppId.value(); 161 appData.name = pwaName + L" (" + appData.name + L")"; 162 // If it's pwa app, appUserModelId should be their own pwa id. 163 appData.appUserModelId = windowAumid; 164 } 165 } 166 167 bool isMinimized = WindowUtils::IsMinimized(window); 168 unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window); 169 170 if (isMinimized) 171 { 172 // set the screen area as position, the values we get for the minimized windows are out of the screens' area 173 WorkspacesData::WorkspacesProject::Monitor::MonitorRect monitorRect = getMonitorRect(monitorNumber); 174 rect.left = monitorRect.left; 175 rect.top = monitorRect.top; 176 rect.right = monitorRect.left + monitorRect.width; 177 rect.bottom = monitorRect.top + monitorRect.height; 178 } 179 180 std::wstring guid = isGuidNeeded ? WorkspacesWindowProperties::GetGuidFromHwnd(window) : L""; 181 182 WorkspacesData::WorkspacesProject::Application app{ 183 .id = guid, 184 .name = appData.name, 185 .title = title, 186 .path = appData.installPath, 187 .packageFullName = appData.packageFullName, 188 .appUserModelId = appData.appUserModelId, 189 .pwaAppId = appData.pwaAppId, 190 .commandLineArgs = L"", 191 .version = L"1", 192 .isElevated = IsProcessElevated(pid), 193 .canLaunchElevated = appData.canLaunchElevated, 194 .isMinimized = isMinimized, 195 .isMaximized = WindowUtils::IsMaximized(window), 196 .position = WorkspacesData::WorkspacesProject::Application::Position{ 197 .x = rect.left, 198 .y = rect.top, 199 .width = rect.right - rect.left, 200 .height = rect.bottom - rect.top, 201 }, 202 .monitor = monitorNumber, 203 }; 204 205 apps.push_back(app); 206 } 207 208 return apps; 209 } 210 }