用于读取ISRC的PInvoke DeviceIOControl不返回任何数据

时间:2012-08-22 17:04:04

标签: .net winapi pinvoke ioctl cd

我正在使用Idael Cardoso的示例Win32Functions Class与CD-Drive进行交互。我想在托盘中阅读一张CD的ISRC代码。

首先是一些代码 - 这是Win32Functions类 - 我添加了希望正确的代码来读取cd的Q_CHANNEL,这里找到的一些C ++结构(http://msdn.microsoft.com/en-us/library/windows/hardware/ff567601%28v=vs.85%29.aspx)并尝试将它们转换为C#。最后,我添加了一个DeviceIOControl调用来读取轨道的ISRC代码:

    namespace MyISRCReader
    {
  /// <summary>
  /// Wrapper class for Win32 functions and structures needed to handle CD.
  /// </summary>
  internal class Win32Functions
  {   
    public enum DriveTypes:uint 
    {
      DRIVE_UNKNOWN = 0,
      DRIVE_NO_ROOT_DIR,
      DRIVE_REMOVABLE,
      DRIVE_FIXED,
      DRIVE_REMOTE,
      DRIVE_CDROM,
      DRIVE_RAMDISK
    };

    [System.Runtime.InteropServices.DllImport("Kernel32.dll")]
    public extern static DriveTypes GetDriveType(string drive);

    //DesiredAccess values
    public const uint GENERIC_READ      = 0x80000000;
    public const uint GENERIC_WRITE     = 0x40000000;
    public const uint GENERIC_EXECUTE   = 0x20000000;
    public const uint GENERIC_ALL       = 0x10000000;

    //Share constants
    public const uint FILE_SHARE_READ   = 0x00000001;  
    public const uint FILE_SHARE_WRITE  = 0x00000002;  
    public const uint FILE_SHARE_DELETE = 0x00000004;  

    //CreationDisposition constants
    public const uint CREATE_NEW        = 1;
    public const uint CREATE_ALWAYS     = 2;
    public const uint OPEN_EXISTING     = 3;
    public const uint OPEN_ALWAYS       = 4;
    public const uint TRUNCATE_EXISTING = 5;

    /// <summary>
    /// Win32 CreateFile function, look for complete information at Platform SDK
    /// </summary>
    /// <param name="FileName">In order to read CD data FileName must be "\\.\\D:" where D is the CDROM drive letter</param>
    /// <param name="DesiredAccess">Must be GENERIC_READ for CDROMs others access flags are not important in this case</param>
    /// <param name="ShareMode">O means exlusive access, FILE_SHARE_READ allow open the CDROM</param>
    /// <param name="lpSecurityAttributes">See Platform SDK documentation for details. NULL pointer could be enough</param>
    /// <param name="CreationDisposition">Must be OPEN_EXISTING for CDROM drives</param>
    /// <param name="dwFlagsAndAttributes">0 in fine for this case</param>
    /// <param name="hTemplateFile">NULL handle in this case</param>
    /// <returns>INVALID_HANDLE_VALUE on error or the handle to file if success</returns>
    [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);

    /// <summary>
    /// The CloseHandle function closes an open object handle.
    /// </summary>
    /// <param name="hObject">Handle to an open object.</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)]
    public extern static int CloseHandle(IntPtr hObject);

    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_MEDIA_REMOVAL  = 0x002D4804;
    public const uint IOCTL_STORAGE_EJECT_MEDIA    = 0x002D4808;
    public const uint IOCTL_STORAGE_LOAD_MEDIA     = 0x002D480C;
    public const uint IOCTL_CDROM_READ_Q_CHANNEL   = 0x0002402C;  // found here: http://mu97bot.googlecode.com/svn-history/r2/trunk/Include/WinAPIEx.au3
                                                                  // and here:   http://www.ioctls.net/


    /// <summary>
    /// Most general form of DeviceIoControl Win32 function
    /// </summary>
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param>
    /// <param name="IoControlCode">Code of DeviceIoControl operation</param>
    /// <param name="lpInBuffer">Pointer to a buffer that contains the data required to perform the operation.</param>
    /// <param name="InBufferSize">Size of the buffer pointed to by lpInBuffer, in bytes.</param>
    /// <param name="lpOutBuffer">Pointer to a buffer that receives the operation's output data.</param>
    /// <param name="nOutBufferSize">Size of the buffer pointed to by lpOutBuffer, in bytes.</param>
    /// <param name="lpBytesReturned">Receives the size, in bytes, of the data stored into the buffer pointed to by lpOutBuffer. </param>
    /// <param name="lpOverlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns>
    [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);

    [ 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));
        }
      }
      public byte  TrackNumber;
      public byte  Reserved1;
      /// <summary>
      /// Don't use array to avoid array creation
      /// </summary>
      public byte  Address_0;
      public byte  Address_1;
      public byte  Address_2;
      public byte  Address_3;
    };

    public const int MAXIMUM_NUMBER_TRACKS = 100;

    [ StructLayout( LayoutKind.Sequential )]
    public class TrackDataList
    {
      [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;
        }
      }
      public TrackDataList()
      {
        Data = new byte[MAXIMUM_NUMBER_TRACKS*Marshal.SizeOf(typeof(TRACK_DATA))];
      }
    }

    //Struktur für den Input Parameter:
    public const uint IOCTL_CDROM_TRACK_ISRC = 0x3;

    [StructLayout(LayoutKind.Sequential)]
    public class _CDROM_SUB_Q_DATA_FORMAT
    {
        public byte Format;
        public byte Track;
    }
    //Strukturen für den Output Parameter
    [StructLayout(LayoutKind.Explicit)]
    public class _SUB_Q_CHANNEL_DATA
    {
        [FieldOffset(0)]
        public _SUB_Q_CURRENT_POSITION     CurrentPosition;
        [FieldOffset(0)]
        public _SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
        [FieldOffset(0)]
        public _SUB_Q_TRACK_ISRC TrackIsrc;
    }


    [StructLayout(LayoutKind.Sequential)]
    public class _SUB_Q_CURRENT_POSITION
    {
        public _SUB_Q_HEADER Header;
        public byte          FormatCode;
        //private byte        BitMapped;
        public 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));
        //    }
        //}
        public byte          TrackNumber;
        public byte          IndexNumber;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
        public byte[]        AbsoluteAddress;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
        public byte[]        TrackRelativeAddress;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class _SUB_Q_MEDIA_CATALOG_NUMBER
    {
        _SUB_Q_HEADER Header;
        byte        FormatCode;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
        public byte[]        Reserved;
        //private byte BitMapped;
        public byte BitMapped;
        //public byte Reserved1
        //{
        //    get
        //    {
        //        return (byte)(BitMapped & 0xFE);
        //    }
        //    set
        //    {
        //        BitMapped = (byte)((BitMapped & 0x01) | (value & (byte)0xFE));
        //    }
        //}
        //public byte Mcval
        //{
        //    get
        //    {
        //        return (byte)((BitMapped & (byte)0x01) >> 7);
        //    }
        //    set
        //    {
        //        BitMapped = (byte)((BitMapped & (byte)0xFE) | (value << 7));
        //    }
        //}
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=15)]
        public byte[]  MediaCatalog;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class _SUB_Q_TRACK_ISRC
    {

        public _SUB_Q_HEADER Header;
        public byte FormatCode;
        public byte Reserved0;
        public byte Track;
        public byte Reserved1;
        //private byte BitMapped;
        public byte BitMapped;
        //public byte Reserved2
        //{
        //    get
        //    {
        //        return (byte)(BitMapped & 0xFE);
        //    }
        //    set
        //    {
        //        BitMapped = (byte)((BitMapped & 0x01) | (value & (byte)0xFE));
        //    }
        //}
        //public byte Tcval
        //{
        //    get
        //    {
        //        return (byte)((BitMapped & (byte)0x01) >> 7);
        //    }
        //    set
        //    {
        //        BitMapped = (byte)((BitMapped & (byte)0xFE) | (value << 7));
        //    }
        //}
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=15)]
        public byte[]       TrackIsrc;

    }

    [StructLayout(LayoutKind.Sequential)]
    public class _SUB_Q_HEADER
    {
        public byte Reserved;
        public byte AudioStatus;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
        public byte[] DataLength;
    }



    [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;
    }



    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;
    }

    /// <summary>
    /// Overload version of DeviceIOControl to read the TOC (Table of contents)
    /// </summary>
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param>
    /// <param name="IoControlCode">Must be IOCTL_CDROM_READ_TOC for this overload version</param>
    /// <param name="InBuffer">Must be <code>IntPtr.Zero</code> for this overload version </param>
    /// <param name="InBufferSize">Must be 0 for this overload version</param>
    /// <param name="OutTOC">TOC object that receive the CDROM TOC</param>
    /// <param name="OutBufferSize">Must be <code>(UInt32)Marshal.SizeOf(CDROM_TOC)</code> for this overload version</param>
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutTOC</param>
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns>
    [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);

    /// <summary>
    /// Overload version of DeviceIOControl to lock/unlock the CD
    /// </summary>
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"/></param>
    /// <param name="IoControlCode">Must be IOCTL_STORAGE_MEDIA_REMOVAL for this overload version</param>
    /// <param name="InMediaRemoval">Set the lock/unlock state</param>
    /// <param name="InBufferSize">Must be <code>(UInt32)Marshal.SizeOf(PREVENT_MEDIA_REMOVAL)</code> for this overload version</param>
    /// <param name="OutBuffer">Must be <code>IntPtr.Zero</code> for this overload version </param>
    /// <param name="OutBufferSize">Must be 0 for this overload version</param>
    /// <param name="BytesReturned">A "dummy" varible in this case</param>
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns>
    [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);

    /// <summary>
    /// Overload version of DeviceIOControl to read digital data
    /// </summary>
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="Ripper.Win32Functions.CreateFile"</param>
    /// <param name="IoControlCode">Must be IOCTL_CDROM_RAW_READ for this overload version</param>
    /// <param name="rri">RAW_READ_INFO structure</param>
    /// <param name="InBufferSize">Size of RAW_READ_INFO structure</param>
    /// <param name="OutBuffer">Buffer that will receive the data to be read</param>
    /// <param name="OutBufferSize">Size of the buffer</param>
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutBuffer</param>
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns>
    [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);

    /// <summary>
    /// Overload Version of DeviceIOControl to read ISRC Code from CD for special track
    /// </summary>
    /// <param name="hDevice">Handle of device opened with CreateFile, <see cref="MyISRCReader.Win32Functions.CreateFile"</param>
    /// <param name="IoControlCode">Must be IOCTL_CDROM_READ_Q_CHANNEL</param>
    /// <param name="fmt">_CDROM_SUB_Q_DATA_FORMAT structure </param>
    /// <param name="InBufferSize">Size of _CDROM_SUB_Q_DATA_FORMAT</param>
    /// <param name="OutData">_SUB_Q_CHANNEL_DATA structure</param>
    /// <param name="OutBufferSize">Size of _SUB_Q_CHANNEL_DATA buffer</param>
    /// <param name="BytesReturned">Receives the size, in bytes, of the data stored into OutBuffer</param>
    /// <param name="Overlapped">Pointer to an OVERLAPPED structure. Discarded for this case</param>
    /// <returns>0 if the function fails and nonzero if the function succeeded</returns>
    [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError = true)]
    public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode,
      [In] _CDROM_SUB_Q_DATA_FORMAT fmt, uint InBufferSize,
      [In, Out] _SUB_Q_CHANNEL_DATA OutData, uint OutBufferSize,
      ref uint BytesReturned,
      IntPtr Overlapped);
  }
}

