3

在 Windows >= XP 上使用 Delphi 7,如何从计算机中检索每个磁盘的磁盘签名?最好不使用 WMI 或 Diskpart。

如果可能的话,也要快..

谢谢你。

后期编辑:

Documentation: http://pcsupport.about.com/od/termsd/g/disk-signature.htm
MBR disks: http://diddy.boot-land.net/firadisk/files/signature.htm
GPT disks: http://thestarman.pcministry.com/asm/mbr/GPT.htm

How to get it with DiskPart (method found on Google when searching "disk signature"):
Diskpart >> list disk >> select disk [n] >>
detail disk >> Disk ID: 0E35445B for MBR disks
and GUID: 55FD03F2-6B11-49DF-8167-D30B94A4509D for GPT Disks
4

3 回答 3

5

您可以使用DeviceIoControlIOCTL_DISK_GET_DRIVE_LAYOUT_EX获取您需要的信息。

program DiskSignatureGuid;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

type
  TDriveLayoutInformationMbr = record
    Signature: DWORD;
  end;

  TDriveLayoutInformationGpt = record
    DiskId: TGuid;
    StartingUsableOffset: Int64;
    UsableLength: Int64;
    MaxPartitionCount: DWORD;
  end;

  TPartitionInformationMbr = record
    PartitionType: Byte;
    BootIndicator: Boolean;
    RecognizedPartition: Boolean;
    HiddenSectors: DWORD;
  end;

  TPartitionInformationGpt = record
    PartitionType: TGuid;
    PartitionId: TGuid;
    Attributes: Int64;
    Name: array [0..35] of WideChar;
  end;

  TPartitionInformationEx = record
    PartitionStyle: Integer;
    StartingOffset: Int64;
    PartitionLength: Int64;
    PartitionNumber: DWORD;
    RewritePartition: Boolean;
    case Integer of
      0: (Mbr: TPartitionInformationMbr);
      1: (Gpt: TPartitionInformationGpt);
  end;

  TDriveLayoutInformationEx = record
    PartitionStyle: DWORD;
    PartitionCount: DWORD;
    DriveLayoutInformation: record
      case Integer of
      0: (Mbr: TDriveLayoutInformationMbr);
      1: (Gpt: TDriveLayoutInformationGpt);
    end;
    PartitionEntry: array [0..15] of TPartitionInformationGpt;
    //hard-coded maximum of 16 partitions
  end;

const
  PARTITION_STYLE_MBR = 0;
  PARTITION_STYLE_GPT = 1;
  PARTITION_STYLE_RAW = 2;

const
  IOCTL_DISK_GET_DRIVE_LAYOUT_EX = $00070050;

procedure Main;
const
  // Max number of drives assuming primary/secondary, master/slave topology
  MAX_IDE_DRIVES = 16;
var
  i: Integer;
  Drive: string;
  hDevice: THandle;
  DriveLayoutInfo: TDriveLayoutInformationEx;
  BytesReturned: DWORD;
begin
  for i := 0 to MAX_IDE_DRIVES - 1 do
  begin
    Drive := '\\.\PHYSICALDRIVE' + IntToStr(i);
    hDevice := CreateFile(PChar(Drive), 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
      nil, OPEN_EXISTING, 0, 0);
    if hDevice <> INVALID_HANDLE_VALUE then
    begin
      if DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, nil, 0,
        @DriveLayoutInfo, SizeOf(DriveLayoutInfo), BytesReturned, nil) then
      begin
        case DriveLayoutInfo.PartitionStyle of
        PARTITION_STYLE_MBR:
          Writeln(Drive + ', MBR, ' +
            IntToHex(DriveLayoutInfo.DriveLayoutInformation.Mbr.Signature, 8));
        PARTITION_STYLE_GPT:
          Writeln(Drive + ', GPT, ' +
            GUIDToString(DriveLayoutInfo.DriveLayoutInformation.Gpt.DiskId));
        PARTITION_STYLE_RAW:
          Writeln(Drive + ', RAW');
        end;
      end;
      CloseHandle(hDevice);
    end;
  end;
end;

begin
  Main;
  Readln;
end.

请注意,由于0传递给 的dwDesiredAccess参数CreateFile,因此不需要提升权限。尽管有些不透明,但在文档中对此进行了解释:

