Why doesn’t SHGetFileInfo give me customized folder icons?

A customer reported that they were unable to obtain cutomized folder icons. Whenever they asked for the icon for a folder, they always got a plain folder icon, Even if they asked for folders like My Documents, which come with their own custom icons. The customer says that they are using the code from this Web site:

public static System.Drawing.Icon GetFileIcon(string name, IconSize size, 
                                              bool linkOverlay)
    Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
    uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
    if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;

    /* Check the size specified for return. */
    if (IconSize.Small == size)
        flags += Shell32.SHGFI_SMALLICON ; // include the small icon flag
        flags += Shell32.SHGFI_LARGEICON ;  // include the large icon flag
    Shell32.SHGetFileInfo( name, 
        ref shfi, 
        (uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi), 
        flags );

    // Copy (clone) the returned icon to a new object, thus allowing us 
    // to call DestroyIcon immediately
    System.Drawing.Icon icon = (System.Drawing.Icon)
    User32.DestroyIcon( shfi.hIcon ); // Cleanup
    return icon;

As I noted some time ago, the SHGFI_USE­FILE­ATTRIBUTES flag means "Pretend that the file/directory exists, and that its file attributes are what I passed as the dwFileAttributes parameter. Do this regardless of whether it actually exists or not."

Since you're passing the flag, and you say "Trust me, it's a directory," the shell says, "Okay, then here's the icon for a standard directory. I can't give you anything better than that because you told me not to access the disk."

If you want to get the icon for a live file or directory, then remove the SHGFI_USE­FILE­ATTRIBUTES flag.

Bonus chatter: Yes, somebody posted a comment on that Web site asking how to get the function to work for special folders, and somebody else posted the same answer (namely, remove the SHGFI_USE­FILE­ATTRIBUTES flag). But that question/answer was posted long after our customer asked the question.

Comments (9)
  1. Brian_EE says:

    Another example of “You get what you pay for”.

  2. Koro says:

    This is what I hate about copy-paste code. People treating Win32 as a “black box” and wanting nothing to do with it, and only copy-pasting chunks of code from “p/invoke” websites without understanding.

    That and this code is not exception-safe (could leak the shfi.hIcon) and it does not Dispose the initial icon object before calling DestroyIcon.

    1. ErikF says:

      I also find adding bitflags to be dangerous; C# supports bitwise operators just fine (and they don’t do weird stuff if you duplicate flags!)

      1. Koro says:

        This code is obviously old VB WinForms code hastily ported to C#.

  3. Kevin says:

    > flags +=

    Ugh. Bit flags are not numbers. You don’t add them. You bitwise-OR them.

    Sure, it gives you the same result either way, and the performance difference is negligible to nonexistent. But that’s not the point. The point is that you’re doing something that doesn’t make logical sense. If you accidentally add a flag twice, you get random garbage. If you accidentally bitwise-OR it twice, nothing happens.

  4. Alex Guteniev says:

    How does SHGetFileInfo know that the path is a folder then, assuming FILE_ATTRIBUTE_DIRECTORY is not passed?

    1. David Trapp says:

      By accessing the path you passed and checking what type of file it is.

    2. skSdnW says:

      It doesn’t and the source article also knows this: “The code for GetFolderIcon is very similar to GetFileIcon, except that the dwFileAttributes parameter is passed Shell32.FILE_ATTRIBUTE_DIRECTORY as opposed to Shell32.FILE_ATTRIBUTE_NORMAL for files.”

  5. Alex Cohn says:

    Why doesn’t Explorer use this flag when it come across FILE_ATTRIBUTE_OFFLINE?

Comments are closed.

Skip to main content