这是我的简单测试程序 - 我打开CD的句柄并获取我的CD的正确轨道数。我的ISRC-Read后的错误代码是1,表示“错误的功能”。输出结构数据都是值0 ...

namespace MyISRCReader
{    
    class Program
    {
        static void Main(string[] args)
        {
            IntPtr cdHandle = IntPtr.Zero;
            char Drive = 'D';
            Win32Functions.CDROM_TOC Toc = new Win32Functions.CDROM_TOC();
            bool TocValid;

            if (Win32Functions.GetDriveType(Drive + ":\\") == Win32Functions.DriveTypes.DRIVE_CDROM)
            {
                cdHandle = Win32Functions.CreateFile("\\\\.\\" + Drive + ':', Win32Functions.GENERIC_READ, Win32Functions.FILE_SHARE_READ, IntPtr.Zero, Win32Functions.OPEN_EXISTING, 0, IntPtr.Zero);
                if (((int)cdHandle != -1) && ((int)cdHandle != 0))
                {
                    uint Dummy = 0;
                    Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_STORAGE_LOAD_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero);
                    uint BytesRead = 0;
                    TocValid = Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_CDROM_READ_TOC, IntPtr.Zero, 0, Toc, (uint)Marshal.SizeOf(Toc), ref BytesRead, IntPtr.Zero) != 0;
                    if (TocValid)
                    {
                        Console.WriteLine("track count: " + (Toc.LastTrack - Toc.FirstTrack + 1));

                        Win32Functions._CDROM_SUB_Q_DATA_FORMAT format = new Win32Functions._CDROM_SUB_Q_DATA_FORMAT();
                        Win32Functions._SUB_Q_CHANNEL_DATA data = new Win32Functions._SUB_Q_CHANNEL_DATA();

                        format.Format = (byte)Win32Functions.IOCTL_CDROM_TRACK_ISRC;
                        format.Track = (byte)2;

                        Console.WriteLine("Last Win32 Error beforde ISRC read: " + Marshal.GetLastWin32Error().ToString());

                        int erg = Win32Functions.DeviceIoControl(cdHandle,
                                                       Win32Functions.IOCTL_CDROM_TRACK_ISRC,
                                                       format,
                                                       (uint)Marshal.SizeOf(format),
                                                       data,
                                                       (uint)Marshal.SizeOf(data),
                                                       ref Dummy,
                                                       IntPtr.Zero);

                        Console.WriteLine("Last Win32 error: " + Marshal.GetLastWin32Error().ToString());
                        if (erg == 0)
                        {
                            //Reading ISRC failed
                            Console.WriteLine("Could not read ISRC!");
                        }
                        else
                        {
                            //Reading ISRC succeeded
                            Console.WriteLine("Tracknumber: " + data.TrackIsrc.Track + " - ISRC: " + data.TrackIsrc.TrackIsrc[data.TrackIsrc.Track].ToString());
                        }
                    }
                    else
                    {
                        Console.WriteLine("Toc invalid");
                    }

                }
                else
                {
                    Console.WriteLine("Handle Error");
                }
            }
            Console.Write("Eject media? (y/n)");
            var ch  = Console.ReadKey();
            if (ch.KeyChar == 'y' || ch.KeyChar=='Y')
            {
                uint Dummy = 0;
                Win32Functions.DeviceIoControl(cdHandle, Win32Functions.IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero);
            }
            Console.ReadKey();
        }
    }
}

