dotnet/wpf · error · InvalidOperationException

SR.PenService_WindowAlreadyRegistered

Error message

SR.PenService_WindowAlreadyRegistered

What it means

WispLogic.RegisterInputSource (WispLogic.cs:3157) throws InvalidOperationException(SR.PenService_WindowAlreadyRegistered) when RegisterStylusPointProcessor/InputSource registration finds the given PresentationSource (window) already present in __penContextsMap. Each HwndSource can be registered with the stylus/pen service only once.

Solutions

  1. Guard registration with a check that the source is not already registered (track your own set of registered sources).
  2. Pair every registration with an unregistration on window/source close.
  3. Move registration code out of handlers that can fire multiple times (e.g. Loaded) into a once-only initialization path.

Example fix

// before
OnLoaded((s,e) => StylusLogic.RegisterStylusPointProcessor(myProcessor, source));
// after
if (!registered.Contains(source)) { registered.Add(source); StylusLogic.RegisterStylusPointProcessor(myProcessor, source); }
Defensive patterns

Strategy: validation

Validate before calling

if (registeredSources.Contains(inputSource)) return; // skip duplicate registration
registeredSources.Add(inputSource);

Try / catch

try { RegisterStylusPointProcessor(proc, source); } catch (InvalidOperationException ex) when (ex.Message.Contains("already registered")) { /* treat as success, idempotent */ }

Prevention

When it happens

Trigger: Calling WispLogic-based registration APIs (e.g. StylusLogic.RegisterStylusPointProcessor or internal AddPenContexts path) twice with the same PresentationSource, or re-registering a window whose registration was never torn down.

Common situations: Host apps registering a custom stylus plug-in multiple times (e.g. on every window load without unload pairing); window re-shown/recycled after source teardown failed; duplicate initialization in derived Window classes.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/2cf8c1ec57197f6c. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Stylus/Wisp/WispLogic.cs:3157

        /////////////////////////////////////////////////////////////////////
        internal void RegisterHwndForInput(InputManager inputManager, PresentationSource inputSource)
        {
            HwndSource hwndSource = (HwndSource)inputSource;

            GetAndCacheTransformToDeviceMatrix(hwndSource);

            // Keep track so we don't bother looking for changes if someone happened to query this before
            // an Avalon window was created where we get TabletAdd/Removed notification.
            bool initializedTablets = (_tabletDeviceCollection == null);

            // This causes EnableCore to be called on TabletPC systems which enabled stylus input!
            WispTabletDeviceCollection tablets = WispTabletDevices;

            lock (__penContextsLock)
            {
                if (__penContextsMap.ContainsKey(inputSource))
                {
                    throw new InvalidOperationException(SR.PenService_WindowAlreadyRegistered);
                }

                PenContexts penContexts = new PenContexts(StylusLogic.GetCurrentStylusLogicAs<WispLogic>(), inputSource);

                __penContextsMap[inputSource] = penContexts;

                // If FIRST one set this as the one to manage TabletAdded/Removed notifications.
                if (__penContextsMap.Count == 1)
                {
                    // Make sure our view of TabletDevices is up to date we didn't just cause it
                    // to be initialized and we had some real tablet devices.
                    if (!initializedTablets && tablets.Count > 0)
                    {
                        tablets.UpdateTablets();

                        // Update the last known device count.
                        _lastKnownDeviceCount = GetDeviceCount();
                    }

View on GitHub (pinned to 81131a70a4)