dotnet/wpf · error · InvalidOperationException

ERROR_NOACCESS (998) / ERROR_ACCESS_DENIED (5)

ERROR_NOACCESS (998) / ERROR_ACCESS_DENIED (5)

Error message

InvalidOperationException

What it means

The Win32 error mapper throws InvalidOperationException for ERROR_NOACCESS (998, invalid access to a memory location) and ERROR_ACCESS_DENIED (5). The native call was refused — either a bad memory access or insufficient rights — and the library maps this to the standard 'operation cannot be performed' exception rather than a UIA-specific one.

Solutions

  1. Run the automation client at the same or higher elevation/integrity level as the target app
  2. Avoid automating secure UI (UAC consent, elevated windows) — UIPI blocks it by design
  3. Catch InvalidOperationException and treat the target as blocked rather than broken
  4. If 998 appears in your own P/Invoke, check buffer sizes and marshaling before blaming the target

Example fix

// before
var info = GetWindowInfo(hwnd); // error 5/998 -> InvalidOperationException
// after
try
{
    var info = GetWindowInfo(hwnd);
}
catch (InvalidOperationException)
{
    // access denied / invalid access: target likely elevated; require matching elevation
    info = null;
}
Defensive patterns

Strategy: validation

Validate before calling

// Compare integrity levels before automating a window:
bool canAccess = GetProcessIntegrityLevel(targetPid) <= GetProcessIntegrityLevel(currentPid);

Try / catch

try { QueryWindow(hwnd); } catch (InvalidOperationException) { ElevateOrSkip(target); }

Prevention

When it happens

Trigger: Win32 calls made by UIA client proxies failing with last error 998 or 5 — accessing a window of an elevated/other-session process, or a native API writing to an invalid buffer.

Common situations: Automating an app running elevated (admin) from a non-elevated automation process; UIPI/mandatory-integrity-level blocks on UAC prompts; cross-session (RDP/service) access attempts.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

                case 1404:  // 1404 ERROR_INVALID_HOOK_HANDLE       Invalid hook handle.
                case 1405:  // 1405 ERROR_INVALID_DWP_HANDLE        Invalid handle to a multiple-window position structure.
                case 1406:  // 1406 ERROR_TLW_WITH_WSCHILD          Cannot create a top-level child window.
                case 1407:  // 1407 ERROR_CANNOT_FIND_WND_CLASS     Cannot find window class.
                case 1408:  // 1408 ERROR_WINDOW_OF_OTHER_THREAD    Invalid window; it belongs to other thread.
                    throw new ElementNotAvailableException();

                // We're getting this in AMD64 when calling RealGetWindowClass; adding this code
                // to allow the DRTs to pass while we continue investigation.
                case 87:    //   87 ERROR_INVALID_PARAMETER
                    throw new ElementNotAvailableException();

                case 8:     //    8 ERROR_NOT_ENOUGH_MEMORY         Not enough storage is available to process this command.
                case 14:    //   14 ERROR_OUTOFMEMORY               Not enough storage is available to complete this operation.
                    throw new OutOfMemoryException();

                case 998:   //  998 ERROR_NOACCESS                  Invalid access to memory location.
                case 5:     //    5 ERROR_ACCESS_DENIED
                    throw new InvalidOperationException();

                case 121:   //  121 ERROR_SEM_TIMEOUT               The semaphore timeout period has expired.
                case 258:   //  258 WAIT_TIMEOUT                    The wait operation timed out.
                case 1053:  // 1053 ERROR_SERVICE_REQUEST_TIMEOUT   The service did not respond to the start or control request in a timely fashion.
                case 1460:  // 1460 ERROR_TIMEOUT                   This operation returned because the timeout period expired.
                    throw new TimeoutException();

                default:
                    // Not sure how to map the reset of the error codes so throw generic Win32Exception.
                    throw new Win32Exception(errorCode);
            }
        }

        internal static bool UnhookWinEvent(IntPtr winEventHook)
        {
            // There is no indication in the Windows SDK documentation that UnhookWinEvent()
            // will set an error to be retrieved with GetLastError
            return UnsafeNativeMethods.UnhookWinEvent(winEventHook);

View on GitHub (pinned to 81131a70a4)