dotnet/wpf · error · Win32Exception

SR.Cursor_LoadImageFailure (fileName)

Error message

SR.Cursor_LoadImageFailure (fileName)

What it means

Cursor.LoadFromFile calls the Win32 LoadImage API to load a cursor from a .cur/.ani file. When LoadImage fails with ERROR_FILE_NOT_FOUND (2) or ERROR_PATH_NOT_FOUND (3), the code throws a Win32Exception carrying that code with the 'Cursor_LoadImageFailure' message that includes the file name. This tells you the cursor file path passed to the Cursor constructor does not exist on disk.

Solutions

  1. Verify the cursor file exists at the exact path using File.Exists before constructing the Cursor.
  2. Use an absolute path or Path.Combine(AppContext.BaseDirectory, relativePath) instead of a working-directory-relative path.
  3. Ensure the .cur/.ani file is included in project output (set Copy to Output Directory = Copy if newer for Content/Resource files).
  4. Fall back to Cursors.Arrow (or another built-in cursor) when the custom file cannot be loaded.
  5. Catch Win32Exception around Cursor construction and surface a friendly message naming the missing file.

Example fix

// before
cursor = new Cursor("Cursors\\hand.cur");
// after
string path = Path.Combine(AppContext.BaseDirectory, "Cursors", "hand.cur");
cursor = File.Exists(path) ? new Cursor(path) : Cursors.Arrow;
Defensive patterns

Strategy: validation

Validate before calling

string path = Path.Combine(AppContext.BaseDirectory, "Cursors", "hand.cur");
if (!File.Exists(path)) throw new FileNotFoundException("Cursor file missing", path);
Cursor cursor = new Cursor(path);

Type guard

bool IsValidCursorFile(string path) => File.Exists(path) && new FileInfo(path).Length > 0 && string.Equals(Path.GetExtension(path), ".cur", StringComparison.OrdinalIgnoreCase);

Try / catch

try { cursor = new Cursor(cursorPath); }
catch (Win32Exception ex) when (ex.NativeErrorCode == 2 || ex.NativeErrorCode == 3)
{ logger.LogWarning("Cursor file not found: {Path}", cursorPath); cursor = Cursors.Arrow; }

Prevention

When it happens

Trigger: new Cursor(path) or Cursor.SetCursor with a fileName string where LoadImage returns 0 and errorCode is ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND — i.e. the file or an intermediate directory does not exist.

Common situations: Hard-coded paths like 'Cursors\hand.cur' relative to a working directory that differs at runtime; files not deployed/copied to output directory (missing 'Copy to Output Directory'); missing cursor resource files in packaged ClickOnce/MSIX apps; case or extension typos.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Cursor.cs:186

                                                                NativeMethods.LR_LOADFROMFILE |
                                                                (_scaleWithDpi? NativeMethods.LR_DEFAULTSIZE : 0x0000));

            int errorCode = Marshal.GetLastWin32Error();
            if (_cursorHandle == null || _cursorHandle.IsInvalid)
            {
                // LoadImage returns a null handle but does not set
                // the error condition when icon file is of an incorrect type (e.g., .bmp)
                //
                // LoadImage has a bug where it doesn't set the correct error code
                // when a file is given that is not an ico file.  Icon load fails
                // but win32 error code is still zero (success).  Thus, we need to
                // special case this scenario.
                //
                if (errorCode != 0)
                {
                    if ((errorCode == NativeMethods.ERROR_FILE_NOT_FOUND) || (errorCode == NativeMethods.ERROR_PATH_NOT_FOUND))
                    {
                        throw new Win32Exception(errorCode, SR.Format(SR.Cursor_LoadImageFailure, fileName));
                    }
                    else
                    {
                        throw new Win32Exception(errorCode);
                    }
                }
                else
                {
                    throw new ArgumentException(SR.Format(SR.Cursor_LoadImageFailure, fileName));
                }
            }
        }

        //**** DEAD CODE - retained only for compat, if user sets quirk flag  ****
        private const int BUFFERSIZE = 4096; // the maximum size of the buffer used for loading from stream

        private void LegacyLoadFromStream(Stream cursorStream)
        {

View on GitHub (pinned to 81131a70a4)