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

标签 c# windows file filesystems ntfs

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

我怀疑如果我可以按顺序读取磁盘上的文件,按照它们在磁盘上出现的实际顺序(假设驱动器没有碎片),这会快得多。

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

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

最佳答案

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

[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:https://web.archive.org/web/20160130161216/http://www.wd-3.com/archive/luserland.htm .该程序要简单得多,因为您只需要初始逻辑簇号(代码的第一个版本可以提取所有 LCN 范围,但它没有用,因为您必须从文件的第一个字节到最后一个字节进行哈希处理)。请注意,空文件(长度为 0 的文件)没有分配任何簇。该函数返回集群的 -1 (ERROR_HANDLE_EOF)。您可以选择如何处理它。

关于c# - 如何获取文件第一个字节在磁盘上的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29605019/

相关文章:

Windows 蓝牙自动配对或禁用身份验证

c# - 如何通过 C# 代码编辑 Windows 中的注册表值(regedit)?

python - [Python]比较两个zip文件的功能,一个位于FTP目录,另一个在我的本地机器上

c# - 有没有更好的方法来格式化这个字符串?

c++ - 乘以定义的符号: mysqlclient, pthread-win32

python - 这是我当前写入文件的方式。但是,我不能做UTF-8?

c - 学习C,[1]为什么要在main中获得无限循环? [2]为什么在不触摸文件检查代码的情况下出现段错误

c# - Action 属性

c# - 如何验证该列是否存在于 DataRow 对象中?

c# - 从 C# 创建 Angular build(ng build)