简体   繁体   中英

how to retrieve the disk signature of all the disks in Windows using Delphi 7?

Using Delphi 7, on a Windows >= XP, how can I retrieve the disk signature of every disk from the computer? Preferably without using WMI or Diskpart.

And if it's possible, to be fast too..

Thank you.

Later edit:

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

You can use DeviceIoControl and IOCTL_DISK_GET_DRIVE_LAYOUT_EX to obtain the information that you require.

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.

Note that since 0 is passed to the dwDesiredAccess parameter of CreateFile , elevated rights are not required. This is explained, albeit somewhat opaquely, in the documentation :

Direct access to the disk or to a volume is restricted ... The following requirements must be met for such a call to succeed:

  • The caller must have administrative privileges.
  • The dwCreationDisposition parameter must have the OPEN_EXISTINGflag.
  • When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITEflag.

Note The dwDesiredAccess parameter can be zero, allowing the application to query device attributes without accessing a device. This is useful for an application to determine the size of a floppy disk drive and the formats it supports without requiring a floppy disk in a drive, for instance. It can also be used for reading statistics without requiring higher-level data read/write permission.

Depends what a "disk signature" is. i don't know what that is. i do know the following code returns:

- \\?\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}

Are those "disk signatures" ?

It uses the Windows Setup 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;

I tested this code on several computers and it works:

MBR disks:

The Disk Signature/Identifier is a 4-byte (longword) number that is randomly generated when the Master Boot Record/Partition Table is first created and stored at byte offset 1B8 (hex) or 440 (dec) through 1BB (hex) or 443 (dec) in the MBR disk sector (0). So, on any MBR disks you can read it directly from that location:

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 Disks:

The Disk Signature/Identifier is a 16-byte (GUID) number that is randomly generated when the GPT is first created and stored at byte offset 038 (hex) or 56 (dec) through 047 (hex) or 71 (dec) in the GPT disk sector (1). So, on any GPT disks you can read it directly from that location:

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;

Ok, now let's combine them:

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;

This code requires elevated privileges to gain access to the disks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM