使用IOCTL_DVD_ *控制代码从C#调用DeviceIoControl

我试图从C#调用DeviceIoControl用于IOCTL_DVD_*控制代码。 阅读了大量信息并尝试了一些例子后,我没有取得多大进展。

我最终要做的是获取DVD_LAYER_DESCRIPTOR结构,了解当前DVD驱动器中的媒体。 我可以在DVD设备上成功调用CreateFile ,但是当我尝试使用控制代码IOCTL_DVD_START_SESSION调用DeviceIoControl ,它返回成功代码,但我似乎没有成功恢复sessionId值,总是返回0.(并且任何尝试我然后尝试使用IOCTL_DVD_READ_STRUCTURE获取图层描述失败,即函数失败或返回成功但是给出一个空白的输出结构。)

在找到一些进行类似调用的C代码后,我能够编译这段代码(使用Visual C ++ 2008 Express Edition),并且它成功地能够启动会话,读取DVD_LAYER_DESCRIPTOR ,并且没有问题地关闭会话,所以我知道这有效。

C#问题似乎与如何定义外部函数和编组参数有关。 以及如何定义传递和返回的各种结构。

我已经查看了www.pinvoke.net他们如何定义它,并使用了一些给出的示例代码和定义,但仍然具有上述相同的问题。 部分问题似乎是每个IOCTL控制代码的参数都不同,主要是结构,但对于IOCTL_DVD_START_SESSION ,输出值是32位整数。 如何定义C#中的extern方法来处理这些不同的情况? 另外,使用正确大小的成员类型定义的各种结构显示它们在C和C#代码之间的大小不同,但各个成员的大小相同???

如果我使用像DeviceIOView这样的程序并观察IOCTL_DVD_START_SESSION的C代码和C#代码IOCTL_DVD_START_SESSION则C版本返回一个3的sessionid,并且当运行C#代码时,DeviceIOView显示发回的数据也是3所以似乎是返回参数的某种编组问题,因为我们在C#代码中只看到0

有没有人有任何想法或工作示例代码如何从C#调用DeviceIoControl来访问DVD信息? (显示应如何定义和使用结构和function。)非常感谢任何指向有用网站或其他建议的链接。

