使用 IOCTL_DVD_* 控制代码从 C# 调用 DeviceIoControl
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1067216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calling DeviceIoControl from C# with IOCTL_DVD_* Control Codes
提问by Nathan Johns
I am trying to call DeviceIoControl from C# for IOCTL_DVD_*
control codes. Having read a lot of information and trying a number of examples I have not made much progress.
我正在尝试从 C# 调用 DeviceIoControl 以获取IOCTL_DVD_*
控制代码。阅读了大量信息并尝试了许多示例后,我并没有取得太大进展。
What I am trying to eventually do is get a DVD_LAYER_DESCRIPTOR
structure about the media currently in the DVD drive. I can call CreateFile
successfully on the DVD device, but when I try to call DeviceIoControl
with the control code IOCTL_DVD_START_SESSION
it returns a success code but I don't seem to get the sessionId value back successfully, always returns 0. (And any attempt I have then made to try getting the layer description with IOCTL_DVD_READ_STRUCTURE
fails, i.e. function fails or returns success but gives a blank output structure.)
我最终想要做的是获得DVD_LAYER_DESCRIPTOR
有关 DVD 驱动器中当前媒体的结构。我可以CreateFile
在 DVD 设备上成功调用,但是当我尝试DeviceIoControl
使用控制代码进行调用时,IOCTL_DVD_START_SESSION
它返回一个成功代码,但我似乎没有成功取回 sessionId 值,总是返回 0。(以及我当时所做的任何尝试尝试获取IOCTL_DVD_READ_STRUCTURE
失败的层描述,即函数失败或返回成功但给出一个空白的输出结构。)
After finding some C code which makes similar calls I was able to compile this code (using Visual C++ 2008 Express Edition) and it successfully is able to start a session, read the DVD_LAYER_DESCRIPTOR
, and close the session without problem so I know this works.
在找到一些进行类似调用的 C 代码后,我能够编译此代码(使用 Visual C++ 2008 Express Edition)并且它能够成功地启动会话、读取DVD_LAYER_DESCRIPTOR
并关闭会话而没有问题,所以我知道这是有效的。
The C# issues appear to relate to how the external function is defined and the parameters marshalled. And how the various structures that are passed and returned are defined.
C# 问题似乎与如何定义外部函数和编组参数有关。以及如何定义传递和返回的各种结构。
I have looked at www.pinvoke.net for how they define it, and have used some of the example code and definitions given but still have the same issues as outlined above. Part of the problem seems to be that for each IOCTL control code the parameters are different, mostly structs, but for IOCTL_DVD_START_SESSION
the output value is a 32 bit integer. How can an extern method in C# be defined to handle these different cases? Also various structs, defined with the right sized member types, show they are different sizes between the C and C# code, but the individual members are the same sizes???
我查看了 www.pinvoke.net 以了解他们如何定义它,并使用了一些给出的示例代码和定义,但仍然存在与上述相同的问题。部分问题似乎是每个 IOCTL 控制代码的参数不同,主要是结构,但IOCTL_DVD_START_SESSION
输出值是一个 32 位整数。如何定义 C# 中的 extern 方法来处理这些不同的情况?此外,使用大小合适的成员类型定义的各种结构显示它们在 C 和 C# 代码之间的大小不同,但各个成员的大小相同???
If I use a program like DeviceIOView
and watch the calls made by both the C code and C# code for IOCTL_DVD_START_SESSION
the C version returns a sessionid of 3 and DeviceIOView shows the data being sent back when running the C# code is also 3 so there seems to be some sort of Marshalling issue of the returned parameters as we only see 0 in the C# code
如果我使用类似的程序DeviceIOView
并观察 C 代码和 C# 代码对IOCTL_DVD_START_SESSION
C 版本的调用返回 3 的 sessionid 并且 DeviceIOView 显示运行 C# 代码时发回的数据也是 3 所以似乎有一些返回参数的编组问题,因为我们在 C# 代码中只看到 0
Does anyone have any ideas or working example code on how to call DeviceIoControl from C# to access DVD information? (Showing how the structures and function should be defined and used.) Any links to useful websites or other advice would be much appreciated.
有没有人对如何从 C# 调用 DeviceIoControl 来访问 DVD 信息有任何想法或工作示例代码?(展示如何定义和使用结构和功能。)任何有用网站或其他建议的链接将不胜感激。
(Being developed in Visual C# 2008 Express Edition, .NET 3.5.)
(在 Visual C# 2008 Express Edition、.NET 3.5 中开发。)
N Johns
约翰斯
Example Code (Added)
示例代码(已添加)
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
);
}
}
To run this code you need to specify the drive letter of a DVD drive on the command line.
要运行此代码,您需要在命令行上指定 DVD 驱动器的驱动器号。
Output
输出
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
The first version fails on both calls with the given error codes:
第一个版本在两次调用中都失败并给出错误代码:
122 - ERROR_INSUFFICIENT_BUFFER
122 - ERROR_INSUFFICIENT_BUFFER
87 - ERROR_INVALID_PARAMETER
87 - ERROR_INVALID_PARAMETER
The second version seems to succeed but the value of SessionId is -10, the initialised value. (From MSDN this value should be between -1 and 3?) The end session also succeeds.
第二个版本似乎成功了,但 SessionId 的值为 -10,即初始化值。(来自 MSDN,这个值应该在 -1 和 3 之间?)结束会话也成功。
[ Note: the second version start session only seems to succeed on every other invocation, not sure why but this also appears to be an issue in the C code I have as it's error handling is to retry again. ]
[注意:第二个版本的启动会话似乎只在所有其他调用上成功,不知道为什么,但这似乎也是我的 C 代码中的一个问题,因为它的错误处理是再次重试。]
回答by arbiter
The problem lies here:
问题出在这里:
result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0400, 0, 1),
IntPtr.Zero, 0, (IntPtr)sessionId, Marshal.SizeOf(sessionId),
out bytesReturned, IntPtr.Zero);
Driver expects pointer to buffer in lpOutBuffer, but you instead provide sessionId itself (which is zero). Of course this will not work.
驱动程序需要指向 lpOutBuffer 中的缓冲区的指针,但您提供 sessionId 本身(为零)。当然这行不通。
Here what you need to do:
在这里你需要做的是:
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, the same applies to all following DeviceIoControl calls, you again provide value, when you need provide pointer to value. And you also need to check if your CTL_CODE function builds valid io code.
顺便说一句,这同样适用于所有随后的 DeviceIoControl 调用,当您需要提供指向值的指针时,您再次提供值。并且您还需要检查您的 CTL_CODE 函数是否构建了有效的 io 代码。
Again, DeviceIoControl expects pointers to buffers for in and out structures.
同样,DeviceIoControl 需要指向输入和输出结构的缓冲区的指针。