如何异步调用 DeviceIOControl 代码?

Posted

技术标签:

【中文标题】如何异步调用 DeviceIOControl 代码?【英文标题】:How to call DeviceIOControl code asynchronously? 【发布时间】:2014-07-08 14:04:40 【问题描述】:

我正在尝试使用 MSDN 上描述的 OVERLAPPED 结构异步调用 DeviceIO 函数。 我正在使用 FSCTL_ENUM_USN_DATA 控制代码来枚举 NTFS 驱动器的 MFT,但我无法异步运行它。文件句柄是用 FILE_FLAG_OVERLAPPED 创建的,但我是否使用重叠结构与 FILE_FLAG_OVERLAPPED 没有区别。该函数不会立即返回。在这两种情况下似乎都是同步的。 下面的示例显示了 C:\ 驱动器上前 100.000 个 MFT 条目的枚举。 由于我对重叠结构的使用不太熟悉,也许我做错了什么。我的问题:如何异步执行 DeviceIoControl(hDevice, FSCTL_ENUM_USN_DATA,...)? 感谢您的帮助。

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

【问题讨论】:

如果驱动程序一般不支持异步 I/O 或者对于某些请求,它会同步处理 I/O 请求,忽略 OVERLAPPED 参数。不过,我不知道这个驱动程序是否支持异步 I/O 模式下的特定请求。 感谢您的回复。根据微软 FSCTL_ENUM_USN_DATA 可以调用异步:msdn.microsoft.com/en-us/library/windows/desktop/… 好吧,再次查看您的代码,我看不到您填充了lOverlapped 变量的hEvent 成员。这可能是同步执行此请求的一个很好的理由。 【参考方案1】:

TL:DR - 只有在收到您的请求的驱动程序将其挂起时,您才会获得异步行为。

当您调用 DeviceIoControl 并传递一个重叠结构时,它不保证操作将是异步的。这意味着它可以是异步的。这取决于将接收您的请求的驱动程序的实现方式。 当您运行 DeviceIoControl 时,它会创建一个 irp 并将其发送给驱动程序。 DeviceIoControl 会将您的线程提升到内核模式以创建和调度 irp。将在该线程上调用驱动程序的回调来处理请求。如果驱动程序决定立即处理(并完成)请求,则请求同步完成。在此流程中,是否使用 FILE_FLAG_OVERLAPPED 打开驱动程序没有区别。 如果驱动程序决定挂起请求,那么您将看到真正的异步行为。 DeviceIoControl 将返回 FALSE,GetLastError 将返回 ERROR_IO_PENDING。这意味着 irp 正在等待完成,并且您在 OVERLAPPED 结构中提供的事件将在 irp 完成时发出信号。

【讨论】:

【参考方案2】:
HEVENT hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
OVERLAPPED  lOverlapped =  0 ;
lOverlapped.hEvent = hEvent;

...

BOOL result = DeviceIoControl(hDevice, FSCTL_ENUM_USN_DATA, &lInputMftData, sizeof(lInputMftData), &lOutputMftData, sizeof(lOutputMftData), &lOutBytesReturned, &lOverlapped);

// If operation is asynchronous, wait for hEvent here or somewhere else

【讨论】:

谢谢。但还是不行。没有异步。重叠的结构和设备句柄应该是正确的:如果我打电话给例如'readfile' 函数具有相同的重叠结构和相同的 hdevice 句柄,它立即返回 GetalstError() = 997 (ERROR_IO_PENDING: Overlapped I/O operation is in progress),这表明 'readfile' 被异步调用。但它不适用于 deviceio。

以上是关于如何异步调用 DeviceIOControl 代码?的主要内容,如果未能解决你的问题,请参考以下文章

DeviceIoControl 在 C++ 和 C# 中工作,但在 C++/CLI 中调用时返回 ERROR_INVALID_FUNCTION

如何使用 DeviceIOControl() 从 CD 中获取“曲目信息”?

使用 DeviceIoControl 都有哪些好的策略?

Winpcap 和 DeviceIoControl - Win7 上的错误代码 1

使用带有 FSCTL_LOCK_VOLUME 的 DeviceIoControl 来锁定卷。调试器问题

带有输入无符号字符缓冲区 C++ 的 DeviceIoControl