Enumerating windows owned by the current process on Windows has some complications due to the GetWindowText*() APIs potentially causing a deadlock. The APIs will send messages to the window's message loop, and if the message loop is waiting on this operation we will enter a deadlock. I previously put in a mitigation for this [1] which brought the incidence rate down by an order of magnitude, but we are still seeing this issue fairly frequently. So, I've added DesktopCaptureOption enumerate_current_process_windows which allows consumers to avoid this issue completely by ignoring these potentially problematic windows. By default the flag is set to true which equates with the current behavior, consumers can set the flag to false to get the new behavior. I've also updated all the capturers that enumerate windows on Windows to respect the option. [1] https://webrtc-review.googlesource.com/c/src/+/195365 Bug: chromium:1152841 Change-Id: I0e0d868957d6fbe1e607a440b3a909d005c93ccf Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219380 Commit-Queue: Austin Orion <auorion@microsoft.com> Reviewed-by: Joe Downing <joedow@chromium.org> Cr-Commit-Position: refs/heads/master@{#34191}
155 lines
5.6 KiB
C++
155 lines
5.6 KiB
C++
/*
|
|
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "modules/desktop_capture/win/window_capture_utils.h"
|
|
|
|
#include <winuser.h>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <mutex>
|
|
|
|
#include "modules/desktop_capture/desktop_capturer.h"
|
|
#include "modules/desktop_capture/win/test_support/test_window.h"
|
|
#include "rtc_base/thread.h"
|
|
#include "test/gtest.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
const char kWindowThreadName[] = "window_capture_utils_test_thread";
|
|
const WCHAR kWindowTitle[] = L"Window Capture Utils Test";
|
|
|
|
std::unique_ptr<rtc::Thread> SetUpUnresponsiveWindow(std::mutex& mtx,
|
|
WindowInfo& info) {
|
|
std::unique_ptr<rtc::Thread> window_thread;
|
|
window_thread = rtc::Thread::Create();
|
|
window_thread->SetName(kWindowThreadName, nullptr);
|
|
window_thread->Start();
|
|
|
|
window_thread->Invoke<void>(
|
|
RTC_FROM_HERE, [&info]() { info = CreateTestWindow(kWindowTitle); });
|
|
|
|
// Intentionally create a deadlock to cause the window to become unresponsive.
|
|
mtx.lock();
|
|
window_thread->PostTask(RTC_FROM_HERE, [&mtx]() {
|
|
mtx.lock();
|
|
mtx.unlock();
|
|
});
|
|
|
|
return window_thread;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(WindowCaptureUtilsTest, GetWindowList) {
|
|
WindowInfo info = CreateTestWindow(kWindowTitle);
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
|
|
EXPECT_GT(window_list.size(), 0ULL);
|
|
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
DestroyTestWindow(info);
|
|
}
|
|
|
|
TEST(WindowCaptureUtilsTest, IncludeUnresponsiveWindows) {
|
|
std::mutex mtx;
|
|
WindowInfo info;
|
|
std::unique_ptr<rtc::Thread> window_thread =
|
|
SetUpUnresponsiveWindow(mtx, info);
|
|
|
|
EXPECT_FALSE(IsWindowResponding(info.hwnd));
|
|
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
|
|
EXPECT_GT(window_list.size(), 0ULL);
|
|
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
|
|
mtx.unlock();
|
|
window_thread->Invoke<void>(RTC_FROM_HERE,
|
|
[&info]() { DestroyTestWindow(info); });
|
|
window_thread->Stop();
|
|
}
|
|
|
|
TEST(WindowCaptureUtilsTest, IgnoreUnresponsiveWindows) {
|
|
std::mutex mtx;
|
|
WindowInfo info;
|
|
std::unique_ptr<rtc::Thread> window_thread =
|
|
SetUpUnresponsiveWindow(mtx, info);
|
|
|
|
EXPECT_FALSE(IsWindowResponding(info.hwnd));
|
|
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(
|
|
GetWindowList(GetWindowListFlags::kIgnoreUnresponsive, &window_list));
|
|
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
|
|
mtx.unlock();
|
|
window_thread->Invoke<void>(RTC_FROM_HERE,
|
|
[&info]() { DestroyTestWindow(info); });
|
|
window_thread->Stop();
|
|
}
|
|
|
|
TEST(WindowCaptureUtilsTest, IncludeUntitledWindows) {
|
|
WindowInfo info = CreateTestWindow(L"");
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
|
|
EXPECT_GT(window_list.size(), 0ULL);
|
|
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
DestroyTestWindow(info);
|
|
}
|
|
|
|
TEST(WindowCaptureUtilsTest, IgnoreUntitledWindows) {
|
|
WindowInfo info = CreateTestWindow(L"");
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreUntitled, &window_list));
|
|
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
DestroyTestWindow(info);
|
|
}
|
|
|
|
TEST(WindowCaptureUtilsTest, IgnoreCurrentProcessWindows) {
|
|
WindowInfo info = CreateTestWindow(kWindowTitle);
|
|
DesktopCapturer::SourceList window_list;
|
|
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreCurrentProcessWindows,
|
|
&window_list));
|
|
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
|
|
[&info](DesktopCapturer::Source window) {
|
|
return reinterpret_cast<HWND>(window.id) ==
|
|
info.hwnd;
|
|
}),
|
|
window_list.end());
|
|
DestroyTestWindow(info);
|
|
}
|
|
|
|
} // namespace webrtc
|