您的代码中有几件事。其中一些看起来像是之前尝试的残留物。无论如何,这是有效的:
Public Partial 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(pszPath As String,
dwFileAttributes As Integer,
ByRef psfi As SHFILEINFO,
cbFileInfo As Integer,
uFlags As Integer) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(hIcon As IntPtr) As Boolean
End Function
Public Shared Function GetShellIcon(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
将 PInvoke 代码放入其自己的类中有几个好处。首先,它有助于将代码与所有这些幻数、结构和常量隔离开来。 PInvoke 可以是私有的并通过方法公开(GetShellIcon
) 它完成所有 scut 工作并调用 API 方法。此外,当从 VS CodeAnalysis 工具中使用它时,它不会抱怨它。NativeMethods
班级。
您的代码没有做的事情之一是销毁检索到的图标并释放该资源;还有你的SHGetFileInfo
看起来不对,这可能会导致不好的事情。当它无法获取图标时,我不会在 PInvoke 代码中创建空白/空位图,而是返回Nothing
供代码处理。
最后封装了PInvoke代码,使用起来更方便,也更短:
Dim fPath As String = "C:\Temp"
Dim di = New DirectoryInfo(fPath)
' store imagelist index for known/found file types
Dim exts As New Dictionary(Of String, Int32)
Dim img As Image
Dim lvi As ListViewItem
For Each d In di.EnumerateDirectories("*.*", SearchOption.TopDirectoryOnly)
lvi = New ListViewItem(d.Name)
lvi.SubItems.Add("") ' no file name
lvi.SubItems.Add(Directory.GetFiles(d.FullName).Count().ToString)
myLV.Items.Add(lvi)
img = NativeMethods.GetShellIcon(d.FullName)
imgLst.Images.Add(img)
lvi.ImageIndex = imgLst.Images.Count - 1
Next
For Each f In di.EnumerateFiles("*.*")
lvi = New ListViewItem(f.DirectoryName)
lvi.SubItems.Add(f.Name) ' no file name
lvi.SubItems.Add("n/a")
myLV.Items.Add(lvi)
If exts.ContainsKey(f.Extension) = False Then
' try simplest method
img = Drawing.Icon.ExtractAssociatedIcon(f.FullName).ToBitmap
If img Is Nothing Then
img = NativeMethods.GetShellIcon(f.FullName)
End If
If img IsNot Nothing Then
imgLst.Images.Add(img)
exts.Add(f.Extension, imgLst.Images.Count - 1)
Else
' ?? use some default or custom '?' one?
End If
End If
lvi.ImageIndex = exts(f.Extension)
Next
对于文件,它首先尝试使用 NET 获取图标Icon.ExtractAssociatedIcon
方法并求助于因某种原因失败的 PInvoke。
我改变了exts
List(Of String
to a Dictionary(Of String, Int32)
。一旦代码获取扩展程序的图标,它就会将该图像的索引保存在ImageList
这样就不需要再次查找扩展名/图标。这在大型文件夹上加快了相当多的速度。
如果您声明Dictionary
在方法之外,然后不清除ImageList
每次,您都可以让它们在运行时积累图像。这text file
文件夹中的图标Foo
不会与其他地方的文本文件的图像不同。
Result: