Update IsMonitorValid to return false when no displays are found.

Versions of Windows before Win11 will crash when `CreateForMonitor` is
called, but the system has no attached displays. This can be avoided by
adding a check to ensure at least one display is found before we return
true in `IsMonitorValid`. Previously we would early return `true` if the
"monitor" we were checking was the `kFullDesktopScreenId`.

Bug: chromium:1316478
Change-Id: I2562fe3834db574cf3706ee1d604472ac03f9ff3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258920
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Austin Orion <auorion@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#36555}
diff --git a/modules/desktop_capture/win/screen_capture_utils.cc b/modules/desktop_capture/win/screen_capture_utils.cc
index 8411533..2fc2c1a 100644
--- a/modules/desktop_capture/win/screen_capture_utils.cc
+++ b/modules/desktop_capture/win/screen_capture_utils.cc
@@ -24,6 +24,14 @@
 
 namespace webrtc {
 
+bool HasActiveDisplay() {
+  DesktopCapturer::SourceList screens;
+  if (!GetScreenList(&screens))
+    return false;
+
+  return screens.size() >= 1;
+}
+
 bool GetScreenList(DesktopCapturer::SourceList* screens,
                    std::vector<std::string>* device_names /* = nullptr */) {
   RTC_DCHECK_EQ(screens->size(), 0U);
@@ -91,6 +99,12 @@
   // An HMONITOR of 0 refers to a virtual monitor that spans all physical
   // monitors.
   if (monitor == 0) {
+    // There is a bug in a Windows OS API that causes a crash when capturing if
+    // there are no active displays. We must ensure there is an active display
+    // before returning true.
+    if (!HasActiveDisplay())
+      return false;
+
     return true;
   }
 
diff --git a/modules/desktop_capture/win/screen_capture_utils.h b/modules/desktop_capture/win/screen_capture_utils.h
index bcb183b..97bfe81 100644
--- a/modules/desktop_capture/win/screen_capture_utils.h
+++ b/modules/desktop_capture/win/screen_capture_utils.h
@@ -29,6 +29,9 @@
 
 namespace webrtc {
 
+// Returns true if the system has at least one active display.
+bool HasActiveDisplay();
+
 // Output the list of active screens into `screens`. Returns true if succeeded,
 // or false if it fails to enumerate the display devices. If the `device_names`
 // is provided, it will be filled with the DISPLAY_DEVICE.DeviceName in UTF-8
diff --git a/modules/desktop_capture/win/screen_capture_utils_unittest.cc b/modules/desktop_capture/win/screen_capture_utils_unittest.cc
index 80d1fb3..0855554 100644
--- a/modules/desktop_capture/win/screen_capture_utils_unittest.cc
+++ b/modules/desktop_capture/win/screen_capture_utils_unittest.cc
@@ -35,7 +35,8 @@
   DesktopCapturer::SourceList screens;
   ASSERT_TRUE(GetScreenList(&screens));
   if (screens.size() == 0) {
-    RTC_LOG(LS_INFO) << "Skip screen capture test on systems with no monitors.";
+    RTC_LOG(LS_INFO)
+        << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
     GTEST_SKIP();
   }
 
@@ -45,12 +46,33 @@
 }
 
 TEST(ScreenCaptureUtilsTest, FullScreenDeviceIndexToHmonitor) {
+  if (!HasActiveDisplay()) {
+    RTC_LOG(LS_INFO)
+        << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
+    GTEST_SKIP();
+  }
+
   HMONITOR hmonitor;
   ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
   ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
   ASSERT_TRUE(IsMonitorValid(hmonitor));
 }
 
+TEST(ScreenCaptureUtilsTest, NoMonitors) {
+  if (HasActiveDisplay()) {
+    RTC_LOG(LS_INFO) << "Skip ScreenCaptureUtilsTest designed specifically for "
+                        "systems with no monitors";
+    GTEST_SKIP();
+  }
+
+  HMONITOR hmonitor;
+  ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
+  ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
+
+  // The monitor should be invalid since the system has no attached displays.
+  ASSERT_FALSE(IsMonitorValid(hmonitor));
+}
+
 TEST(ScreenCaptureUtilsTest, InvalidDeviceIndexToHmonitor) {
   HMONITOR hmonitor;
   ASSERT_FALSE(GetHmonitorFromDeviceIndex(kInvalidScreenId, &hmonitor));
diff --git a/modules/desktop_capture/win/wgc_capture_source.cc b/modules/desktop_capture/win/wgc_capture_source.cc
index c81cfcb..c95847d 100644
--- a/modules/desktop_capture/win/wgc_capture_source.cc
+++ b/modules/desktop_capture/win/wgc_capture_source.cc
@@ -163,6 +163,12 @@
   if (FAILED(hr))
     return hr;
 
+  // Ensure the monitor is still valid (hasn't disconnected) before trying to
+  // create the item. On versions of Windows before Win11, `CreateForMonitor`
+  // will crash if no displays are connected.
+  if (!IsMonitorValid(*hmonitor_))
+    return E_ABORT;
+
   ComPtr<WGC::IGraphicsCaptureItem> item;
   hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item));
   if (FAILED(hr))
diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc
index dfe3577..36fefa2 100644
--- a/modules/desktop_capture/win/wgc_capturer_win.cc
+++ b/modules/desktop_capture/win/wgc_capturer_win.cc
@@ -57,10 +57,15 @@
 }  // namespace
 
 bool IsWgcSupported(CaptureType capture_type) {
-  // A bug in the WGC API `CreateForMonitor` was fixed in 20H1.
-  if (capture_type == CaptureType::kScreen &&
-      rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1) {
-    return false;
+  if (capture_type == CaptureType::kScreen) {
+    // A bug in the WGC API `CreateForMonitor` was fixed in 20H1.
+    if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1)
+      return false;
+
+    // There is another bug in `CreateForMonitor` that causes a crash if there
+    // are no active displays.
+    if (!HasActiveDisplay())
+      return false;
   }
 
   if (!ResolveCoreWinRTDelayload())
diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
index 18edf45..ddef89f 100644
--- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
+++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
@@ -361,6 +361,18 @@
   EXPECT_GT(frame_->size().height(), 0);
 }
 
+TEST_F(WgcCapturerWinTest, NoMonitors) {
+  if (HasActiveDisplay()) {
+    RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
+                        "systems with no monitors";
+    GTEST_SKIP();
+  }
+
+  // A bug in `CreateForMonitor` prevents screen capture when no displays are
+  // attached.
+  EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
+}
+
 // Window specific tests.
 TEST_F(WgcCapturerWinTest, FocusOnWindow) {
   capturer_ = WgcCapturerWin::CreateRawWindowCapturer(