18

first question here. I'm developing a program in C# (.NET 3.5) that displays files in a listview. I'd like to have the "large icon" view display the icon that Windows Explorer uses for that filetype, otherwise I'll have to use some existing code like this:

        private int getFileTypeIconIndex(string fileName)
    {
        string fileLocation = Application.StartupPath + "\\Quarantine\\" + fileName;
        FileInfo fi = new FileInfo(fileLocation);
        switch (fi.Extension)
        {
            case ".pdf":
                return 1;
            case ".doc": case ".docx": case ".docm": case ".dotx":case ".dotm": case ".dot":case ".wpd": case ".wps":
                return 2;
            default:
                return 0;
        }

    }

The above code returns an integer that is used to select an icon from an imagelist that I populated with some common icons. It works fine but I'd need to add every extension under the sun! Is there a better way? Thanks!

Paul Beesley
  • 767
  • 2
  • 10
  • 22

4 Answers4

12

You might find the use of Icon.ExtractAssociatedIcon a much simpler (an managed) approach than using SHGetFileInfo. But watch out: two files with the same extension may have different icons.

Martin Plante
  • 4,553
  • 3
  • 33
  • 45
  • 1
    Unfortunately, this function has many drawbacks and limitations where file/directory permissions often cause trouble and this function cannot be used for network shares. It's wishful thinking to believe that Microsoft would have made a .NET solution to supplement the need to use SHGetFileInfo PInvoke. Also make sure color depth is set correctly in any ImageList being used because sometimes that is crucial for making sure the icons look like they didn't come straight from Windows 3.1. – vincent gravitas Mar 28 '14 at 01:38
8

The file icons are held in the registry. It's a little convoluted but it works something like

  • Take the file extension and lookup the registry entry for it, for example .DOC Get the default value for that registry setting, "Word.Document.8"
  • Now lookup that value in the registry.
  • Look at the default value for the "Default Icon" registry key, in this case, C:\Windows\Installer{91120000-002E-0000-0000-0000000FF1CE}\wordicon.exe,1
  • Open the file and get the icon, using any number after the comma as the indexer.

There is some sample code at on CodeProject

blowdart
  • 55,577
  • 12
  • 114
  • 149
  • Everyone assumes that this is for files on the file system, but you have the only approach that appears to work purely based on file extension, which is the only approach that works well when building, say, a webdav or ftp client. – Brian Warshaw Jul 31 '12 at 18:53
  • While this is correct, note that it can be overriden per user in HKEY_CURRENTUSER. Take that into account too. – Alejandro May 22 '18 at 11:10
7

I used the following solution from codeproject in one of recent my projects

Obtaining (and managing) file and folder icons using SHGetFileInfo in C#

The demo project is pretty self explanatory but basically you just have to do:

private System.Windows.Forms.ListView FileView;

private ImageList _SmallImageList = new ImageList();
private ImageList _LargeImageList = new ImageList();
private IconListManager _IconListManager;

in the constructor:

_SmallImageList.ColorDepth = ColorDepth.Depth32Bit;
_LargeImageList.ColorDepth = ColorDepth.Depth32Bit;

_SmallImageList.ImageSize = new System.Drawing.Size(16, 16);
_LargeImageList.ImageSize = new System.Drawing.Size(32, 32);

_IconListManager = new IconListManager(_SmallImageList, _LargeImageList);

FileView.SmallImageList = _SmallImageList;
FileView.LargeImageList = _LargeImageList;

and then finally when you create the ListViewItem:

ListViewItem item = new ListViewItem(file.Name, _IconListManager.AddFileIcon(file.FullName));

Worked great for me.

Community
  • 1
  • 1
eric
  • 1,857
  • 5
  • 23
  • 33
4

Edit: Here is a version without PInvoke.

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

public const uint SHGFI_ICON = 0x100;
public const uint SHGFI_LARGEICON = 0x0; // 'Large icon
public const uint SHGFI_SMALLICON = 0x1; // 'Small icon

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

[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);

public static System.Drawing.Icon GetSystemIcon(string sFilename)
{
    //Use this to get the small Icon
    IntPtr hImgSmall; //the handle to the system image list
    //IntPtr hImgLarge; //the handle to the system image list
    APIFuncs.SHFILEINFO shinfo = new APIFuncs.SHFILEINFO();
    hImgSmall = APIFuncs.SHGetFileInfo(sFilename, 0, ref shinfo,
        (uint)Marshal.SizeOf(shinfo), APIFuncs.SHGFI_ICON | APIFuncs.SHGFI_SMALLICON);

    //Use this to get the large Icon
    //hImgLarge = SHGetFileInfo(fName, 0, 
    //  ref shinfo, (uint)Marshal.SizeOf(shinfo), 
    //  Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON);

    //The icon is returned in the hIcon member of the shinfo struct
    System.Drawing.Icon myIcon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shinfo.hIcon).Clone();
    DestroyIcon(shinfo.hIcon); // Cleanup
    return myIcon;
}
Community
  • 1
  • 1
dummy
  • 4,256
  • 3
  • 25
  • 36
  • This is a good answer, BUT you always need to consider maintainability. Using any extern calls should always be a last resort as you have no guarantee that they will work for different versions of OS, service packs, other applications, etc. I'm not saying "don't use this solution", what I am saying is 'consider the cost benefit of the solution'. Do you REALLY need fancier icons? – Derek Nigel Bartram Dec 19 '13 at 17:52