stride3d/stride · error · InvalidOperationException

The given is already registered as a clipboard listener.

Error message

The given {window} is already registered as a clipboard listener.

What it means

ClipboardMonitor.RegisterListener throws InvalidOperationException when the given Window is already present in the static Listeners dictionary, because a window can only hook the clipboard chain once per monitor instance.

Solutions

  1. Call the unregister/deregister method for the window before calling RegisterListener again.
  2. Track registration state per window (e.g. a bool or Listeners.ContainsKey check) and skip duplicate registrations.
  3. Register once in the window's SourceInitialized/Loaded handler with a guard flag.
  4. Ensure cleanup runs on Closed so the same window can be safely re-registered later.

Example fix

// before
protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    ClipboardMonitor.RegisterListener(this);
    ClipboardMonitor.RegisterListener(this); // throws on 2nd call
}
// after
protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    if (!_registered)
    {
        ClipboardMonitor.RegisterListener(this);
        _registered = true;
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!ClipboardMonitorIsRegistered(this))
    ClipboardMonitor.RegisterListener(this);

Type guard

static bool IsRegistered(Window w) => !ClipboardMonitorRegisteredWindows.Contains(w); // track locally

Try / catch

try { ClipboardMonitor.RegisterListener(this); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already registered")) { /* already listening */ }

Prevention

When it happens

Trigger: Calling ClipboardMonitor.RegisterListener(window) twice for the same Window instance without unregistering in between; re-registering after a window was shown/hidden but not unregistered.

Common situations: Registering in both a Loaded handler and a constructor; re-registering after theme/appearance change re-triggers initialization; forgetting to call the unregister counterpart on window close before reopening.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/58f538cd4bf5b588. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Interop/ClipboardMonitor.cs:38

        /// <summary>
        /// Raised when the clipboard has changed and contains text.
        /// </summary>
        /// <remarks>The sender of this event a window that was previously registered as a clipboard viewer with <see cref="RegisterListener"/>.</remarks>
        public static event EventHandler<EventArgs> ClipboardTextChanged;

        /// <summary>
        /// Registers the given <paramref name="window"/> as a clipboard viewer.
        /// </summary>
        /// <param name="window"></param>
        /// <exception cref="ArgumentNullException">window is <c>null</c></exception>
        /// <exception cref="InvalidOperationException">window is already registered.</exception>
        public static void RegisterListener([NotNull] Window window)
        {
            if (window == null) throw new ArgumentNullException(nameof(window));

            HwndSource hwndSource;
            if (Listeners.TryGetValue(window, out hwndSource))
                throw new InvalidOperationException($"The given {window} is already registered as a clipboard listener.");

            hwndSource = GetHwndSource(window);
            if (hwndSource == null)
                return;

            Listeners.Add(window, hwndSource);

            window.Dispatcher.Invoke(() =>
            {
                // start processing window messages
                hwndSource.AddHook(WinProc);
                // set the window as a viewer
                hwndNextViewer = NativeHelper.SetClipboardViewer(hwndSource.Handle);
            });
        }

        /// <summary>
        /// Unregisters the given <paramref name="window"/> as a clipboard viewer.

View on GitHub (pinned to 96fad776d2)