从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代码中遇到的一个问题,因为它的错误处理是再次重试。 ]
的问题就在这里:
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需要指向缓冲区的指针指向输入和输出结构。
发布您的P/Invoke代码? – Michael 2009-07-01 04:07:19