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.