System.Drawing中的“System.ObjectDisposedException”.dll当尝试使用从SH

本文关键字:System SH dll 中的 Drawing ObjectDisposedException | 更新日期: 2023-09-27 18:01:00

我在获取文件夹图标或保持图标持久时遇到一些问题。我编写的允许我管理对文件和文件夹的所有图标函数的访问的类如下:

提供了完整的类,以便您可以比较用于检索图标的不同方法,并显示它在所有其他情况下使用类似的返回数据结构按预期工作。此问题侧重于使用来自GetFolderIcon的结果的问题,与GetFileIcons相比,返回数据的结构类似。您还可以使用 ShellFileGetInfo.IconCollections.shell32 来检索包含 300 多个图标的列表,这些图标也采用类似的结构。

public class ShellFileGetInfo
{
    [DllImport("shell32.dll")]
    private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
    [DllImport("user32.dll", EntryPoint = "DestroyIcon", SetLastError = true)]
    private static extern int DestroyIcon(IntPtr hIcon);
    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    private static extern int ExtractIconEx(string szFileName, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, int nIcons);
    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public IntPtr hIcon;
        public IntPtr iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    };
    // Enum for return of the file type
    public enum ShellFileType
    {
        FileNotFound,
        Unknown,
        Dos,
        Windows,
        Console
    }
    public class FolderIcons
    {
        public Icon open;
        public Icon closed;
    }
    public class FileIcons
    {
        public List<Icon> small;
        public List<Icon> large;
    }
    public static class IconCollections
    {
        private static FileIcons _explorer;
        public static FileIcons explorer
        {
            get
            {
                if(_explorer == null )
                {
                    _explorer = GetFileIcons(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"));
                }
                return _explorer;
            }
        }
        private static FileIcons _shell32;
        public static FileIcons shell32
        {
            get
            {
                if (_shell32 == null )
                {
                    _shell32 = GetFileIcons(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "shell32.dll"));
                }
                return _shell32;
            }
        }
    }
    // Apply the appropriate overlays to the file's icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_ADDOVERLAYS = 0x000000020;
    // Modify SHGFI_ATTRIBUTES to indicate that the dwAttributes member of the SHFILEINFO structure at psfi contains the specific attributes that are desired. These attributes are passed to IShellFolder::GetAttributesOf. If this flag is not specified, 0xFFFFFFFF is passed to IShellFolder::GetAttributesOf, requesting all attributes. This flag cannot be specified with the SHGFI_ICON flag.
    public const uint SHGFI_ATTR_SPECIFIED = 0x000020000;
    // Retrieve the item attributes. The attributes are copied to the dwAttributes member of the structure specified in the psfi parameter. These are the same attributes that are obtained from IShellFolder::GetAttributesOf.
    public const uint SHGFI_ATTRIBUTES = 0x000000800;
    // Retrieve the display name for the file, which is the name as it appears in Windows Explorer. The name is copied to the szDisplayName member of the structure specified in psfi. The returned display name uses the long file name, if there is one, rather than the 8.3 form of the file name. Note that the display name can be affected by settings such as whether extensions are shown.
    public const uint SHGFI_DISPLAYNAME = 0x000000200;
    // Retrieve the type of the executable file if pszPath identifies an executable file. The information is packed into the return value. This flag cannot be specified with any other flags.
    public const uint SHGFI_EXETYPE = 0x000002000;
    // Retrieve the handle to the icon that represents the file and the index of the icon within the system image list. The handle is copied to the hIcon member of the structure specified by psfi, and the index is copied to the iIcon member.
    public const uint SHGFI_ICON = 0x000000100;
    // Retrieve the name of the file that contains the icon representing the file specified by pszPath, as returned by the IExtractIcon::GetIconLocation method of the file's icon handler. Also retrieve the icon index within that file. The name of the file containing the icon is copied to the szDisplayName member of the structure specified by psfi. The icon's index is copied to that structure's iIcon member.
    public const uint SHGFI_ICONLOCATION = 0x000001000;
    // Modify SHGFI_ICON, causing the function to retrieve the file's large icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_LARGEICON = 0x000000000;
    // Modify SHGFI_ICON, causing the function to add the link overlay to the file's icon. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_LINKOVERLAY = 0x000008000;
    // Modify SHGFI_ICON, causing the function to retrieve the file's open icon. Also used to modify SHGFI_SYSICONINDEX, causing the function to return the handle to the system image list that contains the file's small open icon. A container object displays an open icon to indicate that the container is open. The SHGFI_ICON and/or SHGFI_SYSICONINDEX flag must also be set.
    public const uint SHGFI_OPENICON = 0x000000002;
    // Version 5.0. Return the index of the overlay icon. The value of the overlay index is returned in the upper eight bits of the iIcon member of the structure specified by psfi. This flag requires that the SHGFI_ICON be set as well.
    public const uint SHGFI_OVERLAYINDEX = 0x000000040;
    // Indicate that pszPath is the address of an ITEMIDLIST structure rather than a path name.
    public const uint SHGFI_PIDL = 0x000000008;
    // Modify SHGFI_ICON, causing the function to blend the file's icon with the system highlight color. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_SELECTED = 0x000010000;
    // Modify SHGFI_ICON, causing the function to retrieve a Shell-sized icon. If this flag is not specified the function sizes the icon according to the system metric values. The SHGFI_ICON flag must also be set.
    public const uint SHGFI_SHELLICONSIZE = 0x000000004;
    // Modify SHGFI_ICON, causing the function to retrieve the file's small icon. Also used to modify SHGFI_SYSICONINDEX, causing the function to return the handle to the system image list that contains small icon images. The SHGFI_ICON and/or SHGFI_SYSICONINDEX flag must also be set.
    public const uint SHGFI_SMALLICON = 0x000000001;
    // Retrieve the index of a system image list icon. If successful, the index is copied to the iIcon member of psfi. The return value is a handle to the system image list. Only those images whose indices are successfully copied to iIcon are valid. Attempting to access other images in the system image list will result in undefined behavior.
    public const uint SHGFI_SYSICONINDEX = 0x000004000;
    // Retrieve the string that describes the file's type. The string is copied to the szTypeName member of the structure specified in psfi.
    public const uint SHGFI_TYPENAME = 0x000000400;
    // Indicates that the function should not attempt to access the file specified by pszPath. Rather, it should act as if the file specified by pszPath exists with the file attributes passed in dwFileAttributes. This flag cannot be combined with the SHGFI_ATTRIBUTES, SHGFI_EXETYPE, or SHGFI_PIDL flags.
    public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
    /// <summary>
    /// Get a list of open and closed icons for the specified folder
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static FolderIcons GetFolderIcon(string path)
    {
        FolderIcons fi = new FolderIcons();
        SHFILEINFO shInfo = new SHFILEINFO();
        IntPtr ptr = new IntPtr();
        try
        {
            ptr = SHGetFileInfo(path, 0, ref shInfo, (uint)Marshal.SizeOf(shInfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS);
            if (ptr != IntPtr.Zero)
            {
                fi.closed = Icon.FromHandle(shInfo.hIcon);
            }
        }
        catch (Exception)
        {
            fi.closed = null;
        } finally
        {
            if(shInfo.hIcon != IntPtr.Zero)
            {
                DestroyIcon(shInfo.hIcon);
            }
        }
        try {
            ptr = SHGetFileInfo(path, 0, ref shInfo, (uint)Marshal.SizeOf(shInfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS | SHGFI_OPENICON);
            if (ptr != IntPtr.Zero) {
                fi.open = Icon.FromHandle(shInfo.hIcon);
            }
        }
        catch (Exception)
        {
            fi.closed = null;
        } finally
        {
            if(shInfo.hIcon != IntPtr.Zero)
            {
                DestroyIcon(shInfo.hIcon);
            }
        }
        return fi;
    }
    /// <summary>
    /// Determine the type of executable the file is
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    public static ShellFileType GetFileType(string file)
    {
        ShellFileType type = ShellFileType.FileNotFound;
        if (File.Exists(file))
        {
            SHFILEINFO shinfo = new SHFILEINFO();
            IntPtr ptr = SHGetFileInfo(file, 128, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_EXETYPE);
            int wparam = ptr.ToInt32();
            int loWord = wparam & 0xffff;
            int hiWord = wparam >> 16;
            type = ShellFileType.Unknown;
            if (wparam != 0)
            {
                if (hiWord == 0x0000 && loWord == 0x5a4d)
                {
                    type = ShellFileType.Dos;
                }
                else if (hiWord == 0x0000 && loWord == 0x4550)
                {
                    type = ShellFileType.Console;
                }
                else if ((hiWord != 0x0000) && (loWord == 0x454E || loWord == 0x4550 || loWord == 0x454C))
                {
                    type = ShellFileType.Windows;
                }
            }
        }
        return type;
    }
    /// <summary>
    /// Get a single icon from a file
    /// </summary>
    /// <param name="resource"></param>
    /// <param name="largeIcon"></param>
    /// <returns></returns>
    public static Icon GetFileIcon(string resource, bool largeIcon = false)
    {
        SHFILEINFO shinfo = new SHFILEINFO();
        Icon icon;
        try
        {
            IntPtr ico;
            if (largeIcon == true)
            {
                ico = SHGetFileInfo(resource, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SMALLICON);
            }
            else
            {
                ico = SHGetFileInfo(resource, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON);
            }
            icon = (Icon)Icon.FromHandle(shinfo.hIcon);
        }
        catch (Exception)
        {
            icon = null;
        }
        finally
        {
            if (shinfo.hIcon != IntPtr.Zero)
            {
                DestroyIcon(shinfo.hIcon);
            }
        }
        return icon;
    }
    /// <summary>
    /// Get all large and small icons from a file
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    public static FileIcons GetFileIcons(string file)
    {
        FileIcons icons = new FileIcons()
        {
            small = new List<Icon>(),
            large = new List<Icon>()
        };
        IntPtr[] large = new IntPtr[999];
        IntPtr[] small = new IntPtr[999];
        Icon ico;
        try
        {
            int count = ExtractIconEx(file, -1, large, small, 999);
            if (count > 0)
            {
                large = new IntPtr[count - 1];
                small = new IntPtr[count - 1];
                ExtractIconEx(file, 0, large, small, count);
                foreach (var x in large)
                {
                    if (x != IntPtr.Zero)
                    {
                        ico = (Icon)Icon.FromHandle(x).Clone();
                        icons.large.Add(ico);
                    }
                }
                foreach (var x in small)
                {
                    if (x != IntPtr.Zero)
                    {
                        ico = (Icon)Icon.FromHandle(x).Clone();
                        icons.small.Add(ico);
                    }
                }
            }
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.Message);
        }
        finally
        {
            foreach (IntPtr ptr in large)
            {
                if (ptr != IntPtr.Zero)
                {
                    DestroyIcon(ptr);
                }
            }
            foreach (IntPtr ptr in small)
            {
                if (ptr != IntPtr.Zero)
                {
                    DestroyIcon(ptr);
                }
            }
        }
        return icons;
    }
}

