IOCTL_DVD_*
制御コードの C# から DeviceIoControl を呼び出そうとしています。多くの情報を読み、多くの例を試してみましたが、あまり進歩していません。
私が最終的にやろうとして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 ビット整数であることです。これらのさまざまなケースを処理するために、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 コードの問題でもあるようです。]