2012-02-09 7 views
7

Sto riscontrando un problema con i cursori personalizzati in un'applicazione WPF. Io uso il seguente codice per creare le Cursor oggetti:SafeFileHandle.Close genera un'eccezione ma l'handle è valido e funziona

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

Quando chiudo la mia domanda e GC inizia raccogliendo la spazzatura, viene generata un'eccezione:

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: 

ho fatto qualche ulteriore indagine, ponendo un punto di interruzione su if (handle.IsClosed) e utilizzando la finestra immediata per chiamare handle.Close(). Alcuni degli SafeFileHandle chiudono bene, altri lanciano la stessa eccezione — immediatamente dopo la creazione dell'handle.

E proprio per rendere le cose divertenti, le maniglie stesse funzionano perfettamente. IsInvalid è falso, IsClosed è falso e vengono visualizzati i cursori. È solo che alcune delle maniglie non possono mai essere chiuse.

Come non ho mai intenzione di chiudere manualmente le maniglie, e saranno chiuse solo durante la finalizzazione degli oggetti Cursor quando l'applicazione si chiude, potrei essere in grado di ignorarle. Non ho provato una versione di Release al di fuori di VS2010 e non so se questo farà apparire una finestra di crash. Ma anche se posso ignorarli, è ancora disordinato.

Quindi in sostanza sto cercando informazioni su cosa potrebbe andare storto qui, dove cercare di provare e fare il debug di questo ... tutto sembra essere in codice nativo o GC e non riesco a eseguire il debug di nessuna di esse .

risposta

15

Stai avvolgendo il HICON tornato da CreateIconIndirect in una SafeFileHandle che, al rilascio, chiama CloseHandle sulla HICON invece della necessaria DestroyIcon. Non avvolgere HICON in SafeFileHandle ma invece in una propria, specializzata 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

Grazie! Sono abbastanza nuovo per WPF, non ho mai fatto WinForms, e questa è la mia prima volta che ho a che fare con il codice nativo. E grazie per aver rimosso il tuo commento e aver reso questa una risposta. :) –

+1

@RyanP Prego. Queste cose sono facili da perdere, soprattutto se tutto sembra funzionare al primo sguardo. – ordag

+0

Grazie per questa soluzione! – Kai

Problemi correlati