27

I am using Icon.ExtractAssociatedIcon to get the icon of a file , that a user selects, in an openfiledialog.

THe problem is if the user selects an icon from a network share then the filename property of the openfiledialog is in UNC format and this causes an ArgumentException in ExtractAssocaitedIcon:

Value of '\\server\share\filename' is not valid for 'filePath'.

Stack Trace:
   at System.Drawing.Icon.ExtractAssociatedIcon(String filePath, Int32 index)

So my question is given a file specified as \\server\share\filename, how do I get the icon?

Note: .NET 2.0

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Rahul
  • 2,194
  • 4
  • 31
  • 46

5 Answers5

37

Looking at this with Reflector, it is ultimately calling ExtractAssociatedIcon in shell32.dll.

Have you tried the going around the BCL accessing it via PInvoke?

Sample code (via PInvoke.Net):

[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr ExtractAssociatedIcon(IntPtr hInst, StringBuilder lpIconPath,
   out ushort lpiIcon);

 // ... snip
    ushort uicon;
    StringBuilder strB = new StringBuilder(260); // Allocate MAX_PATH chars
    strB.Append(openFileDialog1.FileName);
    IntPtr handle = ExtractAssociatedIcon(IntPtr.Zero, strB, out uicon);
    Icon ico = Icon.FromHandle(handle);

    pictureBox1.Image = ico.ToBitmap();
 // ... snip
poizan42
  • 1,461
  • 17
  • 22
Brett Veenstra
  • 47,674
  • 18
  • 70
  • 86
  • 16
    This actually works which makes me wonder, why does the limitation exist in .NET in the fist place – Rahul Dec 21 '09 at 09:16
  • Perfect, this helped me a lot. Agree with @Rahul here. Very strange limitation indeed. – Øyvind Bråthen Sep 27 '12 at 15:14
  • 2
    Works for me too. Looks like first parameter could be set at "IntPtr.Zero" if no handle available at call time. – AFract Oct 28 '14 at 16:28
  • 1
    @AFract hInst is marked as \_Reserved\_, so it is never used. The documentation is just horrible. – poizan42 Feb 15 '18 at 12:17
  • For anyone coming by here frustrated that it doesn't just work out of the box, vote for [this uservoice](https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/33334981-please-fix-system-drawing-icon-extractassociatedic) – poizan42 Feb 15 '18 at 14:09
  • Can you please explain where that magic number `260` is coming from? Thanks. – AntikM Aug 18 '20 at 07:35
  • Don't forget to dispose of ico – apc Nov 26 '20 at 12:24
  • 1
    @antikbd 260 is coming from the Windows API limitation of the maximum length that a path is allowed (reference https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation). You'll find very quickly in coding Windows that if any of your paths ever get any longer than that, you'll run into ALL kinds of problems! I concur with the consensus. I came here looking for a solution because I came across the same UNC problem. Thanks for the solution Brett Veenstra, it's excellent and worked perfectly for me! – Westley Bennett Jan 05 '21 at 14:36
25

For completeness' sake, here's an ExtractAssociatedIcon routine that works:

/// <summary>
/// Returns an icon representation of an image contained in the specified file.
/// This function is identical to System.Drawing.Icon.ExtractAssociatedIcon, xcept this version works.
/// </summary>
/// <param name="filePath">The path to the file that contains an image.</param>
/// <returns>The System.Drawing.Icon representation of the image contained in the specified file.</returns>
/// <exception cref="System.ArgumentException">filePath does not indicate a valid file.</exception>
public static Icon  ExtractAssociatedIcon(String filePath)
{
    int index = 0;

    Uri uri;
    if (filePath == null)
    {
        throw new ArgumentException(String.Format("'{0}' is not valid for '{1}'", "null", "filePath"), "filePath");
    }
    try
    {
        uri = new Uri(filePath);
    }
    catch (UriFormatException)
    {
        filePath = Path.GetFullPath(filePath);
        uri = new Uri(filePath);
    }
    //if (uri.IsUnc)
    //{
    //  throw new ArgumentException(String.Format("'{0}' is not valid for '{1}'", filePath, "filePath"), "filePath");
    //}
    if (uri.IsFile)
    {
        if (!File.Exists(filePath))
        {
            //IntSecurity.DemandReadFileIO(filePath);
            throw new FileNotFoundException(filePath);
        }

        StringBuilder iconPath = new StringBuilder(260);
        iconPath.Append(filePath);

        IntPtr handle = SafeNativeMethods.ExtractAssociatedIcon(new HandleRef(null, IntPtr.Zero), iconPath, ref index);
        if (handle != IntPtr.Zero)
        {
            //IntSecurity.ObjectFromWin32Handle.Demand();
            return Icon.FromHandle(handle);
        }
    }
    return null;
}


/// <summary>
/// This class suppresses stack walks for unmanaged code permission. 
/// (System.Security.SuppressUnmanagedCodeSecurityAttribute is applied to this class.) 
/// This class is for methods that are safe for anyone to call. 
/// Callers of these methods are not required to perform a full security review to make sure that the 
/// usage is secure because the methods are harmless for any caller.
/// </summary>
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
    [DllImport("shell32.dll", EntryPoint = "ExtractAssociatedIcon", CharSet = CharSet.Auto)]
    internal static extern IntPtr ExtractAssociatedIcon(HandleRef hInst, StringBuilder iconPath, ref int index);
}

Note: Any code is released into the public domain. No attribution required.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
2

One method to accomplish this is to retrieve your UNC path and temporarily map it to a drive letter, then use that drive in your .ExtractAssociatedIcon method. When you have retrieved the icon, you can unmap the drive. It's not elegant, but it should work just fine.

Stewbob
  • 16,759
  • 9
  • 63
  • 107
  • yeah I thought of that , wont there be likje potentially security issues, if the user is non admin or UAC will the under the covers mapping work, i think there might be an elevation issue , not sure will look into it thanks – Rahul Dec 04 '09 at 01:34
1

Another option would be to copy the file the user selects to their %TEMP% and use Icon.ExtractAssociatedIcon there. Just remember to cleanup after yourself.

Obviously not a great solution if you're supporting LARGE files!

Brett Veenstra
  • 47,674
  • 18
  • 70
  • 86
0

Just to complement Ian Boyd answer , if the file is an image, instead of the file icon, you can use FileStream to preview it:

foreach (string item in Directory.GetFiles(actualPath))
{

    fi = new FileInfo(item);

    using (FileStream stream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read))
     {
          myImageList.Images.Add(Image.FromStream(stream));

     }
}