I'm having a problem with custom cursors in a WPF application. I use the following code to create the Cursor
objects:
[DllImport("user32.dll")]
private static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
private static Cursor CreateCursor(string cursorName, System.Drawing.Bitmap bmp,
int xHotspot, int yHotspot, out SafeFileHandle handle)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotspot;
tmp.yHotspot = yHotspot;
tmp.fIcon = false;
IntPtr ptr = CreateIconIndirect(ref tmp);
handle = new SafeFileHandle(ptr, true);
if (handle.IsClosed)
{
return null;
}
Cursor cur = CursorInteropHelper.Create(handle);
return cur;
}
When I close my application and GC starts picking up the trash, it throws an exception:
System.Runtime.InteropServices.SEHException was unhandled
Message=External component has thrown an exception.
Source=mscorlib
ErrorCode=-2147467259
StackTrace:
at Microsoft.Win32.Win32Native.CloseHandle(IntPtr handle)
at Microsoft.Win32.SafeHandles.SafeFileHandle.ReleaseHandle()
at System.Runtime.InteropServices.SafeHandle.InternalDispose()
at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
at System.Runtime.InteropServices.SafeHandle.Dispose()
at System.Windows.Input.Cursor.Finalize()
InnerException:
I did some further investigation by placing a breakpoint on if (handle.IsClosed)
and using the immediate window to call handle.Close()
. Some of the SafeFileHandle
's close just fine, others throw the same exception — immediately after the handle was created.
And just to make things fun, the handles themselves work just fine. IsInvalid
is false, IsClosed
is false, and the cursors appear. It's just that some of the handles can never be closed.
As I never intend to close the handles manually, and they will only be closed during finalization of the Cursor
objects when the application closes, I might be able to just ignore them. I haven't tried a Release build outside VS2010 and I don't know if that will cause a crash dialog to appear. But even if I can ignore them, it's still messy.
So basically I'm looking for any info on what might be going wrong here, where to look to try and debug this... everything seems to be in native code or GC and I can't debug any of it.
You're wrapping the HICON
returned from CreateIconIndirect
in a SafeFileHandle
which, on releasing, calls CloseHandle
on the HICON
instead of the needed DestroyIcon
. Don't wrap HICON
in SafeFileHandle
but instead in an own, specialized SafeHandle
:
class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DestroyIcon(
[In] IntPtr hIcon);
private SafeIconHandle()
: base(true)
{
}
public SafeIconHandle(IntPtr hIcon)
: base(true)
{
this.SetHandle(hIcon);
}
protected override bool ReleaseHandle()
{
return DestroyIcon(this.handle);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With