当我尝试使用负责获取文件夹的打开和关闭图标的 GetFolderIcon 函数时FolderIcons,它返回一个值,但是当我尝试将其添加到我的列表视图时,我得到以下异常:

引发的异常:System.Drawing中的"System.ObjectDisposedException.dll

我用来使用该函数并将图标添加到列表视图的图像集合的代码如下:

// Initialize the imagelist object
listView1.LargeImageList = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) };
// Create a new listview item
ListViewItem lvi = new ListViewItem()
{
    Text = "My Computer",
    ImageIndex = viewer1.LargeImageList.Images.Count,
    StateImageIndex = viewer1.LargeImageList.Images.Count
};
// Retrieve the open/closed icons into a class 'x' (i tried as a struct with the same result)
var x = ShellFileGetInfo.GetFolderIcon(
        Environment.GetFolderPath(
            Environment.SpecialFolder.Desktop
        )
    );
 // Try adding the icon to the listview's image collection
listView1.LargeImageList.Images.Add(x.closed);
// Add the listview item to the listview
listView1.Items.Add(lvi);

重要的是要注意,在使用 GetFileIconGetFileIcons 方法时,我没有这个问题,这些方法在需要时调用 DestroyIcon() 后以类似的结构返回其结果。

我已经查看了其他几篇声称 使用 SHGetFileInformation 检索文件夹图标的堆栈溢出文章,但是这些方法似乎都不起作用。我上面的类是找到的最佳方法的混合体,以及 MSDN 文章中有关此方法的实现。 我还向每个常量添加了内联的 MSDN 注释,描述了其目的和用途。

