简体   繁体   English

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

[英]Calling DeviceIoControl from C# with IOCTL_DVD_* Control Codes

I am trying to call DeviceIoControl from C# for IOCTL_DVD_* control codes. 我正在尝试从C#调用DeviceIoControl获得IOCTL_DVD_*控制代码。 Having read a lot of information and trying a number of examples I have not made much progress. 阅读了大量信息并尝试了许多示例之后,我并没有取得太大的进步。

What I am trying to eventually do is get a DVD_LAYER_DESCRIPTOR structure about the media currently in the DVD drive. 我最终想要做的是获取有关DVD驱动器中当前媒体的DVD_LAYER_DESCRIPTOR结构。 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, ie function fails or returns success but gives a blank output structure.) 我可以在DVD设备上成功调用CreateFile ,但是当我尝试使用控制代码IOCTL_DVD_START_SESSION调用DeviceIoControl ,它会返回成功代码,但我似乎未成功地获取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并没有问题地关闭会话,因此我知道这是DVD_LAYER_DESCRIPTOR

The C# issues appear to relate to how the external function is defined and the parameters marshalled. C#问题似乎与如何定义外部函数和编组参数有关。 And how the various structures that are passed and returned are defined. 以及如何定义传递和返回的各种结构。

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. 我查看了www.pinvoke.net上它们的定义方式,并使用了给出的一些示例代码和定义,但仍然遇到与上述相同的问题。 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. 问题的一部分似乎是每个IOCTL控制代码的参数都不同,主要是结构,但IOCTL_DVD_START_SESSION的输出值是32位整数。 How can an extern method in C# be defined to handle these different cases? 如何在C#中定义一个extern方法来处理这些不同的情况? 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??? 同样,用正确大小的成员类型定义的各种结构,显示它们在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版本返回的sessionid为3,并且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? 是否有人对从C#调用DeviceIoControl来访问DVD信息有任何想法或可行的示例代码? (Showing how the structures and function should be defined and used.) Any links to useful websites or other advice would be much appreciated. (显示如何定义和使用结构和功能。)任何指向有用网站的链接或其他建议将不胜感激。

(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

87 - ERROR_INVALID_PARAMETER

The second version seems to succeed but the value of SessionId is -10, the initialised value. 第二个版本似乎成功了,但是SessionId的值为-10(初始化值)。 (From MSDN this value should be between -1 and 3?) The end session also succeeds. (从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代码中似乎也是一个问题,因为它的错误处理是要重试。 ] ]

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). 驱动程序希望指针指向lpOutBuffer中的缓冲区,但是您可以提供sessionId本身(为零)。 Of course this will not work. 当然这是行不通的。

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. 顺便说一句,这同样适用于所有随后的DeviceIoControl调用,当需要提供指向值的指针时,再次提供值。 And you also need to check if your CTL_CODE function builds valid io code. 而且您还需要检查您的CTL_CODE函数是否构建有效的io代码。

Again, DeviceIoControl expects pointers to buffers for in and out structures. 同样,DeviceIoControl期望指向用于in和out结构的缓冲区的指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM