Question

How do I programatically change Volume Serial of a Fat32 partition from C#. I found this example, but it is written with C++ which I don't read well. Could someone please answer a C# code snippet?

Update: I can see the C++ function from above example which I think it's possible to direct port to 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!");
}
Was it helpful?

Solution

No guarantees, be careful.

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);
}

Use e.g. ChangeSerialNumber('D', 0x12345678);

OTHER TIPS

Here is a small example for reading and writing the serial number of a FAT32 volume. To keep the sample small all error handling has been omitted.

Please note that direct access to the sectors of a volume may lead to data loss or corruption. So be careful in using the example below (it is not an example suitable for production usage). No guarantee!

In the example below I use the Win32 API GetDiskFreeSpace (using .Net interop) to get the bytes per sector for the FAT32 volume. To open the fat volume, I use the Win32 API CreateFile because the FileStream class does not support opening disk partitions directly.

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); 
      }                   
    }
  }
}

Furthermore you could use the .Net Framework's DriveInfo class to enumerate the available drives on your computer.

Hope, this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top