我经历过的一些堆栈 溢出链接:

  • 如何使用 Shell32.SHGetFileInfo 获取 Windows 7 上的文件夹图标
  • 如何获取任何文件/文件夹的系统图标
  • 如何获取与特定文件夹关联的图标?
  • 获取驱动器和目录的图标:Icon.ExtractAssociatedIcon(filePath( 不起作用?
  • Icon.FromHandle:我应该处理它,还是调用DestroyIcon?

使用的其他一些引用不属于堆栈 溢出:

  • https://support.microsoft.com/en-us/kb/319350
  • https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85(.aspx
  • http://www.pinvoke.net/default.aspx/shell32.SHGetFileInfo
  • http://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
  • http://forums.codeguru.com/printthread.php?t=319455
  • https://bytes.com/topic/c-sharp/answers/812461-get-system-icon-any-file-folder

我看了其他几个,但没有一个功能齐全。我的方法,我希望根据 MSDN 文档将系统中的覆盖图标添加到图标中,该文档描述了使用 SHGFI_ADDOVERLAYS 常量将自动合并覆盖(如果需要(与图标并返回组合结果。从本质上讲,我的GetFolderIcon方法设计为足够智能,可以处理打开和关闭状态图标的返回,以及根据需要已应用的任何叠加层。

System.Drawing中的“System.ObjectDisposedException”.dll当尝试使用从SH

检索文件夹图标的代码只是检索参考信息,而不是实际获取图标数据。

若要解决此问题,需要使用 .Clone() 方法来复制图标本身。

而不是:

 Icon foo = Icon.FromHandle(shInfo.hIcon);

用:

 Icon foo = (Icon)Icon.FromHandle(shInfo.hIcon).Clone();

这是更正GetFolderIcon方法

/// <summary>
/// Get a list of open and closed icons for the specified folder
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static FolderIcons GetFolderIcon(string path)
{
    FolderIcons fi = new FolderIcons();
    SHFILEINFO shInfo = new SHFILEINFO();
    IntPtr ptr = new IntPtr();
    try
    {
        ptr = SHGetFileInfo(path, 0, ref shInfo, (uint)Marshal.SizeOf(shInfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS);
        if (ptr != IntPtr.Zero)
        {
            fi.closed = (Icon)Icon.FromHandle(shInfo.hIcon).Clone();
        }
    }
    catch (Exception)
    {
        fi.closed = null;
    } finally
    {
        if(shInfo.hIcon != IntPtr.Zero)
        {
            DestroyIcon(shInfo.hIcon);
        }
    }
    try {
        ptr = SHGetFileInfo(path, 0, ref shInfo, (uint)Marshal.SizeOf(shInfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS | SHGFI_OPENICON);
        if (ptr != IntPtr.Zero) {
            fi.open = (Icon)Icon.FromHandle(shInfo.hIcon).Clone();
        }
    }
    catch (Exception)
    {
        fi.closed = null;
    } finally
    {
        if(shInfo.hIcon != IntPtr.Zero)
        {
            DestroyIcon(shInfo.hIcon);
        }
    }
    return fi;
}