Appel de DeviceIoControl à partir de C # avec IOCTL_DVD_ * Codes de contrôle

J’essaie d’appeler DeviceIoControl à partir de C # pour les codes de contrôle IOCTL_DVD_* . Après avoir lu beaucoup d’informations et essayé plusieurs exemples, je n’ai pas beaucoup progressé.

Ce que j’essaie de faire, c’est d’obtenir une structure DVD_LAYER_DESCRIPTOR sur le support présent dans le lecteur de DVD. Je peux appeler CreateFile avec succès sur le lecteur de DVD, mais lorsque j’essaie d’appeler DeviceIoControl avec le code de contrôle IOCTL_DVD_START_SESSION il retourne un code de réussite, mais je ne semble pas récupérer la valeur sessionId avec succès mais renvoie toujours 0. (Et toute tentative I ont ensuite tenté de récupérer la description de la couche avec IOCTL_DVD_READ_STRUCTURE , c’est-à-dire que la fonction échoue ou renvoie succès, mais donne une structure de sortie vierge.)

Après avoir trouvé du code C qui effectue des appels similaires, j’ai pu comstackr ce code (à l’aide de Visual C ++ 2008 Express Edition) et il est capable de démarrer une session, de lire le DVD_LAYER_DESCRIPTOR et de fermer la session sans problème afin que je sache que cela fonctionne.

Les problèmes liés à C # semblent être liés à la définition de la fonction externe et à la hiérarchisation des parameters. Et comment les différentes structures qui sont passées et retournées sont définies.

J’ai consulté le site www.pinvoke.net pour savoir comment ils le définissent et ai utilisé certains exemples de code et de définitions donnés, tout en maintenant les mêmes problèmes que ceux décrits ci-dessus. Une partie du problème semble être que, pour chaque code de contrôle IOCTL, les parameters sont différents, principalement des structures, mais que pour IOCTL_DVD_START_SESSION la valeur de sortie est un entier de 32 bits. Comment définir une méthode externe en C # pour gérer ces différents cas? De plus, diverses structures, définies avec les types de membres de taille correcte, montrent qu’elles ont des tailles différentes entre les codes C et C #, mais que les membres individuels ont les mêmes tailles ???

Si j’utilise un programme tel que DeviceIOView et que DeviceIOView regarde les appels passés à la fois par le code C et le code C # pour IOCTL_DVD_START_SESSION la version C renvoie un ID de session égal à 3 et DeviceIOView indique que les données renvoyées lors de l’exécution du code C # sont également 3, de sorte qu’il semble être une sorte de problème de Marshalling des parameters retournés car nous ne voyons que 0 dans le code C #

Quelqu’un at-il des idées ou un exemple de code pratique sur la façon d’appeler DeviceIoControl à partir de C # pour accéder aux informations d’un DVD? (Indiquer comment les structures et les fonctions doivent être définies et utilisées.) Tous les liens vers des sites Web utiles ou d’autres conseils seraient très appréciés.

(En cours de développement dans Visual C # 2008 Express Edition, .NET 3.5.)

N Johns

Exemple de code (ajouté)

 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(ssortingng[] args) { ssortingng driveLetter = args[0].Subssortingng(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 ); } } 

Pour exécuter ce code, vous devez spécifier la lettre de lecteur d’un lecteur de DVD sur la ligne de commande.

Sortie

 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 

La première version échoue sur les deux appels avec les codes d’erreur donnés:

122 - ERROR_INSUFFICIENT_BUFFER

87 - ERROR_INVALID_PARAMETER

La deuxième version semble réussir, mais la valeur de SessionId est -10, la valeur initialisée. (À partir de MSDN, cette valeur doit être comprise entre -1 et 3?)

[Remarque: la deuxième session de démarrage de version ne semble réussir que sur toutes les invocations. Je ne sais pas pourquoi, mais cela semble également être un problème dans le code C que j’ai car sa gestion des erreurs consiste à réessayer. ]

Le problème réside ici:

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

Le pilote s’attend à ce que le pointeur tampon dans lpOutBuffer, mais vous fournissez plutôt sessionId lui-même (qui est zéro). Bien sûr, cela ne fonctionnera pas.

Voici ce que vous devez faire:

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

BTW, il en va de même pour tous les appels DeviceIoControl suivants, vous fournissez à nouveau une valeur, lorsque vous devez fournir un pointeur sur la valeur. Et vous devez également vérifier si votre fonction CTL_CODE génère un code io valide.

Encore une fois, DeviceIoControl attend des pointeurs vers des tampons pour les structures in et out.