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

问题描述:

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

我试图最终做的是得到DVD_LAYER_DESCRIPTOR结构关于目前在DVD驱动器中的媒体。我可以在DVD设备上成功呼叫CreateFile,但是当我尝试拨打DeviceIoControl并使用控制代码IOCTL_DVD_START_SESSION时,它会返回成功码,但我似乎无法成功返回sessionId值,始终返回0.(并尝试使用I然后尝试获取层描述与IOCTL_DVD_READ_STRUCTURE失败,即功能失败或返回成功,但给出了一个空白的输出结构。)

找到一些C代码,使类似的调用后,我能够编译此代码(使用Visual C++ 2008速成版),并且它能够成功启动一个会话,阅读DVD_LAYER_DESCRIPTOR,并关闭会话而没有问题,所以我知道这是有效的。

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

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

如果我使用像DeviceIOView程序,观看由C代码和C#代码IOCTL_DVD_START_SESSION方面取得的通话C版本返回3会话ID和DeviceIOView展示了运行在C#代码时发送的数据后面也3所以似乎有某种编组返回参数的编组问题,因为我们在C#代码中只看到0

有没有人有任何想法或工作示例代码如何从C#调用DeviceIoControl访问DVD信息? (显示如何定义和使用结构和功能。)任何有用的网站或其他建议的链接将不胜感激。

ň约翰

示例代码(新增)

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

要运行该代码(在Visual C#2008 Express版本,.NET 3.5。正在开发的),你需要指定在命令行上驱动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代码中遇到的一个问题,因为它的错误处理是再次重试。 ]

+0

发布您的P/Invoke代码? – Michael 2009-07-01 04:07:19

的问题就在这里:

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需要指向缓冲区的指针指向输入和输出结构。