(在Visual C#2008 Express Edition,.NET 3.5中开发。)

N Johns

示例代码(已添加)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using System.IO; using System.Threading; namespace Example { class Program { static void Main(string[] args) { string driveLetter = args[0].Substring(0, 1).ToUpper() + ":"; SafeFileHandle _hdev = CreateFileR(driveLetter); if (_hdev.IsClosed | _hdev.IsInvalid) { Console.WriteLine("Error opening device"); return; } Console.WriteLine("DeviceIoControl - Version One"); Console.WriteLine("IOCTL_DVD_START_SESSION"); bool result = false; int bytesReturned = 0; int sessionId = 0; result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0400, 0, 1), IntPtr.Zero, 0, (IntPtr)sessionId, Marshal.SizeOf(sessionId), out bytesReturned, IntPtr.Zero); if (result == false) { int error_code = Marshal.GetLastWin32Error(); Console.WriteLine("Result: " + result); Console.WriteLine("error code: " + error_code); } else { Console.WriteLine("Result: " + result); Console.WriteLine("BytesReturned: " + bytesReturned); Console.WriteLine("SessionId: " + sessionId); Console.WriteLine("sizeof(SessionId): " + Marshal.SizeOf(sessionId)); } Console.WriteLine("IOCTL_DVD_READ_STRUCTURE"); Console.WriteLine("Skipping..."); Console.WriteLine("IOCTL_DVD_END_SESSION"); bytesReturned = 0; result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0403, 0, 1), new IntPtr(sessionId), Marshal.SizeOf(sessionId), IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (result == false) { int error_code = Marshal.GetLastWin32Error(); Console.WriteLine("error code: " + error_code); Console.WriteLine("Result: " + result); } else { Console.WriteLine("Result: " + result); Console.WriteLine("BytesReturned: " + bytesReturned); } Console.WriteLine("\nDeviceIoControl - Version Two"); Console.WriteLine("IOCTL_DVD_START_SESSION"); result = false; uint bytesReturned2 = 0; sessionId = -10; NativeOverlapped nativeOverlapped = new NativeOverlapped(); result = DeviceIoControlAlt(_hdev, EIOControlCode.DvdStartSession, 0, 0, sessionId, (uint)Marshal.SizeOf(sessionId), ref bytesReturned2, ref nativeOverlapped); if (result == false) { int error_code = Marshal.GetLastWin32Error(); Console.WriteLine("Result: " + result); Console.WriteLine("error code: " + error_code); } else { Console.WriteLine("Result: " + result); Console.WriteLine("BytesReturned: " + bytesReturned2); Console.WriteLine("SessionId: " + sessionId); Console.WriteLine("sizeof(SessionId): " + Marshal.SizeOf(sessionId)); } Console.WriteLine("IOCTL_DVD_READ_STRUCTURE"); Console.WriteLine("Skipping..."); Console.WriteLine("IOCTL_DVD_END_SESSION"); bytesReturned2 = 0; result = DeviceIoControlAlt(_hdev, EIOControlCode.DvdEndSession, sessionId, (uint)Marshal.SizeOf(sessionId), 0, 0, ref bytesReturned2, ref nativeOverlapped); if (result == false) { int error_code = Marshal.GetLastWin32Error(); Console.WriteLine("Result: " + result); Console.WriteLine("error code: " + error_code); } else { Console.WriteLine("Result: " + result); Console.WriteLine("BytesReturned: " + bytesReturned2); } _hdev.Close(); } public static int CTL_CODE(int DeviceType, int Function, int Method, int Access) { return (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)); } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); public static SafeFileHandle CreateFileR(string device) { string str = device.EndsWith(@"\") ? device.Substring(0, device.Length - 1) : device; return new SafeFileHandle(CreateFile(@"\\.\" + str, WinntConst.GENERIC_READ, WinntConst.FILE_SHARE_READ, IntPtr.Zero, WinntConst.OPEN_EXISTING, WinntConst.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true); } [return: MarshalAs(UnmanagedType.Bool)] [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool DeviceIoControl([In] SafeFileHandle hDevice, [In] int dwIoControlCode, [In] IntPtr lpInBuffer, [In] int nInBufferSize, [Out] IntPtr lpOutBuffer, [In] int nOutBufferSize, out int lpBytesReturned, [In] IntPtr lpOverlapped); internal class WinntConst { // Fields internal static uint FILE_ATTRIBUTE_NORMAL = 0x80; internal static uint FILE_SHARE_READ = 1; internal static uint GENERIC_READ = 0x80000000; internal static uint OPEN_EXISTING = 3; } // Other code for DeviceIoControl from pinvoke.net [Flags] public enum EIOControlCode : uint { // DVD DvdReadStructure = (EFileDevice.Dvd << 16) | (0x0450 << 2) | EMethod.Buffered | (FileAccess.Read << 14), DvdStartSession = (EFileDevice.Dvd << 16) | (0x0400 << 2) | EMethod.Buffered | (FileAccess.Read << 14), DvdEndSession = (EFileDevice.Dvd << 16) | (0x0403 << 2) | EMethod.Buffered | (FileAccess.Read << 14) }; [Flags] public enum EFileDevice : uint { Dvd = 0x00000033, } [Flags] public enum EMethod : uint { Buffered = 0, InDirect = 1, OutDirect = 2, Neither = 3 } [DllImport("Kernel32.dll", EntryPoint="DeviceIoControl", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool DeviceIoControlAlt( Microsoft.Win32.SafeHandles.SafeFileHandle hDevice, EIOControlCode IoControlCode, [MarshalAs(UnmanagedType.AsAny)][In] object InBuffer, uint nInBufferSize, [MarshalAs(UnmanagedType.AsAny)][Out] object OutBuffer, uint nOutBufferSize, ref uint pBytesReturned, [In] ref System.Threading.NativeOverlapped Overlapped ); } } 

要运行此代码,您需要在命令行上指定DVD驱动器的驱动器号。

产量

 DeviceIoControl - Version One IOCTL_DVD_START_SESSION Result: False error code: 122 IOCTL_DVD_READ_STRUCTURE Skipping... IOCTL_DVD_END_SESSION error code: 87 Result: False DeviceIoControl - Version Two IOCTL_DVD_START_SESSION Result: True BytesReturned: 4 SessionId: -10 sizeof(SessionId): 4 IOCTL_DVD_READ_STRUCTURE Skipping... IOCTL_DVD_END_SESSION Result: True BytesReturned: 0 

使用给定错误代码的两个调用都会失败第一个版本:

122 - ERROR_INSUFFICIENT_BUFFER

87 - ERROR_INVALID_PARAMETER

第二个版本似乎成功,但SessionId的值是-10,即初始值。 (从MSDN开始,此值应介于-1和3之间?)结束会话也会成功。

[注意:第二个版本的启动会话似乎只对其他所有调用都成功,不知道为什么,但这似乎也是我所拥有的C代码中的一个问题,因为它的error handling是再次重试。 ]

问题在于:

 result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0400, 0, 1), IntPtr.Zero, 0, (IntPtr)sessionId, Marshal.SizeOf(sessionId), out bytesReturned, IntPtr.Zero); 

驱动程序需要指向lpOutBuffer中缓冲区的指针,但您提供的是sessionId本身(为零)。 当然这不起作用。

在这里你需要做什么:

 IntPtr buffer = Marshal.AllocHGlobal(sizeof(int)); result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0400, 0, 1), IntPtr.Zero, 0, buffer, sizeof(int), out bytesReturned, IntPtr.Zero); int sessionId = Marshal.ReadInt32(buffer); Marshal.FreeHGlobal(buffer); 

BTW,同样适用于所有后续的DeviceIoControl调用,当您需要提供指向值的指针时,您再次提供值。 您还需要检查CTL_CODE函数是否构建有效的io代码。

同样,DeviceIoControl需要指向入口和出口结构的缓冲区指针。