C#中使用SHGetFileInfo获取文件属性:ListView添加创建时间与大小
解决SHGetFileInfo结合ListView获取图标、文件类型及文件元数据的问题
我来帮你理清这里的关键点——首先得澄清一个常见误解:SHGetFileInfo本身并不直接返回文件的创建时间和大小,SHGFI_ATTR_SPECIFIED标志是用来筛选文件系统属性(比如只读、隐藏、系统文件这类),而非文件的元数据(时间、大小)。不过我们可以在同一流程里,用SHGetFileInfo拿图标和文件类型,同时配合你已经在遍历的FileInfo对象获取时间和大小,这样既满足需求又高效。
第一步:确保结构体和API声明正确
先把C#里需要的SHFILEINFO结构体和Win32 API导入写对:
using System; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct SHFILEINFO { public IntPtr hIcon; public int iIcon; public uint dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] public string szTypeName; } public static class Shell32 { [DllImport("shell32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern bool DestroyIcon(IntPtr hIcon); // 定义需要的标志位 public const uint SHGFI_ICON = 0x100; public const uint SHGFI_DISPLAYNAME = 0x200; public const uint SHGFI_SMALLICON = 0x1; public const uint SHGFI_ATTRIBUTES = 0x800; public const uint SHGFI_ATTR_SPECIFIED = 0x2000; public const uint FILE_ATTRIBUTE_READONLY = 0x1; }
第二步:修正你的循环代码
在遍历文件的循环里,我们先调用SHGetFileInfo获取图标和文件类型,再直接用FileInfo拿创建时间和大小,最后把所有信息添加到ListView:
// 假设你已经初始化了ListView和对应的ImageList // listView1.LargeImageList = imageList1; // 或者用SmallImageList DirectoryInfo dir = new DirectoryInfo(@"C:\Your\Target\Directory"); foreach (FileInfo file in dir.EnumerateFiles()) { SHFILEINFO shfi = new SHFILEINFO(); // 调用SHGetFileInfo获取图标和文件类型 IntPtr result = Shell32.SHGetFileInfo( file.FullName, 0, ref shfi, (uint)Marshal.SizeOf(shfi), Shell32.SHGFI_ICON | Shell32.SHGFI_DISPLAYNAME | Shell32.SHGFI_SMALLICON ); if (result != IntPtr.Zero) { // 将图标添加到ImageList(记得只添加一次,避免重复) if (!imageList1.Images.ContainsKey(file.Extension)) { imageList1.Images.Add(file.Extension, Icon.FromHandle(shfi.hIcon)); // 释放系统分配的图标资源,避免内存泄漏 Shell32.DestroyIcon(shfi.hIcon); } // 准备ListView项 ListViewItem item = new ListViewItem(file.Name); item.SubItems.Add(shfi.szTypeName); // 文件类型 item.SubItems.Add(file.CreationTime.ToString("yyyy-MM-dd HH:mm:ss")); // 创建时间 item.SubItems.Add(FormatFileSize(file.Length)); // 格式化文件大小 item.ImageKey = file.Extension; // 关联ImageList里的图标 listView1.Items.Add(item); } } // 辅助方法:格式化文件大小为易读格式 private string FormatFileSize(long bytes) { string[] sizes = { "B", "KB", "MB", "GB" }; double len = bytes; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len /= 1024; } return $"{len:0.##} {sizes[order]}"; }
关于SHGFI_ATTR_SPECIFIED的正确用法
如果你的需求里确实需要判断文件的系统属性(比如是否只读、隐藏),可以结合SHGFI_ATTRIBUTES和SHGFI_ATTR_SPECIFIED来实现,比如:
// 检查文件是否为只读 uint targetAttr = Shell32.FILE_ATTRIBUTE_READONLY; IntPtr attrResult = Shell32.SHGetFileInfo( file.FullName, targetAttr, ref shfi, (uint)Marshal.SizeOf(shfi), Shell32.SHGFI_ATTRIBUTES | Shell32.SHGFI_ATTR_SPECIFIED ); bool isReadOnly = (shfi.dwAttributes & targetAttr) != 0;
关键总结
SHGetFileInfo的核心能力是获取Shell相关信息(图标、显示名称),文件元数据(时间、大小)用FileInfo获取更直接高效;- 记得手动释放
SHGetFileInfo返回的图标句柄,避免内存泄漏; SHGFI_ATTR_SPECIFIED是用来筛选特定的文件系统属性,而非获取文件元数据。
内容的提问来源于stack exchange,提问作者Lucy82
相关产品推荐
相关产品推荐

