dotnet/wpf · error · ElementNotAvailableException

ElementNotAvailableException

Error message

ElementNotAvailableException

What it means

Misc.GetWindowThreadProcessId throws ElementNotAvailableException when UnsafeNativeMethods.GetWindowThreadProcessId returns threadId == 0, which means the HWND is no longer valid — the window has been destroyed. The library signals this as an unavailable element rather than a raw Win32 failure.

Solutions

  1. Validate the handle with IsWindow(hwnd) before calling UIA APIs on it
  2. Re-acquire the element from a fresh tree walk instead of reusing cached providers
  3. Catch ElementNotAvailableException around property access and treat it as 'element gone'
  4. Fix lifetime management in your automation so handles are not outlived by their windows

Example fix

// before
uint threadId = Misc.GetWindowThreadProcessId(hwnd, out processId); // throws if hwnd dead
// after
if (SafeNativeMethods.IsWindow(hwnd))
{
    uint threadId = Misc.GetWindowThreadProcessId(hwnd, out processId);
}
else
{
    processId = 0; // window destroyed; skip or re-walk tree
}
Defensive patterns

Strategy: validation

Validate before calling

if (!SafeNativeMethods.IsWindow(hwnd)) { processId = 0; return; } // window destroyed
var pid = Misc.GetWindowThreadProcessId(hwnd, out _);

Type guard

bool IsValidWindow(IntPtr h) => h != IntPtr.Zero && IsWindow(h);

Try / catch

try { tid = Misc.GetWindowThreadProcessId(hwnd, out pid); } catch (ElementNotAvailableException) { pid = 0; /* window closed */ }

Prevention

When it happens

Trigger: Calling GetWindowThreadProcessId(hwnd) on a destroyed or never-created window handle — commonly when reading element runtime-id/process properties after the target window closed.

Common situations: App under automation exits mid-test; cached HWND from an earlier enumeration is used after the window closed; invalid IntPtr passed to UIA client helpers.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationClientSideProviders/MS/Internal/AutomationProxies/Misc.cs:702

            int lastWin32Error = 0;
            int style = UnsafeNativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_STYLE, out lastWin32Error);

            if (style == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }

            return style;
        }

        internal static uint GetWindowThreadProcessId(IntPtr hwnd, out uint processId)
        {
            // GetWindowThreadProcessId does use SetLastError().  So a call to GetLastError() would be meanless.
            uint threadId = UnsafeNativeMethods.GetWindowThreadProcessId(hwnd, out processId);

            if (threadId == 0)
            {
                throw new ElementNotAvailableException();
            }

            return threadId;
        }

        internal static short GlobalAddAtom(string atomName)
        {
            short atom = UnsafeNativeMethods.GlobalAddAtom(atomName);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (atom == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
            return atom;
        }

        internal static short GlobalDeleteAtom(short atom)
        {

View on GitHub (pinned to 81131a70a4)