对磁盘或卷的直接访问受到限制......必须满足以下要求才能使此类调用成功:

  • 调用者必须具有管理权限。
  • dwCreationDisposition 参数必须具有 OPEN_EXISTING 标志。
  • 打开卷或软盘时,dwShareMode 参数必须具有 FILE_SHARE_WRITE 标志。

注意dwDesiredAccess参数可以为零,允许应用程序在不访问设备的情况下查询设备属性例如,这对于应用程序确定软盘驱动器的大小及其支持的格式很有用,而无需驱动器中的软盘。它也可以用于读取统计信息,而不需要更高级别的数据读/写权限。

于 2013-06-16T10:48:32.877 回答
2

取决于“磁盘签名”是什么。我不知道那是什么。我知道以下代码返回:

- \\?\ide#disksandisk_sdssdx120gg25___________________r201____#5&20f0fb49&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
- \\?\ide#diskst1000dm003-9yn162______________________hp13____#5&33aaabee&0&1.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

那些是“磁盘签名”吗?

它使用 Windows设置 API

procedure GetDisks(slDisks: TStrings);
var
    InterfaceDevInfo: HDEVINFO;
    index: DWORD;
    status: BOOL;
    Name: string;
begin
    { Get the interface device information set that contains all devices of event class. }
    InterfaceDevInfo := SetupDiGetClassDevs(
            @GUID_DEVINTERFACE_DISK,
            nil,                            // Enumerator
            0,                              // Parent Window
            (DIGCF_PRESENT or DIGCF_INTERFACEDEVICE)    // Only Devices present & Interface class
        );

    if InterfaceDevInfo = HDEVINFO(INVALID_HANDLE_VALUE) then
    begin
        RaiseEnumerateDisksError('SetupDiGetClassDevs failed', GetLastError);
        Exit;
    end;

    { Enumerate all the disk devices }
    Index := 0;
    while (True) do
    begin
        Status := GetDeviceProperty(InterfaceDevInfo, index, Name);
        if not Status then
            Break;

        slDisks.Add(Name);

        Inc(Index);
    end;
    SetupDiDestroyDeviceInfoList(InterfaceDevInfo);
end;

function GetDeviceProperty(InterfaceDevInfo: HDEVINFO; Index: LongWord;
        out Name: string): Boolean;
var
    interfaceData: TSPDeviceInterfaceData;
    interfaceDetailData: PSPDeviceInterfaceDetailData;
    status: BOOL;
    interfaceDetailDataSize: LongInt;
    reqSize: Cardinal;
    errorCode: LongInt;
begin
    Result := False;

    ZeroMemory(@interfaceData, SizeOf(InterfaceData));
    interfaceData.cbSize := SizeOf(interfaceData);

    //Retreiving context structure for specified device interface
    status := SetupDiEnumDeviceInterfaces(
            InterfaceDevInfo,               // Interface Device Info handle
            nil,                                // Device Info data
            GUID_DEVINTERFACE_DISK,     // Interface registered by driver
            Index,                          // Member
            interfaceData);             // Device Interface Data

    if not status then
    begin
        errorCode := GetLastError;
        if (errorCode = ERROR_NO_MORE_ITEMS ) then
        begin
            //no more interfaces, exit returning default value of False
            Exit;
        end
        else
        begin
            RaiseEnumerateDisksError('SetupDiEnumDeviceInterfaces failed.', errorCode);
        end;
    end;

    // Find out required buffer size, so pass nil
    status := SetupDiGetDeviceInterfaceDetail(
            InterfaceDevInfo,       // Interface Device info handle
            @interfaceData,     // Interface data for the event class
            nil,                        // Checking for buffer size
            0,                          // Checking for buffer size
            reqSize,                    // Buffer size required to get the detail data
            nil);                       // Checking for buffer size

    // This call returns ERROR_INSUFFICIENT_BUFFER with reqSize
    // set to the required buffer size. Ignore the above error and
    // pass a bigger penis to get the detail data
    if not status then
    begin
        errorCode := GetLastError;
        if errorCode <> ERROR_INSUFFICIENT_BUFFER then
        begin
            RaiseEnumerateDisksError('SetupDiGetDeviceInterfaceDetail failed.', errorCode);
            Exit;
        end;
    end;

    // Allocate memory to get the interface detail data
    // This contains the devicepath we need to open the device
    interfaceDetailDataSize := reqSize;
    GetMem(interfaceDetailData, interfaceDetailDataSize);
    ZeroMemory(interfaceDetailData, interfaceDetailDataSize);

    interfaceDetailData.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
