Chamando DeviceIoControl de c # com IOCTL_DVD_ * códigos de controle
Eu estou tentando chamar DeviceIoControl de c # paraIOCTL_DVD_*
códigos de controle. Tendo lido muitas informações e tentando vários exemplos, não fiz muito progresso.
O que estou tentando fazer é obter umDVD_LAYER_DESCRIPTOR
estrutura sobre a mídia atualmente na unidade de DVD. eu posso ligarCreateFile
com sucesso no dispositivo de DVD, mas quando tento ligarDeviceIoControl
com o código de controleIOCTL_DVD_START_SESSION
ele retorna um código de sucesso, mas eu não consigo recuperar o valor de sessionId com sucesso, sempre retorna 0. (E qualquer tentativa que fiz para tentar obter a descrição da camada comIOCTL_DVD_READ_STRUCTURE
falha, ou seja, a função falha ou retorna sucesso, mas fornece uma estrutura de saída em branco.
Depois de encontrar algum código C que faz chamadas semelhantes, eu era capaz de compilar este código (usando o Visual C ++ 2008 Express Edition) e ele é capaz de iniciar uma sessão com sucesso, ler oDVD_LAYER_DESCRIPTOR
e feche a sessão sem problemas, então sei que isso funciona.
Os problemas do C # parecem estar relacionados à forma como a função externa é definida e os parâmetros organizados. E como as várias estruturas que são passadas e retornadas são definidas.
Eu olhei para www.pinvoke.net para como eles definem, e usei alguns dos códigos de exemplo e definições dadas, mas ainda tenho os mesmos problemas descritos acima. Parte do problema parece ser que para cada código de controle IOCTL os parâmetros são diferentes, principalmente estruturas, mas paraIOCTL_DVD_START_SESSION
o valor de saída é um inteiro de 32 bits. Como um método externo em C # pode ser definido para lidar com esses diferentes casos? Também várias estruturas, definidas com os tipos de membros de tamanho correto, mostram que eles têm tamanhos diferentes entre os códigos C e C #, mas os membros individuais são do mesmo tamanho ???
Se eu usar um programa comoDeviceIOView
e observe as chamadas feitas pelo código C e pelo código C # paraIOCTL_DVD_START_SESSION
a versão C retorna um sessionid de 3 e o DeviceIOView mostra que os dados que estão sendo enviados quando o código C # é também 3, então parece haver algum tipo de problema Marshalling dos parâmetros retornados, pois só vemos 0 no código C #.
Alguém tem alguma idéia ou código de exemplo de trabalho sobre como chamar DeviceIoControl de C # para acessar as informações do DVD? (Mostrando como as estruturas e funções devem ser definidas e usadas.) Quaisquer links para sites úteis ou outros conselhos seriam muito apreciados.
(Sendo desenvolvido no Visual C # 2008 Express Edition, .NET 3.5.)
N Johns
Exemplo de código (adicionado)
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
);
}
}
Para executar este código, você precisa especificar a letra da unidade de DVD na linha de comando.
Saída
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
A primeira versão falha em ambas as chamadas com os códigos de erro fornecidos:
122 - ERROR_INSUFFICIENT_BUFFER
87 - ERROR_INVALID_PARAMETER
A segunda versão parece ter sucesso, mas o valor de SessionId é -10, o valor inicializado. (Do MSDN esse valor deve estar entre -1 e 3?) A sessão final também é bem-sucedida.
[Nota: a segunda sessão de início da versão só parece ter sucesso em todas as outras invocações, não tenho certeza do porquê, mas isso também parece ser um problema no código C que tenho, já que o tratamento de erros é para tentar novamente. ]