Search code examples
c#iconspinvokehandle

Method failing after 3310 successes


I have inherited the following extension method which creates an ImageSource object based on a file path

public static class ImageSourceExtensions
{
    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    };

    public const uint SHGFI_ICON = 0x100;
    public const uint SHGFI_LARGEICON = 0x0;

    [DllImport("shell32.dll")]
    public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

    public static ImageSource GetIconFromFolder(this string filePath)
    {
        SHFILEINFO shinfo = new SHFILEINFO();
        SHGetFileInfo(filePath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo),
                SHGFI_ICON | SHGFI_LARGEICON);

        using (Icon i = Icon.FromHandle(shinfo.hIcon))
        {
            //Convert icon to a Bitmap source
            ImageSource img = Imaging.CreateBitmapSourceFromHIcon(
                                    i.Handle,
                                    new Int32Rect(0, 0, i.Width, i.Height),
                                    BitmapSizeOptions.FromEmptyOptions());

            return img;
        }
    }
}

This extension method works perfectly for both folders and files 3310 times. On the 3311th method call, the following exception is thrown:

'Win32 handle that was passed to Icon is not valid or is the wrong type'

This error is thrown when using the shinfo.hIcon property.

The error was initially thrown in my main application and was on a different file each time. I have since extracted this class into a new project and can get the same error to be thrown (after the same number of times) by doing this on a button click:

String path = @"C:\Generic Folder\Generic Document.pdf";

        for (int i = 0; i <4000; i++)
        {
            ImageSource img = path.GetIconFromFolder();
        }

Does anybody know of an obvious cause for this?


Solution

  • You are running out of HANDLEs. Windows has a finite number of handles and when you get one (in this case a handle to an icon), you are expected to release it after you no longer use it. If you don't, Windows will run out of free handles to throw around.

    If SHGetFileInfo returns an icon handle in the hIcon member of the SHFILEINFO structure pointed to by psfi, you are responsible for freeing it with DestroyIcon when you no longer need it.

    So you will need to PInvoke DestroyIcon and pass your shinfo.hIcon to it at the end of your function.

    The disposal of the Icon created with FromHandle will not destroy the original handle:

    When using this method, you must dispose of the original icon by using the DestroyIcon method in the Win32 API to ensure that the resources are released.