//  ineerfaceDetailData.cbSize := 5; //ansi version
//  ineerfaceDetailData.cbSize := 6; //unicode version

    //Getting interface detail data into properly sized buffer...
    status := SetupDiGetDeviceInterfaceDetail(
            InterfaceDevInfo,               // Interface Device info handle
            @interfaceData,             // Interface data for the event class
            interfaceDetailData,            // Interface detail data
            interfaceDetailDataSize,    // Interface detail data size
            reqSize,                            // Buffer size required to get the detail data
            nil);                               // Interface device info

    if not Status then
    begin
        RaiseEnumerateDisksError('Error in SetupDiGetDeviceInterfaceDetail', GetLastError);
        Exit;
    end;

    // Now we have the device path.
    Name := PChar(@interfaceDetailData.DevicePath[0]);

(*
    //DevicePath is suitable for a CreateFile, whish is what i want in the end
    hDevice := CreateFile(
            PChar(interfaceDetailData.DevicePath),    // device interface name
            GENERIC_READ or GENERIC_WRITE,       // dwDesiredAccess
            FILE_SHARE_READ or FILE_SHARE_WRITE, // dwShareMode
            nil,                               // lpSecurityAttributes
            OPEN_EXISTING,                      // dwCreationDistribution
            0,                                  // dwFlagsAndAttributes
            0                                // hTemplateFile
        );
*)
    Result := True;
end;
于 2013-06-14T16:57:09.630 回答
1

我在几台计算机上测试了这段代码,它可以工作:

MBR 磁盘:

磁盘签名/标识符是一个 4 字节(长字)数字,在第一次创建主引导记录/分区表时随机生成并存储在字节偏移 1B8(十六进制)或 440(十进制)到 1BB(十六进制)或 443 (dec) 在 MBR 磁盘扇区 (0) 中。因此,在任何 MBR 磁盘上,您都可以直接从该位置读取它:

const
  // Max number of drives assuming primary/secondary, master/slave topology
  MAX_IDE_DRIVES = 16;
var
   i: Integer;
   RawMBR: array[0..511] of Byte;
   btsIO: DWORD;
   hDevice: THandle;
   s: string;
