Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SafeFileHandle.Close throws an exception but the handle is valid and works

Tags:

c#

wpf

handle

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.

like image 980
kitti Avatar asked Feb 09 '12 20:02

kitti


1 Answers

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);
    }
}
like image 170
ordag Avatar answered Oct 08 '22 16:10

ordag