2

如何以编程方式从 C# 更改 Fat32 分区的卷序列号。我找到了这个例子,但它是用 C++ 编写的,我读得不好。有人可以回答 C# 代码片段吗?

更新:我可以从上面的示例中看到 C++ 函数,我认为可以将端口定向到 C#

void CVolumeSerialDlg::ChangeSerialNumber(DWORD Drive, const DWORD newSerial)
{
  const max_pbsi = 3;

  struct partial_boot_sector_info
  {
    LPSTR Fs; // file system name

    DWORD FsOffs; // offset of file system name in the boot sector

    DWORD SerialOffs; // offset of the serialnumber in the boot sector

  };

  partial_boot_sector_info pbsi[max_pbsi] =
  {
   {"FAT32", 0x52, 0x43},
   {"FAT",   0x36, 0x27},
   {"NTFS",  0x03, 0x48}
  };

  TCHAR szDrive[12];

  char Sector[512];

  DWORD i;

  sprintf(szDrive, "%c:\\", Drive & 0xFF);

  if (!disk.Open(szDrive))
  {
    ShowErrorString("Could not open disk!");
    return;
  }

  // read sector
  if (!disk.ReadSector(0, Sector))
  {
    ShowErrorString("Could not read sector!");
    return;
  }

  // try to search for a valid boot sector
  for (i=0;i<max_pbsi;i++)
  {
    if (strncmp(pbsi[i].Fs, Sector+pbsi[i].FsOffs, strlen(pbsi[i].Fs)) == 0)
    {
      // we found a valid signature
      break;
    }
  }

  if (i >= max_pbsi)
  {
    MessageBox(_T("Cannot change serial number of this file system!"),
       _T("Error"), MB_ICONERROR);
    return;
  }

  // patch serial number
  *(PDWORD)(Sector+pbsi[i].SerialOffs) = newSerial;

  // write boot sector
  if (!disk.WriteSector(0, Sector))
  {
    ShowErrorString("Could not write sector!");
    return;
  }

  ShowErrorString("Volume serial number changed successfully!\r"
        "You might want to restart your system for changes to take effect!");
}
4

2 回答 2

5

没有保证,小心。

void ChangeSerialNumber(char volume, uint newSerial)
{
    var fsInfo = new[]
    {
        new { Name = "FAT32", NameOffs = 0x52, SerialOffs = 0x43 },
        new { Name = "FAT", NameOffs = 0x36, SerialOffs = 0x27 },
        new { Name = "NTFS", NameOffs = 0x03, SerialOffs = 0x48 }
    };

    using (var disk = new Disk(volume))
    {
        var sector = new byte[512];
        disk.ReadSector(0, sector);

        var fs = fsInfo.FirstOrDefault(
                f => Strncmp(f.Name, sector, f.NameOffs)
            );
        if (fs == null) throw new NotSupportedException("This file system is not supported");

        var s = newSerial;
        for (int i = 0; i < 4; ++i, s >>= 8) sector[fs.SerialOffs + i] = (byte)(s & 0xFF);

        disk.WriteSector(0, sector);
    }
}

bool Strncmp(string str, byte[] data, int offset)
{
    for(int i = 0; i < str.Length; ++i)
    {
        if (data[i + offset] != (byte)str[i]) return false;
    }
    return true;
}

class Disk : IDisposable
{
    private SafeFileHandle handle;

    public Disk(char volume)
    {
        var ptr = CreateFile(
            String.Format("\\\\.\\{0}:", volume),
            FileAccess.ReadWrite,
            FileShare.ReadWrite,
            IntPtr.Zero,
            FileMode.Open,
            0,
            IntPtr.Zero
            );

        handle = new SafeFileHandle(ptr, true);

        if (handle.IsInvalid) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
    }

    public void ReadSector(uint sector, byte[] buffer)
    {
        if (buffer == null) throw new ArgumentNullException("buffer");
        if (SetFilePointer(handle, sector, IntPtr.Zero, EMoveMethod.Begin) == INVALID_SET_FILE_POINTER) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

        uint read;
        if (!ReadFile(handle, buffer, buffer.Length, out read, IntPtr.Zero)) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        if (read != buffer.Length) throw new IOException();
    }

