Mit dem IOCTL_CDROM_READ_TOC control code kann man die Vorgehensweise aus Table of Content (MMC) vereinfachen. Man benötigt keinen Command Descriptor Block und muss die Antwort nicht auswerten sondern erhält sofort eine aufbereitete Struktur. Ausserdem benötigt man keine Schreibrechte für die Laufwerke. Der IOCTL_CDROM_READ_TOC control code ist in der Headerdatei Ntddcdrm.h definert. Diese findet sich nicht im MSDN sondern im DDK. Am besten bemüht man eine Internetsuchmaschine und findet zum Beispiel hier:
#define IOCTL_CDROM_READ_TOC
CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
Die Function CTL_CODE ist zum Beispiel bei OSR Online erläutert. Die Konstanten finden sich in der Ntddcdrm.h und der devioctl.h (Zum Beispiel hier).
#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
#define FILE_DEVICE_CD_ROM 0x00000002
#define FILE_READ_ACCESS ( 0x0001 )
#define METHOD_BUFFERED 0
Daraus ergibt sich dann in Delphi folgendes:
const
FILE_DEVICE_CD_ROM = $00000002;
IOCTL_CDROM_BASE = FILE_DEVICE_CD_ROM;
FILE_READ_ACCESS = $0001;
METHOD_BUFFERED = 0;
const
IOCTL_CDROM_READ_TOC = (IOCTL_CDROM_BASE shl 16) or
(FILE_READ_ACCESS shl 14) or
($0000 shl 2) or
METHOD_BUFFERED;
Als Antwort erhält man eine CDROM_TOC Struktur, welche die TRACK_DATA Struktur enthält:
typedef struct _CDROM_TOC {
UCHAR Length[2];
UCHAR FirstTrack;
UCHAR LastTrack;
TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
} CDROM_TOC, *PCDROM_TOC;
typedef struct _TRACK_DATA {
UCHAR Reserved;
UCHAR Control :4;
UCHAR Adr :4;
UCHAR TrackNumber;
UCHAR Reserved1;
UCHAR Address[4];
} TRACK_DATA, *PTRACK_DATA;
Die Constante MAXIMUM_NUMBER_TRACKS findet sich in der ntddcdrm.h:
#define MAXIMUM_NUMBER_TRACKS 100
Daraus ergibt sich eine ähnliche Struktur wie bereits in Table of Content (MMC). Sie unterscheidet darin, dass hier die Trackdata in einem statischen Array enthalten sind sowie Control und Adr als Halbbytes definiert sind, was es in Delphi nicht gibt. In Delphi ergibt sich:
const
MAXIMUM_NUMBER_TRACKS = 100;
type
TAddress = Array[0..3] of Byte;
type
TTRACK_DATA = record
Reserved : Byte;
Adr_and_Control : Byte;
TrackNumber : Byte;
Reserved1 : Byte;
Address : TAddress;
end;
PTRACK_DATA = ^TTRACK_DATA;
type
TCDROM_TOC = record
Length : Word;
FirstTrack : Byte;
LastTrack : Byte;
TrackData : Array[1..MAXIMUM_NUMBER_TRACKS] of TTRACK_DATA;
end;
PCDROM_TOC = ^TCDROM_TOC;
Erläuterungen zu den Variablen findet man bei MSDN nicht. Da die Werte die selben sind wie bei der Verwendung der MMC sollten sie auch die selbe Bedeutung besitzen.
Die Ausführung reduziert sich auf:
...
begin
...
{
* Handle holen.
}
dwDesiredAccess := GENERIC_READ;
dwShareMode := FILE_SHARE_READ;
aDevice := CreateFile(PAnsiChar(Format('\\.\%s:', [aDrive])),
dwDesiredAccess, dwShareMode,
nil, OPEN_EXISTING, 0, 0);
{
* TOC initialisieren.
}
FillChar(TOC, SizeOf(TOC), $00);
{
* Befehl ausführen.
}
DeviceIoControl(aDevice, IOCTL_CDROM_READ_TOC, nil, 0, @TOC, SizeOf(TOC),
nReturned, nil);
TOC.Length := TOC.Length and $00FF shl 8) or (TOC.Length and $FF00 shr 8)
...
end;
Der Datentyp Word scheint in C und Delphi etwas unterschiedlich zu sein. Deshalb muss man die Bytes der Länge tauschen, da $1200 (4608) nicht das selbe ist wie $0012 (18).
Im Gegensatz zu Table of Content (MMC) führt die Ausführung von DeviceIoControl hier zu einem Fehler "Das Gerät ist nicht bereit" wenn kein Datenträger eingelegt ist.
Demo, welche diese Funktion nutzt:

Medien Disc TOC (IOCTL) (podDiscTOC(IOCTL).7z - 269 kb) MD5 (1 kb). Stand: 26. Februar 2012
Fr_An - Erstellt: 18. Februar 2012 - Letzte Änderung: 26. Februar 2012
seit 28. Juli 2010