4

Is this possible? It gives me an error, and I had previously thought it could work for folders and drives and stuff like that as well.

Icon.ExtractAssociatedIcon("C:\") did not work when I tried it, and threw an error.

How can I get the associated icon from EVERYTHING? This is vb.net

Cyclone
  • 17,939
  • 45
  • 124
  • 193

2 Answers2

7

The SHGetFileInfo() shell function can provide you with the icon you are looking for. This code worked well, it generated appropriate icons for drives, folders and files:

Imports System.Drawing
Imports System.Reflection
Imports System.Runtime.InteropServices

Public Module NativeMethods
  Public Function GetShellIcon(ByVal path As String) As Icon
    Dim info As SHFILEINFO = New SHFILEINFO()
    Dim retval as IntPtr = SHGetFileInfo(path, 0, info, Marshal.SizeOf(info), &H100)
    If retval = IntPtr.Zero Then Throw New ApplicationException("Could not retrieve icon")
    '' Invoke private Icon constructor so we do not have to copy the icon
    Dim cargt() As Type = { GetType(IntPtr) }
    Dim ci As ConstructorInfo = GetType(Icon).GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, cargt, Nothing)
    Dim cargs() As Object = { info.IconHandle }
    Dim icon As Icon = CType(ci.Invoke(cargs), Icon)
    Return icon
  End Function

  '' P/Invoke declaration
  Private Structure SHFILEINFO
    Public IconHandle As IntPtr
    Public IconIndex As Integer
    Public Attributes As Integer
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
    Public DisplayString As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)> _
    Public TypeName As String
  End Structure

  Private Declare Auto Function SHGetFileInfo lib "Shell32.dll" (path As String, _
    attributes As Integer, byref info As SHFILEINFO, infoSize As Integer, flags As Integer) As IntPtr

End Module
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • According to your answer to me [here](http://stackoverflow.com/a/21613348/939213) `The CLR will never automatically release an IntPtr` - which is in line with [SHGetFileInfo docs](http://msdn.microsoft.com/en-us/library/windows/desktop/bb762179%28v=vs.85%29.aspx) `you are responsible for freeing it with DestroyIcon`. So isn't your code missing a `DestroyIcon` here? (I might be completely off here. I don't really know VB.net.) – ispiro Feb 07 '14 at 13:15
  • 1
    No. That's what the ConstructorInfo trick is all about. It tells the Icon class that it now owns the handle. Which ensures that it automatically calls DestroyIcon when the object is garbage collected. Doing it any other way is very difficult since it is not legal to call DestroyIcon until you have a hard guarantee that the Icon object isn't going to be used anywhere. – Hans Passant Feb 07 '14 at 13:47
  • Thanks. I realized from your comment (in the code) that you were getting around having to do `icon2 = (Icon)icon1.Clone(); etc.`. I didn't realize it would also take care of destroying the icon. Nice. Thanks again. – ispiro Feb 07 '14 at 13:54
  • Hans, I'm a C# programmer and desperately need this hack. I tried to implement an approach that assigns the icon handle to a static public variable and then use DestroyIcon at strategic places, to no avail (I still leak memory). Do you have a C# implementation to share? – Jazimov Nov 20 '18 at 00:15
  • 2
    @Jazimov it is probably little late but here you go: https://gist.github.com/D4koon/2b397eca452b75115f19063560efbbb3 – Sebastian Ax Mar 05 '20 at 05:29
  • I've been waiting each day for this. :) But, in all seriousness, it's never too late to learn something new so I will take a look to see if I still can use the tip, thank you. – Jazimov Apr 24 '20 at 14:02
  • Thanks Sebastian. I needed this today <3 – Waescher Oct 30 '20 at 11:38
1

It is not possible to use Icon.ExtractAssociatedIcon on anything other than files. This API is a thin wrapper on top of the Win32 call ExtractAssociatedIcon. While the documentation for the managed code is a bit ambiguous, the native documentation is much clearer that the target must be a file. It goes further to say that it must be an executable file.

Unfortunately I'm not sure if there is an equivalent function for Directories or not.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454