NickeManarin/ScreenToGif · error · Exception

It was not possible to get a list of known screens.

Error message

It was not possible to get a list of known screens.

What it means

Thrown by the Recorder's monitor-selection routine when MonitorHelper.AllMonitorsGranular() returns an empty list, so none of the three FirstOrDefault fallbacks (point-containing, primary, any) resolve a monitor. AllMonitorsGranular delegates to the AllMonitors getter which drives User32.EnumDisplayMonitors via a callback closure; if that native enumeration yields zero monitors, the List is empty. The throw means the window cannot determine which screen to anchor itself to.

Source

Thrown at ScreenToGif/Windows/Recorder.xaml.cs:1430

            {
                UserSettings.All.RecorderLeft = Arguments.Region.Left - Constants.LeftOffset;
                UserSettings.All.RecorderTop = Arguments.Region.Top - Constants.TopOffset;
                UserSettings.All.RecorderWidth = (int)Arguments.Region.Width + Constants.HorizontalOffset;
                UserSettings.All.RecorderHeight = (int)Arguments.Region.Height + Constants.VerticalOffset;
                Arguments.Region = Rect.Empty;
            }
        }

        //Since the list of monitors could have been changed, it needs to be queried again.
        _viewModel.Monitors = MonitorHelper.AllMonitorsGranular();

        //Detect closest screen to the point (previously selected top/left point or current mouse coordinate).
        var point = startup ? (double.IsNaN(UserSettings.All.RecorderTop) || double.IsNaN(UserSettings.All.RecorderLeft) ?
            CursorHelper.GetMousePosition(_scale, Left, Top) : new Point((int)UserSettings.All.RecorderLeft, (int)UserSettings.All.RecorderTop)) : new Point((int) Left, (int) Top);
        var closest = _viewModel.Monitors.FirstOrDefault(x => x.Bounds.Contains(point)) ?? _viewModel.Monitors.FirstOrDefault(x => x.IsPrimary) ?? _viewModel.Monitors.FirstOrDefault();

        if (closest == null)
            throw new Exception("It was not possible to get a list of known screens.");

        //Move the window to the correct location.
        var left = UserSettings.All.RecorderLeft;
        var top = UserSettings.All.RecorderTop;

        if (double.IsNaN(UserSettings.All.RecorderTop) || double.IsNaN(UserSettings.All.RecorderLeft))
        {
            left = closest.WorkingArea.Left + closest.WorkingArea.Width / 2d - ActualWidth / 2d;
            top = closest.WorkingArea.Top + closest.WorkingArea.Height / 2d - ActualHeight / 2d;
        }
        else
        {
            //To much to the Left.
            if (closest.WorkingArea.Left > UserSettings.All.RecorderLeft + UserSettings.All.RecorderWidth - 100)
                left = closest.WorkingArea.Left;

            //Too much to the top.
            if (closest.WorkingArea.Top > UserSettings.All.RecorderTop + UserSettings.All.RecorderHeight - 100)

View on GitHub (pinned to a4d0a67c21)

Solutions

  1. Ensure at least one display is active and powered on before opening the recorder.
  2. Reconnect the disconnected monitor, then close and reopen the recorder window so AllMonitorsGranular re-enumerates.
  3. Delay the reposition call until the SystemEvents.DisplaySettingsChanged event has fully fired and the desktop is stable.
  4. Do not launch the recorder from a non-interactive session (service/scheduled task) — it needs an interactive desktop to enumerate monitors.
  5. Add a guard: if the list is empty, fall back to SystemParameters.PrimaryScreenWidth/Height instead of throwing, or retry enumeration after a short delay.

Example fix

// before
_viewModel.Monitors = MonitorHelper.AllMonitorsGranular();
var point = /* ... */;
var closest = _viewModel.Monitors.FirstOrDefault(x => x.Bounds.Contains(point)) ?? _viewModel.Monitors.FirstOrDefault(x => x.IsPrimary) ?? _viewModel.Monitors.FirstOrDefault();
if (closest == null)
    throw new Exception("It was not possible to get a list of known screens.");

// after: retry + graceful fallback to SystemParameters primary screen
_viewModel.Monitors = MonitorHelper.AllMonitorsGranular();
for (var i = 0; _viewModel.Monitors.Count == 0 && i < 3; i++)
    _viewModel.Monitors = MonitorHelper.AllMonitorsGranular();

var point = /* ... */;
var closest = _viewModel.Monitors.FirstOrDefault(x => x.Bounds.Contains(point))
           ?? _viewModel.Monitors.FirstOrDefault(x => x.IsPrimary)
           ?? _viewModel.Monitors.FirstOrDefault();

if (closest == null)
{
    // No enumerated monitor — anchor to the WPF primary-screen rect instead of crashing.
    closest = new Monitor { Bounds = new Rect(0, 0, SystemParameters.PrimaryScreenWidth, SystemParameters.PrimaryScreenHeight),
                            WorkingArea = SystemParameters.WorkArea, IsPrimary = true };
    LogWriter.Log(new InvalidOperationException("Monitor enumeration returned empty list; falling back to primary screen."), "Monitor enumeration fallback.");
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate monitor enumeration before using it for positioning.
var monitors = MonitorHelper.AllMonitorsGranular();
if (monitors == null || monitors.Count == 0)
{
    // Retry once after the display subsystem settles.
    await Task.Delay(200);
    monitors = MonitorHelper.AllMonitorsGranular();
}
if (monitors.Count == 0)
    throw new InvalidOperationException("No displays available; cannot position the recorder.");

_viewModel.Monitors = monitors;

Type guard

// Ensure a non-empty monitor list before resolving 'closest'.
static bool HasUsableMonitor(List<Monitor> monitors, Point point) =>
    monitors.Count > 0 &&
    (monitors.Any(x => x.Bounds.Contains(point)) || monitors.Any(x => x.IsPrimary));

if (!HasUsableMonitor(_viewModel.Monitors, point))
    return; // skip repositioning, keep current location

Try / catch

try
{
    // ... monitor selection + positioning ...
}
catch (Exception e) when (e.Message.Contains("list of known screens"))
{
    LogWriter.Log(e, "Monitor enumeration failed during recorder positioning.");
    // Keep the window at its last known position instead of crashing.
}

Prevention

When it happens

Trigger: Called during recorder startup/positioning (startup=true) or repositioning. AllMonitorsGranular() returns an empty list because User32.EnumDisplayMonitors invoked no callback items — typically when the calling thread has no visible desktop, the session is in transition (logoff/lock/UAC secure desktop), or all displays were disconnected while the window was open.

Common situations: All monitors disconnected / powered off while ScreenToGif was open; running on a session that lost its desktop (fast-user-switch, RDP disconnect); the window is being positioned during a WM_DISPLAYCHANGE before the new monitor list is populated; headless service context with no interactive desktop; display driver crashed and the GDI desktop is momentarily gone.

Related errors


AI-assisted analysis of NickeManarin/ScreenToGif@a4d0a67c21 (2026-08-13). Data as JSON: /api/errors/d4eb33f148cdc270. Report an issue: GitHub.