begin
   s := '';
   for i := 0 to MAX_IDE_DRIVES - 1 do
   begin
      hDevice := CreateFile(PChar('\\.\PHYSICALDRIVE' + IntToStr(i)), GENERIC_READ,
         FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
      if hDevice <> INVALID_HANDLE_VALUE then
      begin
         SetFilePointer(hDevice, 0, nil, FILE_BEGIN);  //MBR starts in sector 0
         if not ReadFile(hDevice, RawMBR[0], 512, btsIO, nil) then
         begin
            CloseHandle(hDevice);
            Continue;
         end;
         CloseHandle(hDevice);
         s := s + 'Disk ' + IntToStr(i) + ' = ' + IntToHex(RawMBR[443], 2) + ' ' +
            IntToHex(RawMBR[442], 2) + ' ' + IntToHex(RawMBR[441], 2) +
            ' ' + IntToHex(RawMBR[440], 2) + #13#10;
      end;
   end;
   ShowMessage(s);
end;

GPT 磁盘:

磁盘签名/标识符是第一次创建 GPT 时随机生成的 16 字节 (GUID) 数字,并存储在GPT 磁盘扇区 (1)。因此,在任何 GPT 磁盘上,您都可以直接从该位置读取它:

const
  // Max number of drives assuming primary/secondary, master/slave topology
  MAX_IDE_DRIVES = 16;
var
   i: Integer;
   RawMBR: array[0..511] of Byte;
   btsIO: DWORD;
   hDevice: THandle;
   s: string;
begin
   s := '';
   for i := 0 to MAX_IDE_DRIVES - 1 do
   begin
      hDevice := CreateFile(PChar('\\.\PHYSICALDRIVE' + IntToStr(i)), GENERIC_READ,
         FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
      if hDevice <> INVALID_HANDLE_VALUE then
      begin
         SetFilePointer(hDevice, 512, nil, FILE_BEGIN); //GPT starts in sector 1
         if not ReadFile(hDevice, RawMBR[0], 512, btsIO, nil) then
         begin
            CloseHandle(hDevice);
            Continue;
         end;
         CloseHandle(hDevice);
         s := s + 'Disk ' + IntToStr(i) + ' = ' + IntToHex(RawMBR[59], 2) +
            ' ' + IntToHex(RawMBR[58], 2) + ' ' + IntToHex(RawMBR[57], 2) +
            ' ' + IntToHex(RawMBR[56], 2) + ' - ' + IntToHex(RawMBR[61], 2) +
            ' ' + IntToHex(RawMBR[60], 2) + ' - ' + IntToHex(RawMBR[63], 2) +
            ' ' + IntToHex(RawMBR[62], 2) + ' - ' + IntToHex(RawMBR[64], 2) +
            ' ' + IntToHex(RawMBR[65], 2) + ' - ' + IntToHex(RawMBR[66], 2) +
            ' ' + IntToHex(RawMBR[67], 2) + ' ' + IntToHex(RawMBR[68], 2) +
            ' ' + IntToHex(RawMBR[69], 2) + ' ' + IntToHex(RawMBR[70], 2) +
            ' ' + IntToHex(RawMBR[71], 2) +
            #13#10;
      end;
   end;
   ShowMessage(s);
end;

好的,现在让我们将它们组合起来:

procedure TForm1.Button1Click(Sender: TObject);
const
   // Max number of drives assuming primary/secondary, master/slave topology
   MAX_IDE_DRIVES = 16;
var
   i: Integer;
   DiskType: Byte;
   RawMBR: array[0..511] of Byte;
   btsIO: DWORD;
   hDevice: THandle;
   s: string;
begin
   s := '';
   for i := 0 to MAX_IDE_DRIVES - 1 do
   begin
      hDevice := CreateFile(PChar('\\.\PHYSICALDRIVE' + IntToStr(i)), GENERIC_READ,
         FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
      if hDevice <> INVALID_HANDLE_VALUE then
      begin
         SetFilePointer(hDevice, 512, nil, FILE_BEGIN); //sector 1 for GPT
         if not ReadFile(hDevice, RawMBR[0], 512, btsIO, nil) then
         begin
            CloseHandle(hDevice);
            Continue;
         end;
         if (IntToHex(RawMBR[0], 2) + IntToHex(RawMBR[1], 2) +
            IntToHex(RawMBR[2], 2) + IntToHex(RawMBR[3], 2) +
            IntToHex(RawMBR[4], 2) + IntToHex(RawMBR[5], 2) +
            IntToHex(RawMBR[6], 2) + IntToHex(RawMBR[7], 2)) =
            '4546492050415254' then //EFI PART
            DiskType := 1 //GPT
         else
         begin
            DiskType := 0; //MBR
            SetFilePointer(hDevice, 0, nil, FILE_BEGIN); //sector 0 for MBR
            if not ReadFile(hDevice, RawMBR[0], 512, btsIO, nil) then
            begin
               CloseHandle(hDevice);
               Continue;
            end;
         end;
         CloseHandle(hDevice);
         if DiskType = 0 then
            s := s + 'Disk ' + IntToStr(i) + ' = ' + IntToHex(RawMBR[443], 2) + ' ' +
               IntToHex(RawMBR[442], 2) + ' ' + IntToHex(RawMBR[441], 2) +
               ' ' + IntToHex(RawMBR[440], 2) + #13#10
         else
            s := s + 'Disk ' + IntToStr(i) + ' = ' + IntToHex(RawMBR[59], 2) +
               ' ' + IntToHex(RawMBR[58], 2) + ' ' + IntToHex(RawMBR[57], 2) +
               ' ' + IntToHex(RawMBR[56], 2) + ' - ' + IntToHex(RawMBR[61], 2) +
               ' ' + IntToHex(RawMBR[60], 2) + ' - ' + IntToHex(RawMBR[63], 2) +
               ' ' + IntToHex(RawMBR[62], 2) + ' - ' + IntToHex(RawMBR[64], 2) +
               ' ' + IntToHex(RawMBR[65], 2) + ' - ' + IntToHex(RawMBR[66], 2) +
               ' ' + IntToHex(RawMBR[67], 2) + ' ' + IntToHex(RawMBR[68], 2) +
               ' ' + IntToHex(RawMBR[69], 2) + ' ' + IntToHex(RawMBR[70], 2) +
               ' ' + IntToHex(RawMBR[71], 2) +
               #13#10;
      end;
   end;
   ShowMessage(s);
end;

此代码需要提升权限才能访问磁盘。

于 2013-06-14T22:14:19.343 回答