2011-08-27 8 views
2

C#에서 Fat32 파티션의 Volume Serial을 프로그래밍 방식으로 어떻게 변경합니까? this example을 찾았지 만, 잘 읽지 않는 C++로 작성되었습니다. 누군가 C# 코드 스 니펫에 대답 해 주시겠습니까?프로그래밍 방식으로 FAT32 볼륨 일련 번호 변경

업데이트 : 나는 그것을 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!"); 
} 
+0

C#에서 필요한 스 니펫을 보여주세요. 아무도 전체 소스를 번역하지 않습니다 ... – Yahia

답변

4

보장에 포트를 직접 가능하다고 생각 위의 예에서 C++ 함수를 볼 수 있습니다,주의하십시오.

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

+0

는 @ Hansjoerg의 대답보다 더 안전합니다. 틀 렸으면 고쳐줘. – VOX

+0

@VOX - 실제로는 똑같은 기술입니다. 링크 된 기사의 더 직접적인 변환 만 사용했습니다 (따라서 클래스는 FAT32뿐만 아니라 FAT [16] 및 NTFS도 지원함). .NET, Hansjoerg의 대답 이미 사용 가능한 더 많은 .NET 래퍼를 사용합니다. – Mormegil

+0

감사합니다. 받아 들였습니다 – VOX

3

다음은 FAT32 볼륨의 일련 번호를 읽고 쓰는 작은 예제입니다. 샘플을 작게 유지하기 위해 모든 오류 처리가 생략되었습니다.

볼륨 섹터에 직접 액세스하면 데이터가 손실되거나 손상 될 수 있습니다. 따라서 아래의 예제를 사용할 때주의하십시오 (프로덕션 용도로는 적합하지 않습니다). 보증 없음!

아래 예제에서는 Win32 API GetDiskFreeSpace (.Net interop 사용)를 사용하여 FAT32 볼륨의 섹터 당 바이트를 얻습니다. fat 볼륨을 열려면 FileStream 클래스 이 디스크 파티션을 직접 열 수 없으므로 Win32 API CreateFile을 사용합니다.

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

또한 당신은 당신의 컴퓨터에 가능한 드라이브를 열거하는 닷넷 프레임 워크의 DriveInfo를 클래스를 사용할 수 있습니다.

희망. 도움이됩니다.