    public void WriteSector(uint sector, byte[] buffer)
    {
        if (buffer == null) throw new ArgumentNullException("buffer");
        if (SetFilePointer(handle, sector, IntPtr.Zero, EMoveMethod.Begin) == INVALID_SET_FILE_POINTER) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

        uint written;
        if (!WriteFile(handle, buffer, buffer.Length, out written, IntPtr.Zero)) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        if (written != buffer.Length) throw new IOException();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (handle != null) handle.Dispose();
        }
    }

    enum EMoveMethod : uint
    {
        Begin = 0,
        Current = 1,
        End = 2
    }

    const uint INVALID_SET_FILE_POINTER = 0xFFFFFFFF;

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr CreateFile(
        string fileName,
        [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
        [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
        IntPtr securityAttributes,
        [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
        int flags,
        IntPtr template);

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint SetFilePointer(
         [In] SafeFileHandle hFile, 
         [In] uint lDistanceToMove,
         [In] IntPtr lpDistanceToMoveHigh,
         [In] EMoveMethod dwMoveMethod);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool ReadFile(SafeFileHandle hFile, [Out] byte[] lpBuffer,
        int nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

    [DllImport("kernel32.dll")]
    static extern bool WriteFile(SafeFileHandle hFile, [In] byte[] lpBuffer,
        int nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
        [In] IntPtr lpOverlapped);
}

使用例如ChangeSerialNumber('D', 0x12345678);

于 2011-08-27T20:32:54.530 回答
4

这是一个读取和写入 FAT32 卷的序列号的小示例。为了保持样本小,所有错误处理都被省略了。

请注意,直接访问卷的扇区可能会导致数据丢失或损坏。所以在使用下面的例子时要小心(它不是一个适合生产使用的例子)。没有保证!

在下面的示例中,我使用 Win32 API GetDiskFreeSpace(使用 .Net 互操作)来获取 FAT32 卷的每个扇区的字节数。为了打开胖卷,我使用了 Win32 API CreateFile,因为 FileStream 类不支持直接打开磁盘分区。

static uint GenericRead = 0x80000000;
static uint GenericWrite = 0x40000000;                       
static uint OpenExisting = 3;

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters);

static void ReadAndSetSerialNumber()
{
  const string driveLetter = "e:"; // Drive with FAT32 file system.

  uint sectorsPerCluster;
  uint bytesPerSector;
  uint numberOfFreeClusters;
  uint totalNumberOfClusters;

  GetDiskFreeSpace(String.Format(@"{0}\", driveLetter), out sectorsPerCluster, out bytesPerSector,
    out numberOfFreeClusters, out totalNumberOfClusters);

  Console.Out.WriteLine("Info for drive {0}", driveLetter);
  Console.Out.WriteLine("Bytes per sector: {0}", bytesPerSector);

  const int fatSerialOffset = 0x43;
  const int fatIdOffset = 0x52;
  const string fatFileSystemId = "FAT32";

  using (SafeFileHandle sfh = CreateFile(String.Format("\\\\.\\{0}", driveLetter), GenericRead | GenericWrite,
    (uint)FileShare.ReadWrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero))
  {
    using (FileStream fs = new FileStream(sfh, FileAccess.ReadWrite))
    {
      byte[] firstSector = new byte[bytesPerSector];

      fs.Read(firstSector, 0, (int)bytesPerSector);

      if (Encoding.ASCII.GetString(firstSector, fatIdOffset, fatFileSystemId.Length) == fatFileSystemId)
      {
        Console.Out.WriteLine("FAT32 file system found...");

        uint serial = BitConverter.ToUInt32(firstSector, fatSerialOffset);

        Console.Out.WriteLine("Read serial number: {0:X4}-{1:X4}", serial >> 16, serial & 0xFFFF);

        // Write new serial number.
        byte[] newserial = BitConverter.GetBytes((uint)10000123);

        Array.Copy(newserial, 0, firstSector, fatSerialOffset, newserial.Length);
        fs.Seek(0, SeekOrigin.Begin);
        fs.Write(firstSector, 0, (int)bytesPerSector); 
      }                   
    }
  }
}

此外,您可以使用 .Net Framework 的 DriveInfo 类来枚举计算机上的可用驱动器。

希望这可以帮助。

于 2011-08-27T20:54:19.560 回答