.NET 8中如何获取Microsoft Scripting Runtime实现文件夹大小计算?
Windows平台下.NET快速统计文件夹大小的优化方案
关于"Microsoft Scripting Runtime"的说明
你看到的旧方案依赖的是COM组件Microsoft Scripting Runtime,在现代.NET(.NET Core/.NET 5+)环境中确实不再作为默认引用存在,而且COM互操作的性能远不如原生.NET方法,完全没必要继续纠结这个组件。
更快的底层实现方案
1. 直接调用Windows原生文件系统API(FindFirstFileEx/FindNextFile)
这是最贴近底层的实现方式,绕开.NET托管封装的额外开销,直接与Windows文件系统交互,性能最优。通过P/Invoke调用这些API,能避免托管代码的冗余逻辑,适合对性能要求极高的场景。
示例代码:
using System; using System.Runtime.InteropServices; public static class FolderSizeCalculator { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct WIN32_FIND_DATA { public uint dwFileAttributes; public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr FindFirstFileEx(string lpFileName, int fInfoLevelId, out WIN32_FIND_DATA lpFindFileData, int fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FindClose(IntPtr hFindFile); private const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; private const int FINDEX_INFO_LEVELS = 0; // FindExInfoStandard private const int FINDEX_SEARCH_OPS = 0; // FindExSearchNameMatch public static long CalculateFolderSize(string folderPath) { long totalSize = 0; string searchPattern = Path.Combine(folderPath, "*"); IntPtr findHandle = FindFirstFileEx(searchPattern, FINDEX_INFO_LEVELS, out WIN32_FIND_DATA findData, FINDEX_SEARCH_OPS, IntPtr.Zero, 0); if (findHandle == IntPtr.Zero) return totalSize; try { do { if (findData.cFileName == "." || findData.cFileName == "..") continue; if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { string subFolderPath = Path.Combine(folderPath, findData.cFileName); totalSize += CalculateFolderSize(subFolderPath); } else { long fileSize = (long)findData.nFileSizeHigh << 32 | findData.nFileSizeLow; totalSize += fileSize; } } while (FindNextFile(findHandle, out findData)); } finally { FindClose(findHandle); } return totalSize; } }
2. 优化.NET原生枚举的并行遍历
如果不想编写P/Invoke代码,可以对现有.NET方法做进一步优化。比如用Parallel.ForEach并行遍历文件,但要注意磁盘类型:SSD适合并行处理,机械硬盘可能因磁头频繁切换导致性能下降,需根据实际环境选择。
示例代码:
using System; using System.IO; using System.Threading; public static long FastCalculateFolderSize(string folderPath) { long totalSize = 0; var files = Directory.EnumerateFiles(folderPath, "*", SearchOption.AllDirectories); Parallel.ForEach(files, file => { try { var fileInfo = new FileInfo(file); Interlocked.Add(ref totalSize, fileInfo.Length); } catch (UnauthorizedAccessException) { // 处理无权限访问的文件,直接跳过 } catch (FileNotFoundException) { // 处理遍历中被删除的文件,直接跳过 } }); return totalSize; }
3. 利用Windows USN日志(进阶方案)
如果需要频繁统计文件夹大小,可以利用Windows的更新序列号(USN)日志——它会记录文件系统的变更信息,能快速获取文件大小的变化,避免每次全量遍历。不过这个方案实现复杂,仅适合高频统计的特定场景。
注意事项
- 异常处理:遍历文件夹时必须处理
UnauthorizedAccessException、FileNotFoundException等异常,避免程序崩溃。 - 磁盘缓存:第一次统计后系统会缓存文件信息,后续统计速度会明显提升,测试时要区分冷启动和热启动的差异。
- 符号链接:如果需要处理符号链接,要在代码中判断并决定是否跟随链接,避免无限递归或重复统计内容。
内容的提问来源于stack exchange,提问作者jc508
相关产品推荐
相关产品推荐

