C#实现快速查询文件的方法
更新时间:2022年6月20日 13:53 点击:543 作者:天方
相信使用过Everything的人都对其超快的搜索速度印象非常深刻,它的主要原理是通过扫描NTFS磁盘的USN Journal读取的文件列表,而不是磁盘目录,由于USN Journal非常小,因此能实现快速搜索。
由于.Net程序的Dll基本上是通用的,在C#中也可以直接使用它。
public class MFTScanner { private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); private const uint GENERIC_READ = 0x80000000; private const int FILE_SHARE_READ = 0x1; private const int FILE_SHARE_WRITE = 0x2; private const int OPEN_EXISTING = 3; private const int FILE_READ_ATTRIBUTES = 0x80; private const int FILE_NAME_IINFORMATION = 9; private const int FILE_FLAG_BACKUP_SEMANTICS = 0x2000000; private const int FILE_OPEN_FOR_BACKUP_INTENT = 0x4000; private const int FILE_OPEN_BY_FILE_ID = 0x2000; private const int FILE_OPEN = 0x1; private const int OBJ_CASE_INSENSITIVE = 0x40; private const int FSCTL_ENUM_USN_DATA = 0x900b3; [StructLayout(LayoutKind.Sequential)] private struct MFT_ENUM_DATA { public long StartFileReferenceNumber; public long LowUsn; public long HighUsn; } [StructLayout(LayoutKind.Sequential)] private struct USN_RECORD { public int RecordLength; public short MajorVersion; public short MinorVersion; public long FileReferenceNumber; public long ParentFileReferenceNumber; public long Usn; public long TimeStamp; public int Reason; public int SourceInfo; public int SecurityId; public FileAttributes FileAttributes; public short FileNameLength; public short FileNameOffset; } [StructLayout(LayoutKind.Sequential)] private struct IO_STATUS_BLOCK { public int Status; public int Information; } [StructLayout(LayoutKind.Sequential)] private struct UNICODE_STRING { public short Length; public short MaximumLength; public IntPtr Buffer; } [StructLayout(LayoutKind.Sequential)] private struct OBJECT_ATTRIBUTES { public int Length; public IntPtr RootDirectory; public IntPtr ObjectName; public int Attributes; public int SecurityDescriptor; public int SecurityQualityOfService; } //// MFT_ENUM_DATA [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, ref MFT_ENUM_DATA lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern Int32 CloseHandle(IntPtr lpObject); [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern int NtCreateFile(ref IntPtr FileHandle, int DesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes, ref IO_STATUS_BLOCK IoStatusBlock, int AllocationSize, int FileAttribs, int SharedAccess, int CreationDisposition, int CreateOptions, int EaBuffer, int EaLength); [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] private static extern int NtQueryInformationFile(IntPtr FileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, int Length, int FileInformationClass); private IntPtr m_hCJ; private IntPtr m_Buffer; private int m_BufferSize; private string m_DriveLetter; private class FSNode { public long FRN; public long ParentFRN; public string FileName; public bool IsFile; public FSNode(long lFRN, long lParentFSN, string sFileName, bool bIsFile) { FRN = lFRN; ParentFRN = lParentFSN; FileName = sFileName; IsFile = bIsFile; } } private IntPtr OpenVolume(string szDriveLetter) { IntPtr hCJ = default(IntPtr); //// volume handle m_DriveLetter = szDriveLetter; hCJ = CreateFile(@"\\.\" + szDriveLetter, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); return hCJ; } private void Cleanup() { if (m_hCJ != IntPtr.Zero) { // Close the volume handle. CloseHandle(m_hCJ); m_hCJ = INVALID_HANDLE_VALUE; } if (m_Buffer != IntPtr.Zero) { // Free the allocated memory Marshal.FreeHGlobal(m_Buffer); m_Buffer = IntPtr.Zero; } } public IEnumerable<String> EnumerateFiles(string szDriveLetter) { try { var usnRecord = default(USN_RECORD); var mft = default(MFT_ENUM_DATA); var dwRetBytes = 0; var cb = 0; var dicFRNLookup = new Dictionary<long, FSNode>(); var bIsFile = false; // This shouldn't be called more than once. if (m_Buffer.ToInt32() != 0) { throw new Exception("invalid buffer"); } // Assign buffer size m_BufferSize = 65536; //64KB // Allocate a buffer to use for reading records. m_Buffer = Marshal.AllocHGlobal(m_BufferSize); // correct path szDriveLetter = szDriveLetter.TrimEnd('\\'); // Open the volume handle m_hCJ = OpenVolume(szDriveLetter); // Check if the volume handle is valid. if (m_hCJ == INVALID_HANDLE_VALUE) { string errorMsg = "Couldn't open handle to the volume."; if (!IsAdministrator()) errorMsg += "Current user is not administrator"; throw new Exception(errorMsg); } mft.StartFileReferenceNumber = 0; mft.LowUsn = 0; mft.HighUsn = long.MaxValue; do { if (DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, ref mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, ref dwRetBytes, IntPtr.Zero)) { cb = dwRetBytes; // Pointer to the first record IntPtr pUsnRecord = new IntPtr(m_Buffer.ToInt32() + 8); while ((dwRetBytes > 8)) { // Copy pointer to USN_RECORD structure. usnRecord = (USN_RECORD)Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType()); // The filename within the USN_RECORD. string FileName = Marshal.PtrToStringUni(new IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2); bIsFile = !usnRecord.FileAttributes.HasFlag(FileAttributes.Directory); dicFRNLookup.Add(usnRecord.FileReferenceNumber, new FSNode(usnRecord.FileReferenceNumber, usnRecord.ParentFileReferenceNumber, FileName, bIsFile)); // Pointer to the next record in the buffer. pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength); dwRetBytes -= usnRecord.RecordLength; } // The first 8 bytes is always the start of the next USN. mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0); } else { break; // TODO: might not be correct. Was : Exit Do } } while (!(cb <= 8)); // Resolve all paths for Files foreach (FSNode oFSNode in dicFRNLookup.Values.Where(o => o.IsFile)) { string sFullPath = oFSNode.FileName; FSNode oParentFSNode = oFSNode; while (dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode)) { sFullPath = string.Concat(oParentFSNode.FileName, @"\", sFullPath); } sFullPath = string.Concat(szDriveLetter, @"\", sFullPath); yield return sFullPath; } } finally { //// cleanup Cleanup(); } } public static bool IsAdministrator() { WindowsIdentity identity = WindowsIdentity.GetCurrent(); WindowsPrincipal principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } }
本文还提供了一个扩展方法,方便我们获取某个磁盘下的所有的文件名。
public static class DriveInfoExtension { public static IEnumerable<String> EnumerateFiles(this DriveInfo drive) { return (new MFTScanner()).EnumerateFiles(drive.Name); } }
需要注意的是,读取USN Journal是需要管理员权限的,因此使用这个类需要管理员权限才能正常运行。
另外,这个类封装的也略为简单,只读取了文件名,实际上还可以读取文件大小,属性等常用信息,修改一下代码非常容易获取这些属性。通过它们可以非常方便写出一些分析磁盘空间占用的程序,这里就不举例了。
到此这篇关于C#实现快速查询文件的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持猪先飞。
原文出处:https://www.cnblogs.com/TianFang/p/3427776.html
相关文章
- 下面小编来给大家演示几个php操作zip文件的实例,我们可以读取zip包中指定文件与删除zip包中指定文件,下面来给大这介绍一下。 从zip压缩文件中提取文件 代...2016-11-25
- 我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
- 这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
Jupyter Notebook读取csv文件出现的问题及解决
这篇文章主要介绍了JupyterNotebook读取csv文件出现的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2023-01-06- 这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
- 本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要介绍了Mybatis Plus select 实现只查询部分字段的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-01
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
- 这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25