DeviceIoControl IOCTL_DISK_GET_DRIVE_LAYOUT_EX liefert nur die erste Partion korrekt

    DeviceIoControl IOCTL_DISK_GET_DRIVE_LAYOUT_EX liefert nur die erste Partion korrekt

    Hi,

    ich versuche mir die Festplatte und Partitionen auszulesen. Wie schon der Titel sagt, bekomme ich nur die 1. Partition korrekt. Alle weiteren werde im Array nicht korrekt zurückgeliefert.
    Ich such schon verzweifelt nach dem Fehler, kann ihn aber nicht finden. Hat jemand einen Tipp für mich, woran das liegen könnte?

    Hier mal mein Code:

    Quellcode

    1. unit Unit1;
    2. interface
    3. uses
    4. Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    5. Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    6. type
    7. TForm1 = class(TForm)
    8. mmo1: TMemo;
    9. btn2: TButton;
    10. mmo2: TMemo;
    11. procedure btn2Click(Sender: TObject);
    12. private
    13. function GetSizeAsString(Size: Int64): string;
    14. { Private-Deklarationen }
    15. public
    16. { Public-Deklarationen }
    17. end;
    18. const
    19. PARTITION_BASIC_DATA_GUID = 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7';
    20. PARTITION_ENTRY_UNUSED_GUID = '00000000-0000-0000-0000-000000000000';
    21. PARTITION_SYSTEM_GUID = 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b';
    22. PARTITION_MSFT_RESERVED_GUID = 'e3c9e316-0b5c-4db8-817d-f92df00215ae';
    23. PARTITION_LDM_METADATA_GUID = '5808c8aa-7e8f-42e0-85d2-e1e90434cfb3';
    24. PARTITION_LDM_DATA_GUID = 'af9b60a0-1431-4f62-bc68-3311714a69ad';
    25. PARTITION_MSFT_RECOVERY_GUID = 'de94bba4-06d1-4d40-a16a-bfd50179d6ac';
    26. GTP_ATTRIBUTE_PLATFORM_REQUIRED = $0000000000000001;
    27. GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER = $8000000000000000;
    28. GPT_BASIC_DATA_ATTRIBUTE_HIDDEN = $4000000000000000;
    29. GTP_BASIC_data_ATTRIBUTE_SHADOW_COPY = $2000000000000000;
    30. GTP_BASIC_DATA_ATTRIBUTE_READ_ONLY = $1000000000000000;
    31. PARTITION_ENTRY_UNUSED = $00;
    32. PARTITION_EXTENDED = $05;
    33. PARTITION_FAT_12 = $01;
    34. PARTITION_FAT_16 = $04;
    35. PARTITION_FAT32 = $0B;
    36. PARTITION_IFS = $07;
    37. PARTITION_LDM = $42;
    38. PARTITION_NTFT = $80;
    39. VALID_NTFT = $C0;
    40. Type
    41. PARTITION_STYLE = type Integer;
    42. PARTITION_INFORMATION_MBR = record
    43. PartitionType: Byte;
    44. BootIndicator: BOOL;
    45. RecognizedPartition: BOOL;
    46. HiddenSecorts: DWORD;
    47. end;
    48. PARTITION_INFORMATION_GPT = record
    49. PartitonType: TGUID;
    50. PartitionId: TGUID;
    51. Attributes: DWORD64;
    52. Name: WCHAR;
    53. end;
    54. _PARTITION_INFORMATION_EX = record
    55. PartitionStyle: PARTITION_STYLE;
    56. StartingOffset: LARGE_INTEGER;
    57. PartitionLength: LARGE_INTEGER;
    58. PartitionNumber: ULONG;
    59. RewritePartition: Boolean;
    60. case Integer of
    61. 0: (Mbr: PARTITION_INFORMATION_MBR);
    62. 1: (Gpt: PARTITION_INFORMATION_GPT);
    63. end;
    64. DRIVE_LAYOUT_INFORMATION_MBR = record
    65. Signature: ULONG;
    66. end;
    67. DRIVE_LAYOUT_INFORMATION_GPT = record
    68. DiskID: TGUID;
    69. StartingusableOffset: LARGE_INTEGER;
    70. UsableLength: LARGE_INTEGER;
    71. MaxPartitionCount: ULONG;
    72. end;
    73. DRIVE_LAYOUT_INFORMATION_EX = record
    74. PartitionStyle: DWORD;
    75. PartitionCount: DWORD;
    76. DriveLayoutInfoType: record
    77. case Integer of
    78. 0: (Mbr: DRIVE_LAYOUT_INFORMATION_MBR);
    79. 1: (Gpt: DRIVE_LAYOUT_INFORMATION_GPT);
    80. end;
    81. PartitionInfoEx: array[0..15] of _PARTITION_INFORMATION_EX;
    82. end;
    83. const
    84. PARTITION_STYLE_MBR = PARTITION_STYLE(0);
    85. PARTITION_STYLE_GPT = PARTITION_STYLE(1);
    86. PARTITION_STYLE_RAW = PARTITION_STYLE(2);
    87. function FindFirstVolume(lpszVolumeName: LPTSTR; cchBufferLength: DWord): THandle; stdcall; external 'kernel32.dll' name 'FindFirstVolumeW';
    88. function FindNextVolume(hFindVolume: THandle; lpszVolumeName: LPTSTR; cchBufferLength: DWORD): BOOL; stdcall; external 'kernel32.dll' name 'FindNextVolumeW';
    89. function FindVolumeClose(hFindVolume: THandle): BOOL; stdcall; external 'kernel32.dll' name 'FindVolumeClose';
    90. function GetVolumePathNames(lpszVolumeName: LPCTSTR; lpszVolumePathName: LPTSTR; cchBufferLength: DWORD; lpcchReturnLength: PDWORD): BOOL; stdcall; external 'kernel32.dll' name 'GetVolumePathNamesForVolumeNameW';
    91. var
    92. Form1: TForm1;
    93. implementation
    94. {$R *.dfm}
    95. procedure TForm1.btn2Click(Sender: TObject);
    96. var
    97. RetBytes: DWORD;
    98. hDevice: Cardinal;
    99. Status: LongBool;
    100. Drive: string;
    101. Layout: DRIVE_LAYOUT_INFORMATION_EX;
    102. I,p: Integer;
    103. PartCount: Integer;
    104. begin
    105. mmo1.Lines.Clear;
    106. mmo2.Lines.Clear;
    107. for I := 0 to 15 do
    108. begin
    109. Drive:='\\.\PhysicalDrive'+IntToStr(i);
    110. hDevice:=CreateFile(PChar(Drive), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    111. if hDevice<>INVALID_HANDLE_VALUE then
    112. begin
    113. //SetLength(Layout.PartitionInfoEx, 15);
    114. Status:=DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, nil, 0, @Layout, SizeOf(DRIVE_LAYOUT_INFORMATION_EX), RetBytes, nil);
    115. if (Status=False) then
    116. mmo1.lines.Add(IntToStr(i+1)+'. Festplatte abfrage fehlgeschalten') else
    117. begin
    118. PartCount:=0;
    119. mmo1.Lines.Add(IntToStr(i+1)+'. Festplatte = '+inttostr(Layout.PartitionCount)+' Partitionen');
    120. for p := 0 to Layout.PartitionCount-1 do
    121. begin
    122. // if (Layout.partitionInfoEx[p].StartingOffset.QuadPart<>0) and (Layout.partitionInfoEx[p].PartitionLength.QuadPart<>0) then
    123. // begin
    124. mmo1.Lines.Add(' '+inttostr(p+1)+'. Partition: ');
    125. case Layout.partitionInfoEx[p].PartitionStyle of
    126. PARTITION_STYLE_MBR: mmo1.Lines.Add(' Part-Typ : MBR');
    127. PARTITION_STYLE_GPT: mmo1.Lines.Add(' Part-Typ : GPT');
    128. PARTITION_STYLE_RAW: mmo1.Lines.Add(' Part-Typ : RAW');
    129. end;
    130. if Layout.PartitionInfoEx[p].PartitionStyle=PARTITION_STYLE_MBR then
    131. begin
    132. case Layout.PartitionInfoEx[p].Mbr.PartitionType of
    133. PARTITION_ENTRY_UNUSED: mmo1.lines.Add(' Part-Style : PARTITION_ENTRY_UNUSED');
    134. PARTITION_EXTENDED: mmo1.lines.Add(' Part-Style : PARTITION_EXTENDED');
    135. PARTITION_FAT_12: mmo1.lines.Add(' Part-Style : PARTITION_FAT_12');
    136. PARTITION_FAT_16: mmo1.lines.Add(' Part-Style : PARTITION_FAT_16');
    137. PARTITION_FAT32: mmo1.lines.Add(' Part-Style : PARTITION_FAT32');
    138. PARTITION_IFS: mmo1.lines.Add(' Part-Style : PARTITION_IFS');
    139. PARTITION_LDM: mmo1.lines.Add(' Part-Style : PARTITION_LDM');
    140. PARTITION_NTFT: mmo1.lines.Add(' Part-Style : PARTITION_NTFT');
    141. VALID_NTFT: mmo1.lines.Add(' Part-Style : VALID_NTFT');
    142. else
    143. mmo1.lines.Add(' Part-Style : Unbekannt');
    144. end;
    145. if Layout.PartitionInfoEx[p].Mbr.PartitionType<>PARTITION_ENTRY_UNUSED then
    146. Inc(PartCount);
    147. end;
    148. mmo1.lines.Add(' PartitionNr: '+IntToStr(Layout.partitionInfoEx[p].PartitionNumber));
    149. mmo1.Lines.Add(' StartSektor: '+IntToStr(Layout.partitionInfoEx[p].StartingOffset.QuadPart));
    150. mmo1.lines.Add(' Länge : '+IntToStr(Layout.partitionInfoEx[p].PartitionLength.QuadPart));
    151. mmo1.Lines.Add(' Größe : '+GetSizeAsString(Layout.partitionInfoEx[p].PartitionLength.QuadPart));
    152. // end;
    153. mmo2.Lines.Add(IntToStr(i+1)+'. Festplatte = '+inttostr(PartCount)+' Partitionen');
    154. if Layout.PartitionInfoEx[p].Mbr.PartitionType=PARTITION_IFS then
    155. begin
    156. mmo2.Lines.Add(#9+inttostr(p+1)+'.Partition:'+#9+GetSizeAsString(Layout.partitionInfoEx[p].PartitionLength.QuadPart));
    157. end;
    158. end;
    159. end;
    160. CloseHandle(hDevice);
    161. end;// else mmo1.lines.Add(IntToStr(i)+'. Festplatte hat kein hDeviceHandle');
    162. end;
    163. end;
    164. function TForm1.GetSizeAsString(Size: Int64): string;
    165. var
    166. new: Extended;
    167. Sign: String;
    168. c: Integer;
    169. begin
    170. c:=0;
    171. new:=Size;
    172. while new>1024 do
    173. begin
    174. new:=new/1024;
    175. Inc(c);
    176. end;
    177. case c of
    178. 0: Sign:=' Byte';
    179. 1: Sign:=' KB';
    180. 2: Sign:=' MB';
    181. 3: Sign:=' GB';
    182. 4: Sign:=' TB';
    183. 5: Sign:=' PB';
    184. 6: Sign:=' EB';
    185. 7: Sign:=' ZB';
    186. 8: Sign:=' YB';
    187. else
    188. Sign:=' ('+intToStr(c)+')';
    189. end;
    190. Result:=FormatFloat('#,##0.00', new)+Sign;
    191. end;
    192. end.


    Bin für jede Idee dankbar.

    Gruß Dieter
    Ja, eine Idee schon. Nämlich, dass die Header nicht ganz richtig übersetzt sind.
    Zunächst ein Hinweis:

    Delphi-Code

    1. PARTITION_STYLE = type Integer;

    Das geht, aber es geht auch schöner:

    Delphi-Code

    1. {$z4}
    2. PARTITION_STYLE = (PARTITION_STYLE_MBR = 0, PARTITION_STYLE_GPT = 1, PARTITION_STYLE_RAW = 2, PARTITION_STYLE);

    Du kannst einen Enum aus C direkt auch in Delphi als Enum verwenden, wenn du zuvor {$z4} sagst, also, dass die Ausrichtung des Enums an Vier-Byte-Grenzen geschehen soll.
    Und ich vermute, dass gerade die Ausrichtung der Records das Problem sein wird. Offiziell sollte Delphi ein Record-Feld ja nach der entsprechenden Alignment-Einstellung im Compiler ausrichten, was aber wohl laut diesem Artikel nicht funktioniert, so dass die Records wohl richtig übersetzt wurden (du kannst ja auch auf dein erstes Element zugreifen). Allerdings werden Arrays auch ausgerichtet, das könnte Probleme machen, ich weiß nicht, wie C das da handhabt. Probier mal

    Delphi-Code

    1. DRIVE_LAYOUT_INFORMATION_EX = record
    2. ...
    3. PartitionInfoEx: packed array[0..15] of _PARTITION_INFORMATION_EX;
    4. end;

    das Array zu packen.
    Übrigens finde ich [0..15] etwas fragwürdig. Laut MSDN ist PartitionEntry
    A variable-sized array of PARTITION_INFORMATION_EX structures, one structure for each partition on the drive.

    Also würde ich es eher als [0..0] deklarieren; das ist die Möglichkeit, möglichst schnell Probleme zu finden: Wenn du Range-Checking aktiviert hast, wirst du bei [0..0] sehr schnell einen Fehler bekommen, sobald du auf die zweite Partition zugreifst. Und zwei Partitionen hat eigentlich jede Festplatte heutzutage. Bei [0..15] wirst du aber in einer Testumgebung wahrscheinlich nie ein Problem bemerken, was aber in einer Produktivumgebung durchaus auftreten könnte. Also muss für den Zugriff auf dieses Array die Bereichsprüfung abgeschaltet sein.
    Master of the EDH ;)