From 688235d330e870d3d759f8b3152accd9f3654248 Mon Sep 17 00:00:00 2001 From: Austin Orion Date: Wed, 14 Apr 2021 10:14:01 -0700 Subject: [PATCH] Exclude WS_EX_TOOLWINDOWs for WgcCapturerWin. This changes modifies EnumerateCapturableWindows to accept an optional parameter consisting of extended window styles that will prevent windows with the specified styles from being returned. This allows us to filter out windows with the WS_EX_TOOLWINDOW style for the WgcCapturerWin, which does not support capture of such windows. Bug: webrtc:12679 Change-Id: Id9ac28afd331ba20fcb7f9e7be54ea5eee2e022e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215161 Reviewed-by: Jamie Walch Commit-Queue: Austin Orion Cr-Commit-Position: refs/heads/master@{#33779} --- .../win/test_support/test_window.cc | 14 ++++--- .../win/test_support/test_window.h | 3 +- .../desktop_capture/win/wgc_capturer_win.h | 5 ++- .../win/wgc_capturer_win_unittest.cc | 19 ++++++++++ .../win/window_capture_utils.cc | 37 +++++++++++++------ .../win/window_capture_utils.h | 12 +++++- 6 files changed, 68 insertions(+), 22 deletions(-) diff --git a/modules/desktop_capture/win/test_support/test_window.cc b/modules/desktop_capture/win/test_support/test_window.cc index d5fa9ed24e..bcbadecfaf 100644 --- a/modules/desktop_capture/win/test_support/test_window.cc +++ b/modules/desktop_capture/win/test_support/test_window.cc @@ -41,7 +41,8 @@ LRESULT CALLBACK WindowProc(HWND hwnd, WindowInfo CreateTestWindow(const WCHAR* window_title, const int height, - const int width) { + const int width, + const LONG extended_styles) { WindowInfo info; ::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, @@ -61,11 +62,12 @@ WindowInfo CreateTestWindow(const WCHAR* window_title, // height and width parameters, or if they supplied invalid values. int window_height = height <= 0 ? kWindowHeight : height; int window_width = width <= 0 ? kWindowWidth : width; - info.hwnd = ::CreateWindowW(kWindowClass, window_title, WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, window_width, - window_height, /*parent_window=*/nullptr, - /*menu_bar=*/nullptr, info.window_instance, - /*additional_params=*/nullptr); + info.hwnd = + ::CreateWindowExW(extended_styles, kWindowClass, window_title, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + window_width, window_height, /*parent_window=*/nullptr, + /*menu_bar=*/nullptr, info.window_instance, + /*additional_params=*/nullptr); ::ShowWindow(info.hwnd, SW_SHOWNORMAL); ::UpdateWindow(info.hwnd); diff --git a/modules/desktop_capture/win/test_support/test_window.h b/modules/desktop_capture/win/test_support/test_window.h index 7c7676c194..05727684ea 100644 --- a/modules/desktop_capture/win/test_support/test_window.h +++ b/modules/desktop_capture/win/test_support/test_window.h @@ -33,7 +33,8 @@ struct WindowInfo { WindowInfo CreateTestWindow(const WCHAR* window_title, const int height = 0, - const int width = 0); + const int width = 0, + const LONG extended_styles = 0); void ResizeTestWindow(const HWND hwnd, const int width, const int height); diff --git a/modules/desktop_capture/win/wgc_capturer_win.h b/modules/desktop_capture/win/wgc_capturer_win.h index aae2304263..1171d15fad 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.h +++ b/modules/desktop_capture/win/wgc_capturer_win.h @@ -46,7 +46,10 @@ class WindowEnumerator final : public SourceEnumerator { ~WindowEnumerator() override = default; bool FindAllSources(DesktopCapturer::SourceList* sources) override { - return window_capture_helper_.EnumerateCapturableWindows(sources); + // WGC fails to capture windows with the WS_EX_TOOLWINDOW style, so we + // provide it as a filter to ensure windows with the style are not returned. + return window_capture_helper_.EnumerateCapturableWindows(sources, + WS_EX_TOOLWINDOW); } private: diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index 732de7db20..1056c821c3 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -364,6 +364,25 @@ TEST_F(WgcCapturerWinTest, SelectClosedWindow) { EXPECT_FALSE(capturer_->SelectSource(source_id_)); } +TEST_F(WgcCapturerWinTest, UnsupportedWindowStyle) { + // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not + // support. + window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth, + kMediumWindowHeight, WS_EX_TOOLWINDOW); + capturer_ = WgcCapturerWin::CreateRawWindowCapturer( + DesktopCaptureOptions::CreateDefault()); + DesktopCapturer::SourceList sources; + EXPECT_TRUE(capturer_->GetSourceList(&sources)); + auto it = std::find_if( + sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) { + return src.id == reinterpret_cast(window_info_.hwnd); + }); + + // We should not find the window, since we filter for unsupported styles. + EXPECT_EQ(it, sources.end()); + DestroyTestWindow(window_info_); +} + TEST_F(WgcCapturerWinTest, IncreaseWindowSizeMidCapture) { SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight); EXPECT_TRUE(capturer_->SelectSource(source_id_)); diff --git a/modules/desktop_capture/win/window_capture_utils.cc b/modules/desktop_capture/win/window_capture_utils.cc index 9e33e56c2d..7c5cc70087 100644 --- a/modules/desktop_capture/win/window_capture_utils.cc +++ b/modules/desktop_capture/win/window_capture_utils.cc @@ -27,12 +27,16 @@ namespace webrtc { namespace { struct GetWindowListParams { - GetWindowListParams(int flags, DesktopCapturer::SourceList* result) - : ignoreUntitled(flags & GetWindowListFlags::kIgnoreUntitled), - ignoreUnresponsive(flags & GetWindowListFlags::kIgnoreUnresponsive), + GetWindowListParams(int flags, + LONG ex_style_filters, + DesktopCapturer::SourceList* result) + : ignore_untitled(flags & GetWindowListFlags::kIgnoreUntitled), + ignore_unresponsive(flags & GetWindowListFlags::kIgnoreUnresponsive), + ex_style_filters(ex_style_filters), result(result) {} - const bool ignoreUntitled; - const bool ignoreUnresponsive; + const bool ignore_untitled; + const bool ignore_unresponsive; + const LONG ex_style_filters; DesktopCapturer::SourceList* const result; }; @@ -67,7 +71,13 @@ BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) { return TRUE; } - if (params->ignoreUnresponsive && !IsWindowResponding(hwnd)) { + // Filter out windows that match the extended styles the caller has specified, + // e.g. WS_EX_TOOLWINDOW for capturers that don't support overlay windows. + if (exstyle & params->ex_style_filters) { + return TRUE; + } + + if (params->ignore_unresponsive && !IsWindowResponding(hwnd)) { return TRUE; } @@ -79,7 +89,7 @@ BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) { // pump is waiting on this thread. If we've filtered out unresponsive // windows, this is not a concern, but otherwise we need to check if we can // safely make blocking calls. - if (params->ignoreUnresponsive || CanSafelyMakeBlockingCalls(hwnd)) { + if (params->ignore_unresponsive || CanSafelyMakeBlockingCalls(hwnd)) { const size_t kTitleLength = 500; WCHAR window_title[kTitleLength] = L""; if (GetWindowTextLength(hwnd) != 0 && @@ -89,7 +99,7 @@ BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) { } // Skip windows when we failed to convert the title or it is empty. - if (params->ignoreUntitled && window.title.empty()) + if (params->ignore_untitled && window.title.empty()) return TRUE; // Capture the window class name, to allow specific window classes to be @@ -271,8 +281,10 @@ bool IsWindowResponding(HWND window) { nullptr); } -bool GetWindowList(int flags, DesktopCapturer::SourceList* windows) { - GetWindowListParams params(flags, windows); +bool GetWindowList(int flags, + DesktopCapturer::SourceList* windows, + LONG ex_style_filters) { + GetWindowListParams params(flags, ex_style_filters, windows); return ::EnumWindows(&GetWindowListHandler, reinterpret_cast(¶ms)) != 0; } @@ -432,10 +444,11 @@ bool WindowCaptureHelperWin::IsWindowCloaked(HWND hwnd) { } bool WindowCaptureHelperWin::EnumerateCapturableWindows( - DesktopCapturer::SourceList* results) { + DesktopCapturer::SourceList* results, + LONG ex_style_filters) { if (!webrtc::GetWindowList((GetWindowListFlags::kIgnoreUntitled | GetWindowListFlags::kIgnoreUnresponsive), - results)) { + results, ex_style_filters)) { return false; } diff --git a/modules/desktop_capture/win/window_capture_utils.h b/modules/desktop_capture/win/window_capture_utils.h index f636a312f5..11b2c2c1b5 100644 --- a/modules/desktop_capture/win/window_capture_utils.h +++ b/modules/desktop_capture/win/window_capture_utils.h @@ -86,8 +86,11 @@ enum GetWindowListFlags { // - Program Manager & Start menu. // - [with kIgnoreUntitled] windows with no title. // - [with kIgnoreUnresponsive] windows that unresponsive. +// - Any windows with extended styles that match |ex_style_filters|. // Returns false if native APIs failed. -bool GetWindowList(int flags, DesktopCapturer::SourceList* windows); +bool GetWindowList(int flags, + DesktopCapturer::SourceList* windows, + LONG ex_style_filters = 0); typedef HRESULT(WINAPI* DwmIsCompositionEnabledFunc)(BOOL* enabled); typedef HRESULT(WINAPI* DwmGetWindowAttributeFunc)(HWND hwnd, @@ -107,7 +110,12 @@ class WindowCaptureHelperWin { bool IsWindowOnCurrentDesktop(HWND hwnd); bool IsWindowVisibleOnCurrentDesktop(HWND hwnd); bool IsWindowCloaked(HWND hwnd); - bool EnumerateCapturableWindows(DesktopCapturer::SourceList* results); + + // The optional |ex_style_filters| parameter allows callers to provide + // extended window styles (e.g. WS_EX_TOOLWINDOW) and prevent windows that + // match from being included in |results|. + bool EnumerateCapturableWindows(DesktopCapturer::SourceList* results, + LONG ex_style_filters = 0); private: HMODULE dwmapi_library_ = nullptr;