{"record":{"id":"6617e6b453185916","repo":"dotnet/wpf","slug":"invalidoperationexception-hwndsubclass","errorCode":null,"errorMessage":"InvalidOperationException","messagePattern":"InvalidOperationException","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/HwndSubclass.cs","lineNumber":276,"sourceCode":"        internal IntPtr SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)\n        {\n            IntPtr retval = IntPtr.Zero;\n            bool handled = false;\n            WindowMessage message = (WindowMessage)msg;\n\n            // If we are unattached and we receive a message, then we must have\n            // been used as the original window proc.  In this case, we insert\n            // ourselves as if the original window proc had been DefWindowProc.\n            // We pass in DefWndProcStub as a workaround for a bug in UxTheme on\n            // Windows XP. For details see the comment on the DefWndProcWrapper method.\n            if(_bond == Bond.Unattached)\n            {\n                HookWindowProc(hwnd, new NativeMethods.WndProc(SubclassWndProc),\n                    Marshal.GetFunctionPointerForDelegate(DefWndProcStub));\n            }\n            else if(_bond == Bond.Detached)\n            {\n                throw new InvalidOperationException();\n            }\n\n            IntPtr oldWndProc = _oldWndProc;    // in case we get detached during this method\n\n            if(message == DetachMessage)\n            {\n                // We received our special message to detach.  Make sure it is intended\n                // for us by matching the bridge.\n                if(wParam == IntPtr.Zero || wParam == (IntPtr)_gcHandle)\n                {\n                    int param = (int)lParam;    // 0 - normal, 1 - force, 2 - force and forward\n                    bool force = (param > 0);\n\n                    retval = CriticalDetach(force) ? new IntPtr(1) : IntPtr.Zero ;\n                    handled = (param < 2);\n                }\n            }\n            else","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/HwndSubclass.cs#L258-L294","documentation":"HwndSubclass manages the bond between a managed dispatcher hook and a Win32 window procedure. This InvalidOperationException signals an invalid state transition: the hook callback (SubclassWndProc) reached the attach path while the HwndSubclass is in the Detached bond state, meaning the window proc hook was already torn down.","triggerScenarios":"SubclassWndProc runs for a window message after the HwndSubclass was detached (Bond.Detached) but the hook callback is still invoked; typically a re-entrancy or race where Detach happened during message processing.","commonSituations":"Window destroyed and re-created while a control is still bound; calling Attach/Dispose sequences out of order; multithreaded access to the same HwndSubclass causing a detach mid-message-loop.","solutions":["Ensure Dispose/Detach is only called after the window handle is fully released and no messages are in flight","Do not call Attach on an HwndSubclass that has been detached; create a new HwndSubclass instead","Guard window lifetime so WndProc messages are not delivered after Detach (destroy the window before detaching)","Synchronize access to HwndSubclass from the thread that owns the HWND"],"exampleFix":"// before\nhwndSubclass.Detach();\nSendMessage(hwnd, WM_CLOSE, ...); // messages still arriving on detached subclass\n// after\nSendMessage(hwnd, WM_CLOSE, ...); // let window tear down first\nhwndSubclass.Detach();","handlingStrategy":"try-catch","validationCode":"if (hwndSubclass.Detached) { hwndSubclass = new HwndSubclass(handler); hwndSubclass.Attach(hwnd); }","typeGuard":null,"tryCatchPattern":"try { subclass.Attach(hwnd); } catch (InvalidOperationException) { subclass = new HwndSubclass(handler); subclass.Attach(hwnd); }","preventionTips":["Never reuse an HwndSubclass after Detach — allocate a new one","Destroy the window before detaching so no messages arrive post-detach","Keep all HwndSubclass access on the HWND's owning thread","Guard against double init of attach paths (template/Loaded/OnSourceInitialized)"],"tags":["wpf","win32","window-proc","state"],"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-22T01:17:13.364Z"}