C++ 停止后恢复完成端口通知

C++ 停止后恢复完成端口通知,c++,windows,asynchronous,winapi,overlapped-io,C++,Windows,Asynchronous,Winapi,Overlapped Io,在lpOverlapped参数的MSDN文档中,据说应用程序可以通过设置OVERLAPPED结构的hEvent成员的低位来防止完成端口通知。但是,是否可以在停止通知后恢复通知 我需要使用它来监视网络文件夹的更改: 当GetQueuedCompletionStatus返回FALSE和GetLastError()返回ERROR\u NETNAME\u DELETED时,我这样做(有效): di->Overlapped.hEvent=CreateEvent(NULL、FALSE、FALSE、di->l

lpOverlapped
参数的MSDN文档中,据说应用程序可以通过设置
OVERLAPPED
结构的
hEvent
成员的低位来防止完成端口通知。但是,是否可以在停止通知后恢复通知

我需要使用它来监视网络文件夹的更改:

GetQueuedCompletionStatus
返回
FALSE
GetLastError()
返回
ERROR\u NETNAME\u DELETED
时,我这样做(有效):

di->Overlapped.hEvent=CreateEvent(NULL、FALSE、FALSE、di->lpszDirName);
重新解释(di->Overlapped.hEvent)|=0x1;
当网络问题解决后,我尝试进行反向操作,但没有成功:

reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);
reinterpret\u cast(di->Overlapped.hEvent)&=~(0x1);

(如果解决方案与Windows 7兼容就好了)

首先,完成端口通知不能“挂起”或“恢复”

即使您已向函数传递了与 完成端口和有效的重叠结构,应用程序可以 阻止完成端口通知。这是通过指定 重叠的
结构的
hEvent
成员的有效事件句柄, 并设置其低阶位。一个有效的事件句柄,其低阶 位设置可防止I/O完成从排队到完成 港口

这意味着下一步-当我们调用一些win32 I/O api(将指向重叠的
的指针作为in/out参数的api,例如
ReadFile
ReadDirectoryChangesW
LockFileEx
等)和文件句柄(传递到此api)与完成端口关联-尽管如此,我们可以通过低阶位的事件句柄阻止此调用的完成端口通知。这仅用于具体的api调用,不影响任何其他api调用。所有这些都与
GetQueuedCompletionStatus

(严格地说,我们也可以将
1
传递到适当的位置
hEvent
。但在这种情况下,问题是-如果api返回挂起状态,我们如何获得关于I/O完成的通知?是的,可能仅在文件句柄上等待,调用
GetOverlappedResult
。但这只有在concurent中没有对该文件的任何其他I/O调用时才是正确的)

在任何情况下,我们都需要了解这是如何在内部工作的。所有本机I/O api都具有下一个签名:

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, 
                         ...
                         );
所有这些在开始时都有这5个公共参数。对于作为此调用结果的队列I/O完成,必须满足几个条件。当然,
FileHandle
必须与某个完成端口相关联(到该端口并可以进行数据包发送)。但除此之外,一个强制条件-
ApcContext
必须不为零(
ApcContext!=0
)。如果满足这2个条件并且设备返回非错误状态(如果在文件上设置-必须仅为挂起状态)-当I/O完成时-
ApcContext
指针将被推送到端口。然后就可以通过

NTSTATUS
NTAPI
NtRemoveIoCompletion(
    _In_ HANDLE IoCompletionHandle,
    _Out_ PVOID *KeyContext,
    _Out_ PVOID *ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER Timeout
    );
或者使用win32 shell
GetQueuedCompletionStatus

所以,不将数据包发送到端口的解决方案(甚至是与完成端口关联的文件句柄)-设置
ApcContext=0
。win32层以下一种方式执行此操作(伪代码):

BOOL WINAPI SomeWin32Api(
句柄文件句柄,
LPOVERLAPPED LPOVERLAPPED,
LPU重叠_完成_例程LPCompletion例程
)
{
手柄hEvent=lpOverlapped->hEvent;
PVOID ApcContext=lpOverlapped;
如果((ULONG_PTR)hEvent&1)
{
重新解释铸造(hEvent)和=1;
ApcContext=0;
}
NTSTATUS status=SomeIoApi(
文件句柄,
hEvent,
lpCompletionRoutine,//不完全正确,但根据感觉
在这种情况下,
(PIO_状态_块)重叠,…);
}
它在
OVERLAPPED
中检查
hEvent
的低阶位-如果设置-pass 0 in place
ApcContext
,否则将
lpOverlapped
(指向
OVERLAPPED
)作为上下文传递(
ApcContext=lpOverlapped;

注意,nt层让任何
void*
指针作为
ApcContext
传递。但是win32层总是在这里传递指向重叠结构或0的指针。因为这和
GetQueuedCompletionStatus
将此指针返回为
\u Out\LPOVERLAPPED*LPOVERLAPPED
(与
ntremovieoCompletion
比较-返回为
\uuuupvoid*ApcContext

无论如何,这个技巧只会影响具体的单个win32 I/O调用,如果您从重叠(
reinterpret_cast(di->overlapped.hEvent)&=~(0x1);
)延迟重置
hEvent
中的低位,这已经不会有任何效果-0已通过

另外,从一般的角度来看,当我们将文件句柄与完成端口相关联,但不希望在某些调用中使用它时,很少会出现这种情况。通常这是另一个api调用。例如,我们可以创建异步文件句柄,将其与完成端口相关联。并在call
WriteFile
中使用端口通知,但在开始写入之前,我们可以通过
FSCTL\u set\u compression
设置/删除文件压缩。由于文件是异步的,
FSCTL\u SET\u压缩
也可以完成异步,但我们可以阻止此ioctl的完成端口通知,而不是原地等待(在事件中)它完成。对于这种情况,可以使用此技巧

在大多数情况下,应用程序(如果不是具有大量i/o请求的服务器)可以改为手动调用
GetQueuedCompletionStatus
,通过
BindIoCompletionCallback
CreateThreadpoolIo
将回调绑定到文件。作为您创建iocp的结果系统,线程池将
NTSTATUS
NTAPI
NtRemoveIoCompletion(
    _In_ HANDLE IoCompletionHandle,
    _Out_ PVOID *KeyContext,
    _Out_ PVOID *ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER Timeout
    );
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,...);
}