SafeFileHandle.Close выдает исключение, но дескриптор допустим и работает - PullRequest
7 голосов
/ 10 февраля 2012

У меня проблема с пользовательскими курсорами в приложении WPF.Я использую следующий код для создания объектов Cursor:

[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;
}

Когда я закрываю свое приложение, и GC начинает собирать мусор, оно выдает исключение:

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провел дальнейшее расследование, установив точку останова на if (handle.IsClosed) и используя непосредственное окно для вызова handle.Close().Некоторые из SafeFileHandle закрываются очень хорошо, другие выдают то же исключение - сразу после создания дескриптора.

И просто, чтобы сделать забавные вещи, сами ручки работают просто отлично.IsInvalid ложно, IsClosed ложно, и появляются курсоры.Просто некоторые дескрипторы никогда не могут быть закрыты.

Поскольку я никогда не собираюсь закрывать дескрипторы вручную, и они будут закрываться только во время финализации объектов Cursor при закрытии приложения, я мог бы бытьвозможность просто игнорировать их.Я не пробовал сборку Release вне VS2010, и я не знаю, приведет ли это к появлению диалогового окна сбоя.Но даже если я могу игнорировать их, это все еще грязно.

Так что в основном я ищу любую информацию о том, что может пойти не так, где искать, чтобы попытаться отладить это ... все кажетсяв нативном коде или GC, и я не могу отладить ничего из этого.

1 Ответ

16 голосов
/ 10 февраля 2012

Вы оборачиваете HICON, возвращенный из CreateIconIndirect, в SafeFileHandle, который при отпускании вызывает CloseHandle на HICON вместо необходимого DestroyIcon. Не оборачивайте HICON в SafeFileHandle, а вместо этого в собственную специализированную 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);
    }
}
...