Aufruf von DeviceIoControl aus C # mit IOCTL_DVD_ * Steuercodes

Ich versuche, DeviceIoControl von C # aus aufzurufenIOCTL_DVD_* Steuercodes. Nachdem ich viele Informationen gelesen und einige Beispiele ausprobiert habe, habe ich nicht viel Fortschritte gemacht.

Was ich irgendwann versuche, ist eine zu bekommenDVD_LAYER_DESCRIPTOR Struktur über die aktuell im DVD-Laufwerk befindlichen Medien. Ich kann anrufenCreateFile erfolgreich auf dem DVD-Gerät, aber wenn ich versuche anzurufenDeviceIoControl mit dem SteuercodeIOCTL_DVD_START_SESSION es gibt einen Erfolgscode zurück, aber ich scheine den sessionId-Wert nicht erfolgreich zurückzubekommen, sondern gebe immer 0 zurück. (Und jeder Versuch, den ich unternommen habe, um die Layerbeschreibung mit zu bekommenIOCTL_DVD_READ_STRUCTURE fehlschlägt, d. h. die Funktion schlägt fehl oder gibt Erfolg zurück, gibt aber eine leere Ausgabestruktur aus.)

Nachdem ich einen C-Code gefunden hatte, der ähnliche Aufrufe ausführt, konnte ich diesen Code kompilieren (mit Visual C ++ 2008 Express Edition) und eine Sitzung erfolgreich startenDVD_LAYER_DESCRIPTOR, und schließen Sie die Sitzung ohne Probleme, damit ich weiß, dass dies funktioniert.

Die C # -Probleme scheinen sich darauf zu beziehen, wie die externe Funktion definiert und die Parameter gemarshallt werden. Und wie die verschiedenen Strukturen definiert werden, die übergeben und zurückgegeben werden.

Ich habe auf www.pinvoke.net nachgeschaut, wie sie es definieren, und habe einige der angegebenen Beispielcodes und Definitionen verwendet, habe aber immer noch die gleichen Probleme wie oben beschrieben. Ein Teil des Problems scheint zu sein, dass für jeden IOCTL-Steuercode die Parameter unterschiedlich sind, meist Strukturen, aber fürIOCTL_DVD_START_SESSION Der Ausgabewert ist eine 32-Bit-Ganzzahl. Wie kann eine externe Methode in C # definiert werden, um diese unterschiedlichen Fälle zu behandeln? Verschiedene Strukturen, die mit den richtigen Elementtypen definiert wurden, zeigen auch, dass sie zwischen dem C- und C # -Code unterschiedliche Größen aufweisen, die einzelnen Elemente jedoch die gleichen Größen aufweisen.

Wenn ich ein Programm wieDeviceIOView und beobachten Sie die Anrufe, die sowohl vom C-Code als auch vom C # -Code für getätigt werdenIOCTL_DVD_START_SESSION Die C-Version gibt eine Session-ID von 3 zurück und DeviceIOView zeigt, dass die Daten, die beim Ausführen des C # -Codes zurückgesendet werden, ebenfalls 3 sind. Es scheint also eine Art Marshalling-Problem mit den zurückgegebenen Parametern zu geben, da wir im C # -Code nur 0 sehen

Hat jemand Ideen oder funktionierenden Beispielcode zum Aufrufen von DeviceIoControl von C # aus, um auf DVD-Informationen zuzugreifen? (Zeigen, wie die Strukturen und Funktionen definiert und verwendet werden sollten.) Über Links zu nützlichen Websites oder andere Ratschläge würde ich mich sehr freuen.

(Wird in Visual C # 2008 Express Edition, .NET 3.5 entwickelt.)

N Johns

Beispielcode (hinzugefügt)

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

Um diesen Code auszuführen, müssen Sie den Laufwerksbuchstaben eines DVD-Laufwerks in der Befehlszeile angeben.

Ausgabe

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 

Die erste Version schlägt bei beiden Aufrufen mit den angegebenen Fehlercodes fehl:

122 - ERROR_INSUFFICIENT_BUFFER

87 - ERROR_INVALID_PARAMETER

Die zweite Version scheint erfolgreich zu sein, aber der Wert von SessionId ist -10, der initialisierte Wert. (Von MSDN sollte dieser Wert zwischen -1 und 3 liegen?) Die Endsitzung ist ebenfalls erfolgreich.

[Hinweis: Die Startsitzung der zweiten Version scheint nur bei jedem zweiten Aufruf erfolgreich zu sein, nicht sicher warum, aber dies scheint auch ein Problem im C-Code zu sein, da die Fehlerbehandlung darin besteht, es erneut zu versuchen. ]

Antworten auf die Frage(1)

Ihre Antwort auf die Frage