我正在使用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 ++位域的另一种选择。我不知道如何调试这个或在哪里可以找到错误......有人有想法吗?
答案 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 ++代码中定义的内容。