Introduction
Well, we’re going to create a simple CD player in C#. Be warned, though, that this is a lot of work. Therefore, this series being divided into three parts.
In this part, I am only going to concentrate on most of the Windows API functions that can be used to create the various aspects of the CD player in the background.
Let’s create a project quickly! Remember: This project will be used in the next parts as well, so be careful where you store it.
Open Visual Studio and create a new C# Windows project. Design your form to look like Figure 1:

Figure 1: Design
Add a class and name it something like WINAPI, and add the following namespaces.
using System;
using System.Runtime.InteropServices;
This tells the compiler that we will be making use of the built-in Windows functions.
Add all the constants and variables.
public const uint IOCTL_CDROM_READ_TOC = 0x00024000;
public const uint IOCTL_STORAGE_CHECK_VERIFY = 0x002D4800;
public const uint IOCTL_CDROM_RAW_READ = 0x0002403E;
public const uint IOCTL_STORAGE_LOAD_MEDIA = 0x002D480C;
public const uint IOCTL_STORAGE_EJECT_MEDIA = 0x002D4808;
public const uint GENERIC_READ = 0x80000000;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint OPEN_EXISTING = 3;
public byte TrackNumber;
public byte Reserved1;
public byte Address_0;
public byte Address_1;
public byte Address_2;
public byte Address_3;
These hold the values to be used with the API functions; these will follow soon. Add the Enumerations and structs now:
public enum DriveTypes : uint
{
DRIVE_UNKNOWN = 0,
DRIVE_NO_ROOT_DIR,
DRIVE_REMOVABLE,
DRIVE_FIXED,
DRIVE_REMOTE,
DRIVE_CDROM,
DRIVE_RAMDISK
};
public enum TRACK_MODE_TYPE { YellowMode2, XAForm2, CDDA }
[StructLayout(LayoutKind.Sequential)]
public class RAW_READ_INFO
{
public long DiskOffset = 0;
public uint SectorCount = 0;
public TRACK_MODE_TYPE TrackMode = TRACK_MODE_TYPE.CDDA;
}
[StructLayout(LayoutKind.Sequential)]
public class CDROM_TOC
{
public ushort Length;
public byte FirstTrack = 0;
public byte LastTrack = 0;
public TrackDataList TrackData;
public CDROM_TOC()
{
TrackData = new TrackDataList();
Length = (ushort)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
public class PREVENT_MEDIA_REMOVAL
{
public byte PreventMediaRemoval = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct TRACK_DATA
{
public byte Reserved;
private byte BitMapped;
public byte Control
{
get
{
return (byte)(BitMapped & 0x0F);
}
set
{
BitMapped = (byte)((BitMapped & 0xF0) |
(value & (byte)0x0F));
}
}
public byte Adr
{
get
{
return (byte)((BitMapped & (byte)0xF0) >> 4);
}
set
{
BitMapped = (byte)((BitMapped & (byte)0x0F) |
(value << 4));
}
}
[StructLayout(LayoutKind.Sequential)]
public class TrackDataList
{
public const int MAXIMUM_NUMBER_TRACKS = 100;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
MAXIMUM_NUMBER_TRACKS * 8)]
private byte[] Data;
public TRACK_DATA this[int Index]
{
get
{
if ((Index < 0) | (Index >= MAXIMUM_NUMBER_TRACKS))
{
throw new IndexOutOfRangeException();
}
TRACK_DATA res;
GCHandle handle = GCHandle.Alloc(Data,
GCHandleType.Pinned);
try
{
IntPtr buffer = handle.AddrOfPinnedObject();
buffer = (IntPtr)(buffer.ToInt32() + (Index *
Marshal.SizeOf(typeof(TRACK_DATA))));
res = (TRACK_DATA)Marshal.PtrToStructure(buffer,
typeof(TRACK_DATA));
}
finally
{
handle.Free();
}
return res;
}
}
And now, the methods:
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public extern static DriveTypes GetDriveType(string sDrive);
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static int CloseHandle(IntPtr hObject);
public const uint IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static int DeviceIoControl(IntPtr hDevice,
uint IoControlCode,
IntPtr lpInBuffer, uint InBufferSize,
IntPtr lpOutBuffer, uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static int DeviceIoControl(IntPtr hDevice, uint
IoControlCode,
IntPtr InBuffer, uint InBufferSize,
[Out] CDROM_TOC OutTOC, uint OutBufferSize,
ref uint BytesReturned,
IntPtr Overlapped);
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static int DeviceIoControl(IntPtr hDevice,
uint IoControlCode,
[In] PREVENT_MEDIA_REMOVAL InMediaRemoval, uint InBufferSize,
IntPtr OutBuffer, uint OutBufferSize,
ref uint BytesReturned,
IntPtr Overlapped);
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static int DeviceIoControl(IntPtr hDevice,
uint IoControlCode,
[In] RAW_READ_INFO rri, uint InBufferSize,
[In, Out] byte[] OutBuffer, uint OutBufferSize,
ref uint BytesReturned,
IntPtr Overlapped);
[System.Runtime.InteropServices.DllImport("Kernel32.dll",
SetLastError = true)]
public extern static IntPtr CreateFile(string FileName,
uint DesiredAccess,
uint ShareMode, IntPtr lpSecurityAttributes,
uint CreationDisposition, uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
Conclusion
In the next installment, we will get into the meat of the necessary code to read CD tracks and identify pits. Until then, stay in practise!