首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 讀取硬碟序列號:P/Invoke 呼叫 Windows API 完整實作

     編輯:關於C語言

核心架構

透過 DeviceIoControl 呼叫 SMART_RCV_DRIVE_DATA 控制碼,向磁碟控制器發送 IDENTIFY DEVICE 命令(ATA: 0xEC, ATAPI: 0xA1)取得識別資料區,解析序列號欄位。

關鍵結構定義

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct IDSECTOR {
    public ushort wGenConfig;
    // ... 略 ...
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string sSerialNumber;  // 序列號位於此欄位
    // ... 略 ...
}

[StructLayout(LayoutKind.Sequential)]
internal struct SENDCMDINPARAMS {
    public uint cBufferSize;
    public IDEREGS irDriveRegs;
    public byte bDriveNumber;
    // ... 略 ...
}

[StructLayout(LayoutKind.Sequential)]
internal struct SENDCMDOUTPARAMS {
    public uint cBufferSize;
    public DRIVERSTATUS DriverStatus;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 513)]
    public byte[] bBuffer;  // 包含 IDSECTOR 資料
}

核心邏輯流程

  1. 檢查作業系統版本(需 Windows NT/2000/XP 以上)
  2. CreateFile 開啟 \\.\PhysicalDriveX 取得裝置控制代碼
  3. DeviceIoControl 呼叫 DFP_GET_VERSION 確認裝置支援
  4. 建構 SENDCMDINPARAMS 設定 ATA/ATAPI 識別命令
  5. DeviceIoControl 呼叫 DFP_RECEIVE_DRIVE_DATA 取得回應資料
  6. 從 bBuffer 位移 20-39 位元組解析序列號(位元組序交換)
  7. CloseHandle 釋放資源

完整程式碼

public static string Read(byte drive) {
    OperatingSystem os = Environment.OSVersion;
    if (os.Platform != PlatformID.Win32NT) throw new NotSupportedException("僅支援 Windows NT/2000/XP");

    string driveName = "\\\\.\\PhysicalDrive" + drive;
    uint device = CreateFile(driveName, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (device == INVALID_HANDLE_VALUE) return "";

    GETVERSIONOUTPARAMS verPara = new GETVERSIONOUTPARAMS();
    uint bytRv = 0;
    if (DeviceIoControl(device, DFP_GET_VERSION, 0, 0, ref verPara, Marshal.SizeOf(verPara), ref bytRv, 0) != 0) {
        if (verPara.bIDEDeviceMap > 0) {
            byte bIDCmd = (byte)((verPara.bIDEDeviceMap >> drive & 0x10) != 0 ? IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY);
            SENDCMDINPARAMS scip = new SENDCMDINPARAMS();
            SENDCMDOUTPARAMS scop = new SENDCMDOUTPARAMS();
            scip.cBufferSize = IDENTIFY_BUFFER_SIZE;
            scip.irDriveRegs.bFeaturesReg = 0;
            scip.irDriveRegs.bSectorCountReg = 1;
            scip.irDriveRegs.bCylLowReg = 0;
            scip.irDriveRegs.bCylHighReg = 0;
            scip.irDriveRegs.bDriveHeadReg = (byte)(0xA0 | ((drive & 1) << 4));
            scip.irDriveRegs.bCommandReg = bIDCmd;
            scip.bDriveNumber = drive;

            if (DeviceIoControl(device, DFP_RECEIVE_DRIVE_DATA, ref scip, Marshal.SizeOf(scip), ref scop, Marshal.SizeOf(scop), ref bytRv, 0) != 0) {
                StringBuilder s = new StringBuilder();
                for (int i = 20; i < 40; i += 2) {
                    s.Append((char)scop.bBuffer[i + 1]);
                    s.Append((char)scop.bBuffer[i]);
                }
                CloseHandle(device);
                return s.ToString().Trim();
            }
        }
    }
    CloseHandle(device);
    return "";
}

注意: 需系統管理員權限執行;NVMe/SCSI/USB 裝置可能不支援此介面;建議搭配 WMI Win32_DiskDrive 作為替代方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved