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!