Llamando a DeviceIoControl desde C # con IOCTL_DVD_ * Códigos de control

Estoy intentando llamar a DeviceIoControl desde C # paraIOCTL_DVD_* códigos de control. Después de leer mucha información y probar varios ejemplos, no he avanzado mucho.

Lo que estoy tratando de hacer es conseguir unDVD_LAYER_DESCRIPTOR Estructura sobre los medios actualmente en la unidad de DVD. puedo llamarCreateFile correctamente en el dispositivo de DVD, pero cuando intento llamarDeviceIoControl con el código de controlIOCTL_DVD_START_SESSION devuelve un código de éxito, pero parece que no recupero el valor de sessionId con éxito, siempre devuelve 0. (Y cualquier intento que hice para intentar obtener la descripción de la capa conIOCTL_DVD_READ_STRUCTURE falla, es decir, la función falla o devuelve el éxito pero da una estructura de salida en blanco.)

Después de encontrar un código C que hace llamadas similares, pude compilar este código (usando Visual C ++ 2008 Express Edition) y puedo iniciar una sesión con éxito, lea elDVD_LAYER_DESCRIPTOR, y cierre la sesión sin problema para que sepa que esto funciona.

Los problemas de C # parecen estar relacionados con la forma en que se define la función externa y se ordenan los parámetros. Y cómo se definen las diversas estructuras que se pasan y devuelven.

He consultado www.pinvoke.net para ver cómo lo definen, y he usado parte del código de ejemplo y las definiciones proporcionadas, pero aún tienen los mismos problemas que se describen anteriormente. Parte del problema parece ser que, para cada código de control IOCTL, los parámetros son diferentes, en su mayoría estructuras, pero paraIOCTL_DVD_START_SESSION El valor de salida es un entero de 32 bits. ¿Cómo se puede definir un método externo en C # para manejar estos diferentes casos? Además, varias estructuras, definidas con los tipos de miembros del tamaño correcto, muestran que tienen diferentes tamaños entre los códigos C y C #, pero los miembros individuales tienen los mismos tamaños.

Si utilizo un programa comoDeviceIOView y ver las llamadas realizadas por el código C y el código C # paraIOCTL_DVD_START_SESSION la versión C devuelve un sessionid de 3 y DeviceIOView muestra que los datos que se devuelven cuando se ejecuta el código C # también es 3, por lo que parece haber algún tipo de problema de Marshalling de los parámetros devueltos, ya que solo vemos 0 en el código C #

¿Alguien tiene alguna idea o código de ejemplo de trabajo sobre cómo llamar a DeviceIoControl desde C # para acceder a la información del DVD? (Mostrar cómo se deben definir y utilizar las estructuras y funciones.) Cualquier vínculo a sitios web útiles u otros consejos serían muy apreciados.

(Se está desarrollando en Visual C # 2008 Express Edition, .NET 3.5.)

N Johns

Código de ejemplo (agregado)

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

Para ejecutar este código, debe especificar la letra de la unidad de DVD en la línea de comandos.

Salida

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 

La primera versión falla en ambas llamadas con los códigos de error dados:

122 - ERROR_INSUFFICIENT_BUFFER

87 - ERROR_INVALID_PARAMETER

La segunda versión parece tener éxito, pero el valor de SessionId es -10, el valor inicializado. (¿Desde MSDN este valor debería estar entre -1 y 3?) La sesión final también se realiza correctamente.

[Nota: la segunda sesión de inicio de la versión solo parece tener éxito en cualquier otra invocación, no estoy seguro de por qué, pero esto también parece ser un problema en el código C que tengo, ya que el manejo de errores es volver a intentarlo de nuevo. ]

Respuestas a la pregunta(1)

Su respuesta a la pregunta