how do I show network folder icon in a listview? the one with the green tube under the folder, I have the code that works great with files and folders but when visiting other computer through network, I can't see the network folders that looks like this one.
what should I add?
here's my code:
this is how I display icon in a ListView
Dim fPath As String = Form2.TextBox1.Text
Dim di = New DirectoryInfo(fPath)
' store imagelist index for known/found file types
Dim exts As New Dictionary(Of String, Int32)
If di.Exists = True Then
Dim img As Image
Dim lvi As ListViewItem
For Each d In di.EnumerateDirectories("*.*", SearchOption.TopDirectoryOnly)
lvi = New ListViewItem(d.Name)
lvi.SubItems.Add("")
lvi.SubItems.Add(d.CreationTime.Date)
ListView1.Items.Add(lvi)
img = NativeMethods.GetShellIcon(d.FullName)
ImageList1.Images.Add(img)
lvi.ImageIndex = ImageList1.Images.Count - 1
Next
this is how I get icons from shell32.
Partial Public Class NativeMethods
Private Const MAX_PATH As Integer = 256
Private Const NAMESIZE As Integer = 80
Private Const SHGFI_ICON As Int32 = &H100
<StructLayout(LayoutKind.Sequential)>
Private Structure SHFILEINFO
Public hIcon As IntPtr
Public iIcon As Integer
Public dwAttributes As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=MAX_PATH)>
Public szDisplayName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=NAMESIZE)>
Public szTypeName As String
End Structure
<DllImport("Shell32.dll")>
Private Shared Function SHGetFileInfo(ByVal pszPath As String,
ByVal dwFileAttributes As Integer,
ByRef psfi As SHFILEINFO,
ByVal cbFileInfo As Integer,
ByVal uFlags As Integer) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function
Public Shared Function GetShellIcon(ByVal path As String) As Bitmap
Dim shfi As SHFILEINFO = New SHFILEINFO()
Dim ret As IntPtr = SHGetFileInfo(path, 0, shfi, Marshal.SizeOf(shfi), SHGFI_ICON)
If ret <> IntPtr.Zero Then
Dim bmp As Bitmap = System.Drawing.Icon.FromHandle(shfi.hIcon).ToBitmap
DestroyIcon(shfi.hIcon)
Return bmp
Else
Return Nothing
End If
End Function
End Class
You can get that icon from Shell32
as a small or large image. As Cody Gray points out in a comment, there are more icons (200+) in "imageres.dll". To get these by index, add this method to your NativeMethods
class:
<DllImport("shell32.dll", CharSet:=CharSet.Auto)>
Private Shared Function ExtractIconEx(szFileName As String,
nIconIndex As Integer,
ByRef phiconLarge As IntPtr,
ByRef phiconSmall As IntPtr,
nIcons As UInteger) As UInteger
End Function
Private Shared ImgResFile As String = "imageres.dll"
Private Shared ShellFile As String = "shell32.dll"
Friend Shared Function GetShellIconByIndex(ndx As Int32,
largeIcon As Boolean = False,
Optional FromShell As Boolean = True) As Bitmap
Dim largeIco As IntPtr
Dim smallIco As IntPtr
Dim thisIco As IntPtr
Dim ico As Icon
Dim bmp As Bitmap = Nothing
Dim targtFile = If(FromShell, ShellFile, ImgResFile)
ExtractIconEx(targtFile, ndx, largeIco, smallIco, 1)
Try
If largeIcon Then
ico = Icon.FromHandle(largeIco)
thisIco = largeIco
Else
ico = Icon.FromHandle(smallIco)
thisIco = smallIco
End If
bmp = ico.ToBitmap()
Catch ex As Exception ' swallow exception to return nothing
' really stupid index values can throw ArgumentException
' when the result is IntPtr.Zero
' Rather than test it, catch it an any other(s)
Finally
DestroyIcon(thisIco)
End Try
Return bmp
End Function
The first argument is the index of the icon to get, the second indicates whether you want the large or small version, the last is an optional flag to fetch from imageres.dll
versus shell32.dll
. Note that the method can result Nothing
if something goes wrong.
Then modify your folders loop to get the pipe-folder image (#275) from shell32.dll
when you detect a network drive:
For Each d In di.EnumerateDirectories("*.*", SearchOption.TopDirectoryOnly)
...
If IsNetworkFolder(d) Then
' get #275 as small image from Shell
img = NativeMethods.GetShellIconByIndex(275, False)
If img Is Nothing Then
' ToDo: perhaps load a default image from Resources?
End If
Else
img = NativeMethods.GetShellIcon(d.FullName)
If img Is Nothing Then
img = IconFromFile(d.FullName)
End If
End If
'... add code
Next
Private Function IsNetworkFolder(di As DirectoryInfo) As Boolean
Dim drv As New DriveInfo(di.Root.Name)
Return (drv.DriveType = DriveType.Network)
End Function
This uses a helper function to determine whether the folder is networked or not. If it is, it fetches that specific folder icon, which is #275, from the DLL. Result:
That same folder image is also in imageres.dll
as #137 (and #68 and #69 are similar with world overlays). To get from that instead:
' 137 is the index, false for large icon, false to use imageres instead:
img = NativeMethods.GetShellIconByIndex(137, False, False)
If you want to avoid Magic Numbers in your code, use constants or an enum of the icons used. You could define them all in the NativeMethods
class, but that is 500 items and you may well not recall what they mean 6 months later:
Private Enum ShellIcons
NetworkFolder1 = 275
NetworkFolder2 = 103
SharedFolder = 158
AddNetworkFolder = 278
End Enum
...
img = NativeMethods.GetShellIconByIndex(ShellIcons.NetworkFolder1, False)
This will display the icons stored in shell32.dll
and their index to a Listview
set to LargeIcon
View so you can browse them:
Dim ndx As Int32 = 0
Dim img As Image = Nothing
Dim lvi As ListViewItem
Do
' change second Bool to False to get the ones in imageres.dll
img = NativeMethods.GetShellIconByIndex(ndx, True, True)
If img IsNot Nothing Then
lvi = New ListViewItem(ndx.ToString)
ImageList1.Images.Add(img)
lvi.ImageIndex = ImageList1.Images.Count - 1
myLV.Items.Add(lvi)
ndx += 1
Else
Exit Do
End If
Loop Until img Is Nothing