PInvoke调用和结构(类)是否正确? Win32Functions中的类的注释部分(模拟C ++结构)是模拟C ++位域的另一种选择。我不知道如何调试这个或在哪里可以找到错误......有人有想法吗?

1 个答案:

答案 0 :(得分:0)

当你打电话给以下一行时,有一种可能性:

int erg = Win32Functions.DeviceIoControl(cdHandle, 
                                         Win32Functions.IOCTL_CDROM_TRACK_ISRC,
                                         format,
                                         (uint)Marshal.SizeOf(format),
                                         data,
                                         (uint)Marshal.SizeOf(data),
                                         ref Dummy,
                                         IntPtr.Zero);

你根据dllimport语句直接传递格式和数据对象,它们期待IntPtrs。

[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);

现在我可能已经预料到某种类型的访问冲突异常,而不是你得到的但是它是一个可以开始的地方。

另外,您是否确定当您获得格式和数据的大小时,它会返回正确的大小?我看到你将其中一个定义为

[StructLayout(LayoutKind.Sequential)]
public class _CDROM_SUB_Q_DATA_FORMAT
{
    public byte Format;
    public byte Track;
}

现在你希望大小为2个字节,但它实际上可能是8个,因为默认情况下它会打包大小,以便每个字段从4字节边界开始。 您可以将Pack = 1添加到属性以确保它使用最小字节数。但是你应该仔细检查大小,它们对应于C ++代码中定义的内容。