DeviceIoControl

  • C++

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von Infinity.

    DeviceIoControl

    Hi,

    ich versuche gerade die Funktion DeviceIoControl (msdn.microsoft.com/en-us/libra…op/aa363216(v=vs.85).aspx) der WinAPI aufzurufen um ein Laufwerk zu steuern.

    In VB lässt sich die Funktion sehr einfach verwenden:

    VB.NET-Quellcode

    1. Dim pmr As New PREVENT_MEDIA_REMOVAL With {.PreventMediaRemoval = 1}
    2. Return DeviceIoControl(_DriveHandle, IOCTL.STORAGE_MEDIA_REMOVAL, pmr, Marshal.SizeOf(pmr), IntPtr.Zero, 0, 0, IntPtr.Zero)


    Wenn ich die gleiche Funktion in C++ aufrufe, wird mein Prozess vom Betriebssystem "nach Erhalt eines Signales" angehalten (genaueres sagt mir der Debugger vom Qt Creator nicht). Allerdings erfüllt die Funktion trotzdem noch ihre Aufgabe, also z. B. das Laufwerk zu (ent)sperren. So falsch kann der Code also garnicht sein:

    Quellcode

    1. LPDWORD bytesReturned;
    2. return DeviceIoControl(handle, IOCTL_STORAGE_LOAD_MEDIA, NULL, NULL, NULL, NULL, bytesReturned, NULL) != 0;


    An das DeviceHandle komme ich mit der CreateFile-Funktion:

    VB.NET-Quellcode

    1. _DriveInfo = Drive
    2. _DriveHandle = CreateFile("\\.\" & _DriveInfo.Name(0) & ":", CType(&H80000000 Or &H40000000, FileAccess), FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, &H80, IntPtr.Zero)
    3. If _DriveHandle = IntPtr.Zero OrElse _DriveHandle.ToInt32 = -1 Then
    4. Throw New Win32Exception(Marshal.GetLastWin32Error(), "Couldn't create the drive handle. The drive must be a optical drive.")
    5. End If

    Quellcode

    1. rootPathName = L"\\\\.\\" + rootPathName; //rootPathName enthält die richtige Zeichenfolge (mit debugger geprüft) -> daran liegt es nicht
    2. handle = CreateFile(rootPathName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    3. if(handle == INVALID_HANDLE_VALUE)
    4. {
    5. IoException ex("The drive handle couldn't be created.");
    6. throw ex; // exception wird nicht ausgeworfen
    7. }


    Wie gesagt, in VB funktioniert es und in C++ auch, aber die Anwendung hängt sich auf. Hat jemand eine Idee, was ich falsch mache?
    return DeviceIoControl(handle, IOCTL_STORAGE_LOAD_MEDIA, NULL, NULL, NULL, NULL, bytesReturned, NULL) != 0;

    schonmal versucht bei bytesReturned nen NULL-Pointer zu übergeben, vlt. denkt der sonst, dass du daten Empfangen willst und wartet...(laut MSDN auch optional und somit unnötig anzugeben, wenn du auch keinen Buffer hast).

    Btw. wie meldet dir QT, dass es angehalten worden ist?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Ja, das hatte ich am Anfang und es führt zu genau dem selben Problem. MSDN sagt sogar, dass man als vorletzten Parameter kein NULL übergeben darf, aber mit VB funktioniert auch das problemlos.

    Qt gibt leider nicht viele nützliche Infos zu dem Problem aus, nur:

    Der Prozess wurde nach Erhalt eines Signals vom Betriebssystem angehalten.
    Name des Signals: SIGSEGV
    Bedeutung: Segmentation fault


    Und der Debugger bleibt auch genau in der Zeile stehen, in der ich DeviceIoControl aufrufe.
    SIGSEGV

    Du greifst auf irgendeinen Speicher zu, auf den du nicht darfst. Irgendetwas, was du z.B. als Parameter übergibst hast du im Speicher bereits freigegeben, oder nie alloziert. Vmtl. passiert der Fehler auch erst nach dem return...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Die Beschreibung auf MSDN sagt eindeutig, dass das vorletzte Parameter vom Typ LPDWORD, also einem Pointer zu einem DWORD, also einem Pointer zu einem unsigned long:

    BOOL WINAPI DeviceIoControl(
    _In_ HANDLE hDevice,
    _In_ DWORD dwIoControlCode,
    _In_opt_ LPVOID lpInBuffer,
    _In_ DWORD nInBufferSize,
    _Out_opt_ LPVOID lpOutBuffer,
    _In_ DWORD nOutBufferSize,
    _Out_opt_ LPDWORD lpBytesReturned,
    _Inout_opt_ LPOVERLAPPED lpOverlapped
    );


    Wenn ich statt einem LPDWORD einen Pointer zu einem DWORD übergebe, geht es. Wo ist da der Sinn?

    Folgendes funktioniert:

    Quellcode

    1. DWORD bytesReturned;
    2. PREVENT_MEDIA_REMOVAL pmr;
    3. pmr.PreventMediaRemoval = true;
    4. return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, &pmr, sizeof(pmr), NULL, 0, &bytesReturned, NULL) != 0;


    Aber der Code macht doch im Prinzip nichts anderes als der alte Code? Welchen Unterschied macht es, die Referenz zu einem DWORD/unsigned int zu übergeben, als direkt einen LPDWORD/Pointer zu einem unsigned int zu übergeben?
    Ein Pointer ist zwar ähnlich einer Referenz, aber eine Referenz ist doch nochmal etwas anderes...
    de.wikibooks.org/wiki/C++-Prog…Grundelemente/_Referenzen

    ach natürlich ist ja klar, dass das nicht funktioniert hat....
    LPDWORD ist ja ein Pointer, aber du hast gar nicht definiert auf was der Pointer zeigt, deshalb hast du einen Speicherzugriffsfehler bekommen, wenn du direkt den Pointer von nem DWORD übergibst funktionierts deshalb, aber du kannst auch LPDWORD übergeben und zwar so:

    Quellcode

    1. DWORD bytesReturned;
    2. LPDWORD ptr = &bytesReturned;
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---