dotnet/wpf · error · InvalidOperationException
InvalidOperationException
Error message
InvalidOperationException
What it means
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.
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
Example fix
// before hwndSubclass.Detach(); SendMessage(hwnd, WM_CLOSE, ...); // messages still arriving on detached subclass // after SendMessage(hwnd, WM_CLOSE, ...); // let window tear down first hwndSubclass.Detach();
Defensive patterns
Strategy: try-catch
Validate before calling
if (hwndSubclass.Detached) { hwndSubclass = new HwndSubclass(handler); hwndSubclass.Attach(hwnd); } Try / catch
try { subclass.Attach(hwnd); } catch (InvalidOperationException) { subclass = new HwndSubclass(handler); subclass.Attach(hwnd); } Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
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
- ArgumentNullException
- E_FAIL
- FILTER_E_ACCESS
- InvalidOperationException
- InvalidOperationException (no message)
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/6617e6b453185916.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/HwndSubclass.cs:276
internal IntPtr SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
{
IntPtr retval = IntPtr.Zero;
bool handled = false;
WindowMessage message = (WindowMessage)msg;
// If we are unattached and we receive a message, then we must have
// been used as the original window proc. In this case, we insert
// ourselves as if the original window proc had been DefWindowProc.
// We pass in DefWndProcStub as a workaround for a bug in UxTheme on
// Windows XP. For details see the comment on the DefWndProcWrapper method.
if(_bond == Bond.Unattached)
{
HookWindowProc(hwnd, new NativeMethods.WndProc(SubclassWndProc),
Marshal.GetFunctionPointerForDelegate(DefWndProcStub));
}
else if(_bond == Bond.Detached)
{
throw new InvalidOperationException();
}
IntPtr oldWndProc = _oldWndProc; // in case we get detached during this method
if(message == DetachMessage)
{
// We received our special message to detach. Make sure it is intended
// for us by matching the bridge.
if(wParam == IntPtr.Zero || wParam == (IntPtr)_gcHandle)
{
int param = (int)lParam; // 0 - normal, 1 - force, 2 - force and forward
bool force = (param > 0);
retval = CriticalDetach(force) ? new IntPtr(1) : IntPtr.Zero ;
handled = (param < 2);
}
}
elseView on GitHub (pinned to 81131a70a4)