首先,这个问题不仅与
WriteFile
但对于任何异步I/O函数,几乎所有函数都会得到指向
OVERLAPPED
结构因为对于所有这些功能
IRP
(
I/O请求包
)(查看it定义
wdm。H
)已分配。
hEvent
句柄来自
重叠的
转换为对象指针并存储在
PKEVENT UserEvent;
成员
IRP公司
。事件设置(或未设置)的确切时间
IRP公司
在中完成
IopCompleteRequest
常规这个
IRP公司
完成函数对于所有I/O api都是通用的,所以和规则(当完成触发时)与所有相关。不幸的是,这是非常糟糕的记录。win32层(比较NT层)在此处添加了额外的非常精简的问题。
基于
wrk src code
,我们可以看到异步io的I/O管理器触发完成(有3种类型-事件、apc和iocp(互斥)),当
!NT_ERROR( irp->IoStatus.Status )
或
irp->PendingReturned
。
如果我们使用本机api,则直接返回
NTSTATUS
-何时
(ULONG)status < 0xc0000000
.但这是一个非常有问题的范围
0x80000000 <= status < 0xc0000000
或
NT_WARNING(status)
当不清楚时,将设置完成(偶数设置、apc或数据包到iocp队列)。这是因为在分配之前
IRP公司
输入/输出管理器执行一些基本检查,并可以从此处返回错误。通常I/O管理器会从
NT_ERROR(status)
,这正确意味着将不会完成(不会设置事件)),但存在且很少出现异常。例如
ReadDirectoryChangesW
(或
ZwNotifyChangeDirectoryFile
)the
lpBuffer(lpBuffer)
指针必须与DWORD对齐(与
FILE_NOTIFY_INFORMATION
)否则输入/输出管理器返回
STATUS_DATATYPE_MISALIGNMENT
(0x80000002)发件人
NT_WARNING
范围但在这种情况下不会完成(事件集),因为函数失败
之前
分配
IRP公司
.如果我们打电话
FSCTL_FILESYSTEM_GET_STATISTICS
缓冲区不够大-文件系统驱动程序(不是I/O管理器)返回
STATUS_BUFFER_OVERFLOW
(0x8000005)。但因为在这一点上
IRP公司
已分配且代码不是来自
NT_ERROR
范围-将是事件集。
因此,如果输入/输出管理器出错(之前
IRP公司
已分配)-将不会完成。否则,如果驱动程序出错(传递到
IRP公司
)如果函数返回,则完成
!NT_ERROR(status)
.因此,如果我们得到:
-
NT_SUCCESS(status)
(the
STATUS_PENDING
(
0x103
)是其中的一部分)-will
是
完成
-
NT\U错误(状态)
将无法完成
-
NT\U警告(状态)
-不清楚,取决于输入/输出管理器的此错误
(否)或驾驶员(是)
但使用win32层会使情况更加糟糕。因为不清楚它是如何解释的
NT\U警告(状态)
-大多数win32 api将此解释为错误-返回
错误的
并设置上一个错误(从状态转换)。但是一些api-例如
ReadDirectoryChangesW
将其解释为
成功
代码-返回
符合事实的
并没有设置最后一个错误。如果我们打电话
ReadDirectoryChangesW
如果缓冲区对齐错误(但其他参数有效),则返回。。
符合事实的
不设置任何错误。但api调用确实失败了。这个
ZwNotifyChangeDirectoryFile
内部退货
STATUS\u DATATYPE\u未对齐
在这里
从另一侧,如果
DeviceIoControl
对于
FSCTL\u FILESYSTEM\u GET\u统计信息
失败(返回false),代码为
ERROR_MORE_DATA
(转换自
STATUS\u BUFFER\u溢出
)在此情况下,将设置事件(完成)。
还有win32错误代码我们无法理解-初始状态为
NT\U错误
或
NT\U警告
代码-转换(
RtlNtStatusToDosError
)win32状态错误丢失此信息
问题在于
NT\U警告(状态)
如果我们使用IOCP完成(而不是事件)并设置
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
在文件上—在这种情况下,I/O管理器将完成条目排队到端口,当且仅当
STATUS\u挂起
将通过本机api调用返回。对于win32层,这通常意味着api返回false,最后一个错误为
ERROR_IO_PENDING
.例外情况-
WriteFileEx
,则,
ReadFileEx
此处返回true。然而,在这种情况下,这并没有帮助
ReadDirectoryChangesW
无论如何(我假设这是windows bug)
另请阅读
FILE_SKIP_SET_EVENT_ON_HANDLE
section-这隐式表示在case asynchronous函数中设置显式事件(来自overlapped)的时间-当请求返回时
成功代码
,或返回的错误为
错误\u IO\u挂起
但这里的问题是什么
成功代码
?win32 api返回true?不总是,从
FSCTL\u FILESYSTEM\u GET\u统计信息
-the
ERROR\u MORE\u数据
(
STATUS\u BUFFER\u溢出
)还有成功代码。或
STATUS_NO_MORE_FILES
返回人
NtQueryDirectoryFile
还将设置成功代码-事件(apc或iocp完成)。但还是一样的
NtQueryDirectoryFile
可以返回
STATUS\u DATATYPE\u未对齐
什么时候
FileInformation
对齐错误-这是失败代码,因为在分配之前从I/O管理器返回
IRP公司
这个
NT\U警告
大多数情况下的状态是成功代码(将是完成),但win32层在大多数情况下将其解释为失败代码(返回false)。
测试代码示例:
ULONG BOOL_TO_ERROR(BOOL fOk)
{
return fOk ? NOERROR : GetLastError();
}
void CheckEventState(HANDLE hEvent, ULONG err, NTSTATUS status = RtlGetLastNtStatus())
{
DbgPrint("error = %u(%x)", err, err ? status : STATUS_SUCCESS);
switch (WaitForSingleObject(hEvent, 0))
{
case WAIT_OBJECT_0:
DbgPrint("Signaled\n");
break;
case WAIT_TIMEOUT:
DbgPrint("NON signaled\n");
break;
default:
DbgPrint("error=%u\n", GetLastError());
}
#if 1
EVENT_BASIC_INFORMATION ebi;
if (0 <= ZwQueryEvent(hEvent, EventBasicInformation, &ebi, sizeof(ebi), 0))
{
DbgPrint("EventState = %x\n", ebi.EventState);
}
#endif
}
void demoIoEvent()
{
WCHAR sz[MAX_PATH];
GetSystemDirectoryW(sz, RTL_NUMBER_OF(sz));
HANDLE hFile = CreateFileW(sz, 0, FILE_SHARE_VALID_FLAGS, 0,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILESYSTEM_STATISTICS fs;
OVERLAPPED ov = {};
if (ov.hEvent = CreateEvent(0, TRUE, FALSE, 0))
{
FILE_NOTIFY_INFORMATION fni;
IO_STATUS_BLOCK iosb;
NTSTATUS status = ZwNotifyChangeDirectoryFile(hFile, ov.hEvent, 0, 0, &iosb,
(FILE_NOTIFY_INFORMATION*)(1 + (PBYTE)&fni), 1, FILE_NOTIFY_VALID_MASK, FALSE);
CheckEventState(ov.hEvent, ERROR_NOACCESS, status);
ULONG err = BOOL_TO_ERROR(ReadDirectoryChangesW(hFile,
(FILE_NOTIFY_INFORMATION*)(1 + (PBYTE)&fni), 1, 0, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));
CheckEventState(ov.hEvent, err);
err = BOOL_TO_ERROR(DeviceIoControl(hFile,
FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0, 0, 0, 0, &ov));
CheckEventState(ov.hEvent, err);
err = BOOL_TO_ERROR(DeviceIoControl(hFile,
FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0, &fs, sizeof(fs), 0, &ov));
CheckEventState(ov.hEvent, err);
if (err == ERROR_MORE_DATA)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
ULONG cb = si.dwNumberOfProcessors * fs.SizeOfCompleteStructure;
union {
PVOID pv;
PBYTE pb;
PFILESYSTEM_STATISTICS pfs;
};
pv = alloca(cb);
err = BOOL_TO_ERROR(DeviceIoControl(hFile, FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0,
pv, cb, 0, &ov));
CheckEventState(ov.hEvent, err);
if (!err && GetOverlappedResult(hFile, &ov, &cb, FALSE))
{
do
{
} while (pb += fs.SizeOfCompleteStructure, --si.dwNumberOfProcessors);
}
}
CloseHandle(ov.hEvent);
}
CloseHandle(hFile);
}
}
和输出:
error = 998(80000002)NON signaled
EventState = 0
error = 0(0)NON signaled
EventState = 0
error = 122(c0000023)NON signaled
EventState = 0
error = 234(80000005)Signaled
EventState = 1
error = 0(0)Signaled
EventState = 1