dcsimg

Creating a CD Player in C#, Part 1: The Windows Functions

WEBINAR:
On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame


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:

Design
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!



This article was originally published on April 9th, 2020

About the Author

Hannes DuPreez

Hannes du Preez is a self-taught developer who started learning to program in the days of QBasic. He has written several articles over the years detailing his programming quests and adventures. .NET is his second love, just after his wife and kid. He has always been an avid supporter of .NET since the beginning and is an expert in VB and C#. He was given the Microsoft Most Valuable Professional Award for .NET (2008–2017). He has worked as a moderator and an article reviewer on online forums and currently works as a C# developer and writes articles for CodeGuru.com, Developer.com, DevX.com, and the Database journal.
His first book Visual Studio 2019 In Depth is currently on sale on Amazon and Bpb Publications.

You could reach him at: ojdupreez1978[at]gmail[dot]com

Related Articles

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date