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
- Validate the handle with IsWindow(hwnd) before calling UIA APIs on it
- Re-acquire the element from a fresh tree walk instead of reusing cached providers
- Catch ElementNotAvailableException around property access and treat it as 'element gone'
- 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
- Validate HWNDs with IsWindow immediately before every native call
- Bind element lifetime to window lifetime notifications
- Avoid holding HWNDs across operations that can close the app
- Handle app exit during automation as a normal, expected condition
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
- ElementNotAvailableException
- ElementNotEnabledException
- InvalidOperationException(SR.OperationCannotBePerformed, e)
- Win32Exception
- Win32Exception(Marshal.GetLastWin32Error())
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)