2012-02-09 7 views
7

Tengo un problema con los cursores personalizados en una aplicación WPF. Yo uso el siguiente código para crear las Cursor objetos:SafeFileHandle.Close arroja una excepción pero el identificador es válido y funciona

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

Cuando cierro mi solicitud y GC empieza a recoger la basura, se produce una excepción:

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: 

hice un poco de investigación adicional mediante la colocación de un punto de interrupción en if (handle.IsClosed) y usando la ventana inmediata para llamar al handle.Close(). Algunos de los SafeFileHandle se cierran muy bien, otros arrojan la misma excepción — inmediatamente después de que se creó el identificador.

Y solo para hacer las cosas divertidas, los mangos funcionan bien. IsInvalid es falso, IsClosed es falso y aparecen los cursores. Es solo que algunos de los mangos nunca se pueden cerrar.

Como nunca tengo la intención de cerrar manualmente los mangos, y solo se cerrarán durante la finalización de los objetos Cursor cuando se cierre la aplicación, es posible que pueda ignorarlos. No he probado una versión de lanzamiento fuera de VS2010 y no sé si eso hará que aparezca un cuadro de diálogo de bloqueo. Pero incluso si puedo ignorarlos, sigue siendo desordenado.

Así que, básicamente, estoy buscando información sobre lo que podría estar pasando mal aquí, dónde buscar para intentar y depurar esto ... todo parece estar en código nativo o GC y no puedo depurar nada de eso .

Respuesta

15

Usted está envolviendo el HICON regresaron de CreateIconIndirect en un SafeFileHandle el cual, en la liberación, llama CloseHandle en el HICON en lugar de la DestroyIcon necesario. No envuelva en HICONSafeFileHandle pero en lugar de una propia, especializada 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); 
    } 
} 
+0

Gracias! Soy bastante nuevo en WPF, nunca hice WinForms, y esta es la primera vez que tengo que lidiar con código nativo. Y gracias por eliminar su comentario y hacer de esto una respuesta. :) –

+1

@RyanP De nada. Tales cosas son fáciles de perder, especialmente si todo parece funcionar a primera vista. – ordag

+0

¡Gracias por esa solución! – Kai

Cuestiones relacionadas