如何异步调用 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 中获取“曲目信息”?
Winpcap 和 DeviceIoControl - Win7 上的错误代码 1