3

IOCTL_DVD_*制御コードの C# から DeviceIoControl を呼び出そうとしています。多くの情報を読み、多くの例を試してみましたが、あまり進歩していません。

私が最終的にやろうとしてDVD_LAYER_DESCRIPTORいるのは、現在 DVD ドライブにあるメディアに関する構造を取得することです。CreateFileDVD デバイスで正常に呼び出すことができます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 ビット整数であることです。これらのさまざまなケースを処理するために、C# の extern メソッドをどのように定義できますか? また、適切なサイズのメンバー型で定義されたさまざまな構造体は、C コードと C# コードの間でサイズが異なることを示していますが、個々のメンバーは同じサイズです???

次のようなプログラムを使用して、C バージョンDeviceIOViewの C コードと C# コードの両方によって行われた呼び出しを監視するとIOCTL_DVD_START_SESSION、3 のセッション ID が返され、DeviceIOView は、C# コードの実行時に返されるデータも 3 であることを示します。 C# コードでは 0 しか表示されないため、返されるパラメーターの一種のマーシャリングの問題

C# から DeviceIoControl を呼び出して DVD 情報にアクセスする方法について、アイデアや実際のサンプル コードを持っている人はいますか? (構造と機能がどのように定義され、使用されるべきかを示します。) 有用な Web サイトへのリンクやその他のアドバイスをいただければ幸いです。

(Visual C# 2008 Express Edition、.NET 3.5 で開発中)

N・ジョンズ

サンプルコード (追加)

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

2 番目のバージョンは成功したように見えますが、SessionId の値は初期化された値である -10 です。(MSDN から、この値は -1 から 3 の間である必要がありますか?) 終了セッションも成功します。

[ 注: 2 番目のバージョンの開始セッションは、他のすべての呼び出しでのみ成功するようです。理由はわかりませんが、エラー処理は再試行する必要があるため、これは私が持っている C コードの問題でもあるようです。]

4

1 に答える 1

4

問題はここにあります:

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

ドライバーは lpOutBuffer 内のバッファーへのポインターを想定していますが、代わりに sessionId 自体 (ゼロ) を指定します。もちろん、これは機能しません。

ここであなたがする必要があること:

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 は in 構造体と out 構造体のバッファーへのポインターを想定しています。

于 2009-07-01T07:40:42.713 に答える