C# 如何获取磁盘上文件的第一个字节的位置?

C# 如何获取磁盘上文件的第一个字节的位置?,c#,windows,file,filesystems,ntfs,C#,Windows,File,Filesystems,Ntfs,我需要对C#应用程序中给定U盘上的每个文件进行校验和。我怀疑这里的瓶颈是磁盘的实际读取,所以我希望尽快实现这一点 我想如果我能按照文件在磁盘上的实际显示顺序(假设驱动器没有碎片化),按顺序读取磁盘上的文件,速度会快得多 如何从每个文件的标准路径中找到这些信息?i、 e.给定一个位于“F:\MyFile.txt”的文件,如何在磁盘上找到该文件的起始位置 我正在Windows中运行一个C#应用程序。如果您的驱动器是SSD或基于记忆棒技术,请不要管它 记忆棒和其他类似设备通常基于SSD(或类似)技术,

我需要对C#应用程序中给定U盘上的每个文件进行校验和。我怀疑这里的瓶颈是磁盘的实际读取,所以我希望尽快实现这一点

我想如果我能按照文件在磁盘上的实际显示顺序(假设驱动器没有碎片化),按顺序读取磁盘上的文件,速度会快得多

如何从每个文件的标准路径中找到这些信息?i、 e.给定一个位于“F:\MyFile.txt”的文件,如何在磁盘上找到该文件的起始位置


我正在Windows中运行一个C#应用程序。

如果您的驱动器是SSD或基于记忆棒技术,请不要管它

记忆棒和其他类似设备通常基于SSD(或类似)技术,其中随机读/写访问的问题实际上不是问题。因此,您可以只枚举文件并运行校验和

您可以尝试在多个线程中运行它,但我不确定这是否可以加快进程,这是您可能需要测试的东西。它也可能因设备而异

奖金
@xanatos提到了一个有趣的观点:“我总是注意到在记忆棒上复制上千个文件要比复制一个大文件慢得多。”

复制一个大文件比复制一堆小文件要快得多。原因(通常)不是因为文件彼此靠近,所以硬件更容易按顺序读取它们。问题出在需要跟踪每个文件的操作系统上


如果你曾经在Windows上运行过,你会看到大量的文件创建、文件读取和文件写入。为了复制100个文件,操作系统将打开每个文件,读取其内容,写入另一个文件,关闭两个文件+发送到文件系统的许多更新操作,例如更新两个文件的属性,更新两个文件的安全描述符,更新目录信息等。因此,一个复制操作有许多附属操作

现在。。。我真的不知道它是否对你有用:

[StructLayout(LayoutKind.Sequential)]
public struct StartingVcnInputBuffer
{
    public long StartingVcn;
}

public static readonly int StartingVcnInputBufferSizeOf = Marshal.SizeOf(typeof(StartingVcnInputBuffer));

[StructLayout(LayoutKind.Sequential)]
public struct RetrievalPointersBuffer
{
    public uint ExtentCount;
    public long StartingVcn;
    public long NextVcn;
    public long Lcn;
}

public static readonly int RetrievalPointersBufferSizeOf = Marshal.SizeOf(typeof(RetrievalPointersBuffer));

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFileW(
        [MarshalAs(UnmanagedType.LPWStr)] string filename,
        [MarshalAs(UnmanagedType.U4)] FileAccess access,
        [MarshalAs(UnmanagedType.U4)] FileShare share,
        IntPtr securityAttributes,
        [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
        [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
        IntPtr templateFile);

[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
    ref StartingVcnInputBuffer lpInBuffer, int nInBufferSize,
    out RetrievalPointersBuffer lpOutBuffer, int nOutBufferSize,
    out int lpBytesReturned, IntPtr lpOverlapped);

// Returns a FileStream that can only Read
public static void GetStartLogicalClusterNumber(string fileName, out FileStream file, out long startLogicalClusterNumber)
{
    SafeFileHandle handle = CreateFileW(fileName, FileAccess.Read | (FileAccess)0x80 /* FILE_READ_ATTRIBUTES */, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

    if (handle.IsInvalid)
    {
        throw new Win32Exception();
    }

    file = new FileStream(handle, FileAccess.Read);

    var svib = new StartingVcnInputBuffer();

    int error;

    RetrievalPointersBuffer rpb;

    int bytesReturned;
    DeviceIoControl(handle.DangerousGetHandle(), (uint)589939 /* FSCTL_GET_RETRIEVAL_POINTERS */, ref svib, StartingVcnInputBufferSizeOf, out rpb, RetrievalPointersBufferSizeOf, out bytesReturned, IntPtr.Zero);

    error = Marshal.GetLastWin32Error();

    switch (error)
    {
        case 38: /* ERROR_HANDLE_EOF */
            startLogicalClusterNumber = -1; // empty file. Choose how to handle
            break;

        case 0: /* NO:ERROR */
        case 234: /* ERROR_MORE_DATA */
            startLogicalClusterNumber = rpb.Lcn;
            break;

        default:
            throw new Win32Exception();
    }
}
请注意,该方法将返回一个
FileStream
,您可以保持打开状态并使用它来读取文件,或者您可以轻松地修改它以不返回(也不创建)文件,然后在需要对其进行哈希运算时重新打开该文件

使用:

string[] fileNames = Directory.GetFiles(@"D:\");

foreach (string fileName in fileNames)
{
    try
    {
        long startLogicalClusterNumber;
        FileStream file;
        GetStartLogicalClusterNumber(fileName, out file, out startLogicalClusterNumber);
    }
    catch (Exception e)
    {
        Console.WriteLine("Skipping: {0} for {1}", fileName, e.Message);
    }
}

我正在使用此处描述的API:。该程序简单得多,因为您只需要初始逻辑集群号(代码的第一个版本可以提取所有LCN扩展数据块,但它没有用,因为您必须将文件从第一个字节散列到最后一个字节)。请注意,空文件(长度为0的文件)没有分配任何群集。该函数为集群返回
-1
错误\u句柄\u EOF
)。您可以选择如何处理它。

您可以。。。请注意,我认为这不是一个好主意。。。更正。。。我确实认为这是一个坏消息idea@xanatos为什么这是个坏主意?@oleksii我甚至不知道你需要什么权限。。。而仅仅因为高级方法速度慢而对磁盘执行低级操作,这是我永远不会做的事情。有高级方法和低级方法是有原因的。@xanatos wd-3.com是一个“坏”网站,我建议删除该链接。@andreaplanet删除了该链接。。。它过期了:-(用你的回答替换了链接可能是对的,但从实证测试中,我总是注意到在记忆棒上复制数千个文件比复制一个大文件要慢得多……用于复制的驱动器都是1TB 2.5英寸标准非SSD USB3硬盘,如果这有区别的话…@GoldieLocks Yes it mat我会更新我的答案-它只适用于ssd和mem棒。我不知道如何按顺序读取文件(在低级别)关于旋转磁盘。非常非常有帮助,谢谢!我刚刚开始阅读那篇文章,更不用说理解所有内容了…我唯一的问题是,它似乎不仅仅是返回-1的空文件,而是非常小的文件…我理解空文件没有集群的逻辑,这会产生什么影响小文件?我说的是一行文本,96字节,txt文件。@NTFS上的GoldieLocks如果文件足够小,它可以存储在MFT记录本身中,而不需要使用其他群集。