1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/webrtc/desktop_capture_devices_util_win.h"
#include <windows.h>
#include <array>
#include "base/base_paths_win.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/process/process_handle.h"
#include "base/win/scoped_handle.h"
#include "base/win/window_enumerator.h"
#include "base/win/windows_types.h"
namespace {
bool IsUWPApp(const base::FilePath& app_path) {
base::FilePath system_path;
if (!base::PathService::Get(base::DIR_SYSTEM, &system_path)) {
return false;
}
// The ApplicationFrameHost.exe is the host process for UWP apps. It is
// located in the system directory (usually C:\Windows\System32).
if (base::FilePath::CompareEqualIgnoreCase(system_path.value(),
app_path.DirName().value()) &&
base::FilePath::CompareEqualIgnoreCase(app_path.BaseName().value(),
L"ApplicationFrameHost.exe")) {
return true;
}
return false;
}
// Callback to be provided to `base::win::EnumerateChildWindows` to find the
// child window of the UWP app with the "Windows.UI.Core.CoreWindow" class name.
// When the target window is found, this function should return `true`,
// signaling to `base::win::EnumerateChildWindows` that it should stop
// enumerating and store the found window handle in `out_uwp_app_core_window`.
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633493(v=vs.85)
bool IsUWPAppCoreWindow(HWND& out_uwp_app_core_window, HWND hwnd) {
std::wstring class_name = base::win::GetWindowClass(hwnd);
// Check if the class name matches the UWP app's core window class name.
if (class_name == L"Windows.UI.Core.CoreWindow") {
out_uwp_app_core_window = hwnd;
return true;
}
return false;
}
// Given a window handle `hwnd` for a UWP app, finds the pid of the app's main
// process.
base::ProcessId GetUWPAppCoreWindowProcessId(HWND hwnd) {
HWND main_uwp_app_core_window = nullptr;
// For UWP apps, we need to find the child window which has the class name
// Windows.UI.Core.CoreWindow and get the process ID from it.
base::win::EnumerateChildWindows(
hwnd, base::BindRepeating(&IsUWPAppCoreWindow,
std::ref(main_uwp_app_core_window)));
// There is no child window with the class name Windows.UI.Core.CoreWindow.
if (!main_uwp_app_core_window) {
return base::kNullProcessId;
}
DWORD main_process_id;
DWORD thread_id =
GetWindowThreadProcessId(main_uwp_app_core_window, &main_process_id);
if (!thread_id || !main_process_id) {
return base::kNullProcessId;
}
return main_process_id;
}
// Returns the executable's path for the given process handle.
base::FilePath GetProcessExecutablePath(HANDLE process_handle) {
std::wstring image_path(MAX_PATH, L'\0');
DWORD path_length = image_path.size();
BOOL success = QueryFullProcessImageNameW(process_handle, 0,
image_path.data(), &path_length);
if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Process name is potentially greater than MAX_PATH, try larger max size.
// https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
image_path.resize(UNICODE_STRING_MAX_CHARS);
path_length = image_path.size();
success = QueryFullProcessImageName(process_handle, 0, image_path.data(),
&path_length);
if (!success) {
return base::FilePath();
}
}
return base::FilePath(image_path);
}
} // namespace
base::ProcessId GetAppMainProcessId(intptr_t window_id) {
HWND hwnd = reinterpret_cast<HWND>(window_id);
DWORD process_id;
DWORD thread_id = GetWindowThreadProcessId(hwnd, &process_id);
if (!thread_id || !process_id) {
return base::kNullProcessId;
}
base::win::ScopedHandle process_handle(
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
/*bInheritHandle=*/FALSE, process_id));
if (!process_handle.is_valid()) {
return base::kNullProcessId;
}
// UWP apps' UI follow a hierarchy where the top-level window is created by
// ApplicationFrameHost.exe and the actual app window is a child window of the
// top-level window. Therefore, in this situation, we need to look down in the
// window hierarchy to find the correct process id.
base::FilePath app_path = GetProcessExecutablePath(process_handle.get());
// Checks if process is a UWP app.
if (IsUWPApp(app_path)) {
return GetUWPAppCoreWindowProcessId(hwnd);
}
base::ProcessId main_process_id_candidate = process_id;
base::ProcessId parent_id = base::GetParentProcessId(process_handle.get());
if (parent_id <= 0) {
// If there is no parent process, we return the current process id.
return main_process_id_candidate;
}
base::win::ScopedHandle parent_process_handle(OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, /*bInheritHandle=*/FALSE, parent_id));
while (parent_process_handle.is_valid()) {
base::FilePath parent_path =
GetProcessExecutablePath(parent_process_handle.get());
if (parent_path.empty()) {
return main_process_id_candidate;
}
// If the executables are different, we return the last PID whose executable
// was the same as `app_path`
if (!base::FilePath::CompareEqualIgnoreCase(parent_path.value(),
app_path.value())) {
return main_process_id_candidate;
}
main_process_id_candidate = parent_id;
parent_id = base::GetParentProcessId(parent_process_handle.Get());
parent_process_handle.Set(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
/*bInheritHandle=*/FALSE, parent_id));
}
return main_process_id_candidate;
}
|