在MSDN文档中,GetQueuedCompletionStatus的lpOverlapped
参数表明,应用程序可以通过设置{的hEvent
成员的低位来防止完成端口通知。 {1}}结构。但是,在通知停止后是否可以恢复?
我需要使用它来监视网络文件夹的更改:
当OVERLAPPED
返回GetQueuedCompletionStatus
并且FALSE
返回GetLastError()
时,我这样做(有效):
ERROR_NETNAME_DELETED
当网络问题解决后,我尝试执行反向操作-但它不起作用:
di->Overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, di->lpszDirName );
reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) |= 0x1;
(如果该解决方案与Windows 7兼容,那就很好了
答案 0 :(得分:2)
首先不能将“完成”端口通知“挂起”或“恢复”
即使您已通过函数传递了与 完成端口和有效的
OVERLAPPED
结构,应用程序可以 阻止完成端口通知。通过指定一个hEvent
结构的OVERLAPPED
成员的有效事件句柄, 并设置其低位。一个有效的事件句柄,其低序 设置该位可防止I / O完成从排队到完成 端口。
这意味着下一个-当我们调用某些win32 I / O api(将指向OVERLAPPED
的指针作为输入/输出参数的api,例如ReadFile
,ReadDirectoryChangesW
,{{ 1}}等)和与完成端口相关联的文件句柄(传递给此api)-尽管如此,我们仍可以通过事件句柄以低序位阻止 this 调用的完成端口通知。这仅用于具体的api调用,不会影响其他任何api调用。与LockFileEx
(严格地说,我们也可以在地方GetQueuedCompletionStatus
处简单地传递1
。但是在这种情况下,问题是-如果api返回未决状态,我们如何获得有关I / O的通知?仅在文件句柄上,调用hEvent
。但这仅在同时不对该文件进行任何其他I / O调用的情况下才是正确的)
在任何情况下都需要了解其内部工作方式。所有本机I / O api都具有下一个签名:
GetOverlappedResult
在开始时都具有这个共同的5个参数。对于此调用导致的队列I / O完成,必须满足几个条件。当然NTSTATUS NTAPI SomeIoApi(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
...
);
必须与某个完成端口关联(到该端口,并且可以分组发送)。但是一个强制条件-FileHandle
必须不为零(ApcContext
)。如果满足这2个条件并且设备未返回错误状态(如果在文件上设置了FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
-仅必须处于待处理状态)-当I / O完成时-ApcContext != 0
指针将被推到端口。然后可以通过
ApcContext
或通过win32 shell NTSTATUS
NTAPI
NtRemoveIoCompletion(
_In_ HANDLE IoCompletionHandle,
_Out_ PVOID *KeyContext,
_Out_ PVOID *ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER Timeout
);
。
对于未将数据包发送到端口的解决方案(即使文件句柄也与完成端口相关联)-设置GetQueuedCompletionStatus
。 win32层以另一种方式(伪代码)执行此操作:
ApcContext = 0
它检查BOOL WINAPI SomeWin32Api(
HANDLE FileHandle,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
{
HANDLE hEvent = lpOverlapped->hEvent;
PVOID ApcContext = lpOverlapped;
if ((ULONG_PTR)hEvent & 1)
{
reinterpret_cast<uintptr_t&>(hEvent) &= ~1;
ApcContext = 0;
}
NTSTATUS status = SomeIoApi(
FileHandle,
hEvent,
lpCompletionRoutine, // not exactly, but by sense
ApcContext,
(PIO_STATUS_BLOCK)lpOverlapped,...);
}
中hEvent
的低位-如果已置位,则在原位传递0 OVERLAPPED
,否则传递ApcContext
(指向lpOverlapped
的指针)作为上下文(OVERLAPPED
)
请注意,nt层让任何ApcContext = lpOverlapped;
指针作为void*
传递。但是win32层始终在此处传递指向ApcContext
结构或0的指针。因为this和OVERLAPPED
将此指针返回为GetQueuedCompletionStatus
(与_Out_ LPOVERLAPPED *lpOverlapped
比较-返回为{{1} })
无论如何,该技巧仅影响具体的单个win32 I / O调用,并且如果您从重叠的NtRemoveIoCompletion
中延迟_Out_ PVOID *ApcContext
中的低位重新设置,则已经无效-0位置hEvent
已通过。
从一般的角度来看,当我们将文件句柄与完成端口相关联但不希望在某些调用中使用它时,这种情况很少发生。通常这是另一个api调用。例如,我们可以创建异步文件句柄,并将其与完成端口关联。并在调用reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);
中使用端口通知,但是在开始写入之前,我们可以通过ApcContext
设置/删除文件压缩。由于文件是异步的,因此WriteFile
也可以完成异步,但是我们可以希望阻止此ioctl的完成端口通知,而是就地(在事件上)等待它完成。在这种情况下,可以使用此技巧。
,在大多数情况下,应用程序(如果不是具有大量I / O请求的服务器)可以改为手动调用FSCTL_SET_COMPRESSION
,通过FSCTL_SET_COMPRESSION
或GetQueuedCompletionStatus
将回调绑定到文件。作为您创建iocp的结果系统,将在此iocp上侦听线程池(通过BindIoCompletionCallback
或CreateThreadpoolIo
),然后调用回调。这非常简化了您的src代码和逻辑
发现:
GetQueuedCompletionStatus
)
仅影响此特定请求NtRemoveIoCompletion
和自线程
池。相反,只需调用ReadDirectoryChangesW
获取文件