Вызов DeviceIoControl из C # с IOCTL_DVD_ * управляющими кодами

Я пытаюсь вызвать DeviceIoControl из C # дляIOCTL_DVD_* контрольные коды. Прочитав много информации и попробовав несколько примеров, я не добился большого прогресса.

В конечном итоге я пытаюсь получитьDVD_LAYER_DESCRIPTOR Структура о носителях, находящихся в данный момент в приводе DVD. я могу позвонитьCreateFile успешно на устройстве DVD, но когда я пытаюсь позвонитьDeviceIoControl с контрольным кодомIOCTL_DVD_START_SESSION он возвращает код успеха, но я, кажется, не могу вернуть значение sessionId успешно, всегда возвращает 0. (И любая попытка, которую я тогда сделал, пыталась получить описание слоя сIOCTL_DVD_READ_STRUCTURE терпит неудачу, то есть функция терпит неудачу или возвращает успех, но дает пустую структуру вывода.)

После нахождения некоторого кода на C, который выполняет аналогичные вызовы, я смог скомпилировать этот код (используя Visual C ++ 2008 Express Edition), и он успешно смог начать сеанс, прочитавDVD_LAYER_DESCRIPTORи закройте сессию без проблем, чтобы я знал, что это работает.

Проблемы с C #, по-видимому, связаны с тем, как определяется внешняя функция и параметры распределяются. И как определяются различные структуры, которые передаются и возвращаются.

Я посмотрел на www.pinvoke.net, как они его определяют, и использовал некоторые из приведенного примера кода и определений, но все еще имеют те же проблемы, что и описанные выше. Часть проблемы, кажется, состоит в том, что для каждого управляющего кода IOCTL параметры различны, в основном структуры, но дляIOCTL_DVD_START_SESSION выходное значение - 32-битное целое число. Как определить метод extern в C # для обработки этих разных случаев? Кроме того, различные структуры, определенные с типами элементов правильного размера, показывают, что они имеют разные размеры в коде C и C #, но отдельные элементы имеют одинаковые размеры ???

Если я использую программу, какDeviceIOView и смотреть звонки, сделанные с помощью кода C и C # дляIOCTL_DVD_START_SESSION версия C возвращает идентификатор сеанса, равный 3, а DeviceIOView показывает, что данные, отправляемые обратно при выполнении кода C #, также равен 3, поэтому, похоже, существует некоторая проблема с маршаллингом возвращаемых параметров, поскольку мы видим только 0 в коде C #

У кого-нибудь есть идеи или рабочий пример кода о том, как вызвать DeviceIoControl из C # для доступа к информации DVD? (Показывает, как структуры и функции должны быть определены и использованы.) Любые ссылки на полезные веб-сайты или другие советы будут высоко оценены.

(Разрабатывается в Visual C # 2008 Express Edition, .NET 3.5.)

Н Джонс

Пример кода (добавлено)

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

Для запуска этого кода вам необходимо указать букву дисковода 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, который у меня есть, поскольку обработка ошибок заключается в повторной попытке. ]

 Nathan Johns01 июл. 2009 г., 07:14
Пример кода добавлен выше.
 Nathan Johns01 июл. 2009 г., 06:18
Выложу пример в ближайшее время, просто извлекая пример из кода. Код очень грязный, так как на данный момент это просто тестовый код для моего собственного удовольствия.
 Michael01 июл. 2009 г., 06:07
Разместите свой код P / Invoke?

Ответы на вопрос(1)

result = DeviceIoControl(_hdev, CTL_CODE(0x00000033, 0x0400, 0, 1),
   IntPtr.Zero, 0, (IntPtr)sessionId, Marshal.SizeOf(sessionId),
   out bytesReturned, IntPtr.Zero);

Драйвер ожидает указатель на буфер в lpOutBuffer, но вы вместо этого предоставляете сам идентификатор сессии (который равен нулю). Конечно, это не сработает.

Вот что вам нужно сделать:

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

Кстати, то же самое относится ко всем последующим вызовам DeviceIoControl, вы снова предоставляете значение, когда вам нужно предоставить указатель на значение. И вам также нужно проверить, строит ли ваша функция CTL_CODE правильный код io.

Опять же, DeviceIoControl ожидает указатели на буферы для входных и выходных структур.

Ваш ответ на вопрос