{"record":{"id":"1ba96c6afadceb0d","repo":"dotnet/wpf","slug":"sr-penservice-disposed","errorCode":null,"errorMessage":"SR.Penservice_Disposed","messagePattern":"SR\\.Penservice_Disposed","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Stylus/Wisp/PenThreadWorker.cs","lineNumber":511,"sourceCode":"        {\n            if(!__disposed)\n            {\n                __disposed = true;\n                \n                // Kick thread to wake up and see we are disposed.\n                MS.Win32.Penimc.UnsafeNativeMethods.RaiseResetEvent(_pimcResetHandle);\n                // Let it destroy the reset event.\n            }\n            GC.KeepAlive(this);\n        }\n\n        /////////////////////////////////////////////////////////////////////\n\n        internal bool WorkerAddPenContext(PenContext penContext)\n        {\n            if (__disposed)\n            {\n                throw new ObjectDisposedException(null, SR.Penservice_Disposed);\n            }\n\n            Debug.Assert(penContext != null);\n            \n            WorkerOperationAddContext addContextOperation = new WorkerOperationAddContext(penContext, this);\n\n            lock(_workerOperationLock)\n            {\n                _workerOperation.Add(addContextOperation);\n            }\n\n            // Kick thread to do this work.\n            MS.Win32.Penimc.UnsafeNativeMethods.RaiseResetEvent(_pimcResetHandle);\n\n            // Wait for this work to be completed.\n            addContextOperation.DoneEvent.WaitOne();\n            addContextOperation.DoneEvent.Close();\n","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Stylus/Wisp/PenThreadWorker.cs#L493-L529","documentation":"PenThreadWorker.AddPenContext (WorkerAddPenContext) throws ObjectDisposedException (SR.Penservice_Disposed) when the pen thread worker has already been disposed but a new pen context is added to it. Once the stylus worker thread is shutting down, no further pen contexts may be registered.","triggerScenarios":"Adding a PenContext to the worker after the PenService/worker thread was disposed — e.g. a window/tablet is torn down while another stylus device still attempts registration, or re-enabling a HwndSource's stylus support after shutdown.","commonSituations":"Closing one window while pen events are still in flight; rapid open/close of windows with stylus input; disposing the PenService and then re-attaching a tablet device; shutdown races during application exit.","solutions":["Guard the worker's lifetime: do not add pen contexts after disposal; recreate a new PenThreadWorker if needed","Ensure windows' stylus support is disabled before the worker disposes","Check for races in window close/dispose ordering when multiple HwndSources share the pen service","Catch ObjectDisposedException around context registration during teardown"],"exampleFix":"// before\nworker.WorkerAddPenContext(penContext); // may already be disposed during shutdown\n// after\nif (!worker.IsDisposed)\n    worker.WorkerAddPenContext(penContext);","handlingStrategy":"try-catch","validationCode":"if (!worker.IsDisposed)\n    worker.WorkerAddPenContext(penContext);","typeGuard":null,"tryCatchPattern":"try { worker.WorkerAddPenContext(penContext); }\ncatch (ObjectDisposedException) { /* worker shutting down; skip registration */ }","preventionTips":["Track worker lifetime explicitly and null it after dispose","Never re-register pen contexts during shutdown","Sequence window close before pen service disposal"],"tags":["wpf","stylus","wisp","object-disposed","shutdown-race"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}