2016-08-05 2 views
-1

У меня возникли проблемы с получением значка папки или сохранением значка. Класс, который я написал, который позволяет мне управлять доступом ко всем функциям иконок для файлов и папок, выглядит следующим образом:'System.ObjectDisposedException' в System.Drawing.dll при попытке использовать значки, полученные для папки из SHGetFileInfo

Полный класс, предоставляемый для сравнения различных методов, используемых для извлечения значков, и для того, чтобы показать, что он работает как предназначенный во всех других случаях, используя аналогичную структуру для возвращаемых данных. Этот вопрос фокусируется на проблеме использования результата от 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, она возвращает значение, но когда я пытаюсь добавить его к моему ListView, я набираюсь следующее исключение:

исключение брошено: «System.ObjectDisposedException» в System.Drawing.dll

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

// 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); 

Важно отметить, что у меня нет этой проблемы при использовании GetFileIcon или GetFileIcons методов, которые возвращают свои результаты в аналогичной структуре после вызова DestroyIcon(), где требуется.

Я просмотрел несколько других файлов   Переполнение статей, которые требуют получить значок папки с помощью SHGetFileInfo, однако ни один из этих методов не работает. Мой класс выше - это гибрид лучших найденных методов, а также реализация из статей MSDN об этом. Я также добавил the MSDN notes inline для каждой константы, описывающей ее назначение и использование.

Некоторые из стека   Переливных ссылок я пережившие:

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

Было несколько других, которые я рассматривал, но ничего, что полностью функционально. Мой метод, я хочу добавить значок оверлея из системы в значок в соответствии с документацией MSDN, которая описывает использование константы SHGFI_ADDOVERLAYS, автоматически объединит оверлей, если с пиктограммой требуется какой-либо значок и вернуть объединенный результат. По сути, мой метод GetFolderIcon разработан, чтобы быть достаточно интеллектуальным, чтобы обрабатывать возврат как открытых, так и закрытых значков состояния, а также с любыми оверлеями, которые уже применяются по мере необходимости.

+0

[IconLib от CodeProject] (http://www.codeproject.com/Статьи/16178/IconLib-Icons-Unfolded-MultiIcon и Windows-Vista) могут помочь. [Это также может быть интересно (http://stackoverflow.com/a/35294365/1070452) – Plutonix

+1

Можете ли вы предоставить краткий обзор проблемы, которую вы испытываете в форме чего-то вроде модульного теста или простой программы ? Я ценю, что вы приложили много усилий в свой вопрос (+1), но трудно определить точную проблему (оверлейные значки?), Не говоря уже о том, чтобы выявить проблему. – stuartd

+1

Вы всегда вызываете 'DestroyIcon (shInfo.hIcon);' в конце концов, это, вероятно, проблема. –

ответ

0

Код для получения значка папки, был только для получения справочной информации, а не для получения данных значка.

Чтобы исправить это, для создания копии самой иконки необходим метод .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; 
} 
Смежные вопросы