Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call DeviceIOControl code asynchronously?

I am trying to call DeviceIO functions asynchronously by using the OVERLAPPED structure as described on MSDN. I am using the FSCTL_ENUM_USN_DATA control code to enumerate the MFT of NTFS drives but i am not able to run it asynchronously. The file handle is created with FILE_FLAG_OVERLAPPED but there is no difference whether I use the overlapped structure with FILE_FLAG_OVERLAPPED or not. The function does not return immediately. Is seems to be synchronous in both cases. The example below shows the enumeration of the first 100.000 MFT entries on the C:\ drive. Since I am not so familiar with the usage of overlapped structures maybe I did something wrong. My question: How can I execute DeviceIoControl(hDevice, FSCTL_ENUM_USN_DATA,...) asynchronously? Thanks for any help.

#include "stdafx.h"
#include <Windows.h>

typedef struct {
  DWORDLONG  nextusn;
  USN_RECORD FirstUsnRecord;
  BYTE Buffer[500];
}TDeviceIoControlOutputBuffer, *PTDeviceIoControlOutputBuffer;

int _tmain(int argc, _TCHAR* argv[])
{
    MFT_ENUM_DATA lInputMftData;
    lInputMftData.StartFileReferenceNumber = 0;
    lInputMftData.MinMajorVersion = 2;
    lInputMftData.MaxMajorVersion = 3;
    lInputMftData.LowUsn = 0;
    lInputMftData.HighUsn = 0;

    TDeviceIoControlOutputBuffer lOutputMftData;
    DWORD lOutBytesReturned = 0;
    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    OVERLAPPED  lOverlapped = { 0 };
    lOverlapped.hEvent = hEvent;
    LPCWSTR path = L"\\\\.\\C:";
    HANDLE hDevice = CreateFile(path, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (hDevice != INVALID_HANDLE_VALUE) {
        lOutputMftData.nextusn = 0;
        while (lOutputMftData.nextusn < 100000) {
            lInputMftData.StartFileReferenceNumber = lOutputMftData.nextusn;
            BOOL result = DeviceIoControl(hDevice, FSCTL_ENUM_USN_DATA, &lInputMftData, sizeof(lInputMftData), &lOutputMftData, sizeof(lOutputMftData), &lOutBytesReturned, &lOverlapped);
        }
    }
}
like image 567
jampeter Avatar asked Jul 08 '14 14:07

jampeter


1 Answers

TL:DR - you only get async behavior if the driver that received your request pended it.

When you call DeviceIoControl and pass an overlapped structure, it does not guarantee that the operation will be asynchronous. It means that it can be asynchronous. That depends on the way that the driver that will receive your request is implemented. When you run DeviceIoControl, it creates a irp and sends it to the driver. DeviceIoControl will promote your thread to kernel mode to create and dispatch the irp. The driver's callback will be called on that thread to handle the request. If the driver decides to handle (and complete) the request immediately, then the request is completed synchronously. In this flow, there is no difference between opening the driver with FILE_FLAG_OVERLAPPED or not. If the driver decides to pend the request then you will see real async behavior. DeviceIoControl will return FALSE and GetLastError will return ERROR_IO_PENDING. That means that the irp is pending completion and the event you supplied in the OVERLAPPED struct will be signaled when the irp is completed.

like image 134
shaked cohen Avatar answered Sep 25 '22 17:09

shaked cohen