Created
October 27, 2025 03:52
-
-
Save xv/43d3132a2e4b70a98f5a7815de2479c7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.ComponentModel; | |
| using System.Windows.Interop; | |
| using System.Windows.Input; | |
| using System.Runtime.InteropServices; | |
| using System.IO; | |
| using Windows.Win32.Foundation; | |
| using Windows.Win32.UI.WindowsAndMessaging; | |
| using Windows.Win32; | |
| using Microsoft.Win32.SafeHandles; | |
| internal class SafeCursorHandle : SafeHandleZeroOrMinusOneIsInvalid | |
| { | |
| public SafeCursorHandle(nint handle) : base(true) => SetHandle(handle); | |
| public static SafeCursorHandle FromHCursor(nint hcursor) => new(hcursor); | |
| protected override bool ReleaseHandle() | |
| { | |
| if (!IsInvalid) | |
| { | |
| if (!PInvoke.DestroyCursor((HCURSOR)handle)) | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| handle = nint.Zero; | |
| } | |
| return true; | |
| } | |
| } | |
| internal class CursorLoader : IDisposable | |
| { | |
| private readonly SafeCursorHandle _handle; | |
| private bool _disposed; | |
| private CursorLoader(SafeCursorHandle handle) => | |
| _handle = handle ?? throw new ArgumentNullException(nameof(handle)); | |
| public void Dispose() | |
| { | |
| if (_disposed) | |
| return; | |
| _handle.Dispose(); | |
| _disposed = true; | |
| } | |
| private unsafe static PCWSTR StringToPCWSTR(string str) | |
| { | |
| fixed (char* p = str) | |
| return new PCWSTR(p); | |
| } | |
| public static CursorLoader FromFile(string path) | |
| { | |
| if (string.IsNullOrWhiteSpace(path)) | |
| throw new ArgumentNullException(nameof(path)); | |
| HANDLE handle = PInvoke.LoadImage( | |
| HINSTANCE.Null, | |
| StringToPCWSTR(path), | |
| GDI_IMAGE_TYPE.IMAGE_CURSOR, | |
| 0, | |
| 0, | |
| // LR_DEFAULTSIZE allows scaling with DPI | |
| IMAGE_FLAGS.LR_DEFAULTSIZE | IMAGE_FLAGS.LR_LOADFROMFILE); | |
| if (handle == HCURSOR.Null) | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| return new CursorLoader(SafeCursorHandle.FromHCursor(handle)); | |
| } | |
| public static CursorLoader FromStream(Stream stream) | |
| { | |
| string? path = null; | |
| try | |
| { | |
| path = Path.GetTempFileName(); | |
| using (var fs = File.OpenWrite(path)) | |
| stream.CopyTo(fs); | |
| return FromFile(path); | |
| } | |
| finally | |
| { | |
| if (!string.IsNullOrEmpty(path) && File.Exists(path)) | |
| File.Delete(path); | |
| } | |
| } | |
| public Cursor ToWpfCursor() | |
| { | |
| if (_handle == null || _handle.IsInvalid) | |
| throw new InvalidOperationException("Cursor is not loaded or handle is invalid."); | |
| return CursorInteropHelper.Create(_handle); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| LoadImage | |
| DestroyCursor |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| private readonly Cursor _cursor; | |
| public SomeWindowConstructor() | |
| { | |
| // WPF's built-in Cursor instance constructor essentially does the same thing under the hood: | |
| // _cursor = new Cursor(cursorFile, scaleWithDpi) | |
| _cursor = CursorLoader.FromFile(@"C:\Windows\Cursors\aero_arrow.cur").ToWpfCursor(); | |
| Cursor = _cursor; | |
| Closing += (_, _) => | |
| { | |
| if (Cursor == _cursor) | |
| { | |
| Cursor = null; // Necessary to release the reference to _cursor | |
| _cursor?.Dispose(); | |
| } | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment