Wywołanie DeviceIoControl z C # za pomocą IOCTL_DVD_ * Kody sterujące

Próbuję wywołać DeviceIoControl z C # dlaIOCTL_DVD_* kody sterujące. Po przeczytaniu wielu informacji i wypróbowaniu kilku przykładów nie zrobiłem wiele.

W końcu staram się uzyskaćDVD_LAYER_DESCRIPTOR struktura o mediach aktualnie w napędzie DVD. Mogę zadzwonićCreateFile pomyślnie na urządzeniu DVD, ale gdy próbuję zadzwonićDeviceIoControl z kodem kontrolnymIOCTL_DVD_START_SESSION zwraca kod sukcesu, ale wydaje mi się, że nie przywracam wartości sessionId z powodzeniem, zawsze zwraca 0. (I każda próba, którą wykonałem, aby spróbować uzyskać opis warstwy za pomocąIOCTL_DVD_READ_STRUCTURE kończy się niepowodzeniem, tj. funkcja kończy się niepowodzeniem lub zwraca sukces, ale daje pustą strukturę wyjściową.)

Po znalezieniu kodu C, który wywołuje podobne wywołania, udało mi się skompilować ten kod (przy użyciu Visual C ++ 2008 Express Edition) i udało mu się rozpocząć sesję, przeczytaćDVD_LAYER_DESCRIPTORi zamknij sesję bez problemu, więc wiem, że to działa.

Wydaje się, że kwestie C # odnoszą się do sposobu definiowania funkcji zewnętrznej i konfiguracji parametrów. I jak definiowane są różne przekazane i zwrócone struktury.

Zajrzałem na www.pinvoke.net, aby dowiedzieć się, jak to zdefiniowali, i wykorzystałem niektóre z podanych kodów przykładowych i definicji, ale wciąż mam te same problemy, co opisane powyżej. Częścią problemu wydaje się być to, że dla każdego kodu sterującego IOCTL parametry są różne, głównie struktury, ale dlaIOCTL_DVD_START_SESSION wartością wyjściową jest 32-bitowa liczba całkowita. W jaki sposób można zdefiniować metodę extern w C # do obsługi tych różnych przypadków? Również różne struktury, zdefiniowane za pomocą typów elementów o odpowiednim rozmiarze, pokazują, że są różne rozmiary między kodem C i C #, ale poszczególne elementy mają te same rozmiary?

Jeśli korzystam z programu takiego jakDeviceIOView i obserwuj połączenia wykonywane zarówno przez kod C, jak i przez kod C #IOCTL_DVD_START_SESSION wersja C zwraca sesję 3, a DeviceIOView pokazuje, że dane wysyłane z powrotem podczas uruchamiania kodu C # to 3, więc wydaje się, że istnieje jakiś problem z Marshallingiem zwracanych parametrów, ponieważ widzimy tylko 0 w kodzie C #

Czy ktoś ma jakieś pomysły lub działający przykładowy kod na temat wywoływania DeviceIoControl z C #, aby uzyskać dostęp do informacji o DVD? (Pokazuje, jak struktury i funkcje powinny być zdefiniowane i używane.) Wszelkie linki do przydatnych stron internetowych lub innych porad byłyby bardzo mile widziane.

(Opracowany w Visual C # 2008 Express Edition, .NET 3.5.)

N Johns

Przykładowy kod (dodany)

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

Aby uruchomić ten kod, musisz podać literę napędu DVD w linii poleceń.

Wydajność

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 

Pierwsza wersja kończy się niepowodzeniem w obu połączeniach z podanymi kodami błędów:

122 - ERROR_INSUFFICIENT_BUFFER

87 - ERROR_INVALID_PARAMETER

Wydaje się, że druga wersja odniosła sukces, ale wartość SessionId wynosi -10, wartość zainicjowana. (Od MSDN ta wartość powinna wynosić od -1 do 3?) Sesja końcowa również się powiedzie.

[Uwaga: sesja startowa drugiej wersji wydaje się być skuteczna tylko przy każdym innym wywołaniu, nie wiem dlaczego, ale wydaje się, że jest to również problem w kodzie C, ponieważ jego obsługa błędów polega na ponownym ponowieniu. ]

questionAnswers(1)

yourAnswerToTheQuestion