diff --git a/ThunksList.md b/ThunksList.md index 4bcc0c9..6db438d 100644 --- a/ThunksList.md +++ b/ThunksList.md @@ -296,7 +296,7 @@ | IsNativeVhdBoot | 不存在时,调用NtQuerySystemInformation。 | RtlCaptureStackBackTrace | 调用ntdll.RtlCaptureStackBackTrace。 | SetFileCompletionNotificationModes | 不存在时,什么也不做。 -| GetQueuedCompletionStatusEx | 不存在时,调用 GetQueuedCompletionStatus,注意:丢失可警报状态支持。 +| GetQueuedCompletionStatusEx | 不存在时,调用 GetQueuedCompletionStatus。 | FindFirstFileEx(W/A) | Windows XP、Vista兼容 FIND_FIRST_EX_LARGE_FETCH、FindExInfoStandard参数。 ## mfplat.dll diff --git a/src/Thunks/api-ms-win-core-io.hpp b/src/Thunks/api-ms-win-core-io.hpp index 8745b9d..5452bc0 100644 --- a/src/Thunks/api-ms-win-core-io.hpp +++ b/src/Thunks/api-ms-win-core-io.hpp @@ -86,15 +86,82 @@ namespace YY auto& _Entry = lpCompletionPortEntries[0]; - // TODO: 已知问题:可警报状态丢失!(fAlertable) - auto _bRet = GetQueuedCompletionStatus(CompletionPort, &_Entry.dwNumberOfBytesTransferred, &_Entry.lpCompletionKey, &_Entry.lpOverlapped, dwMilliseconds); - if (_bRet) - { - *ulNumEntriesRemoved = 1; - } - return _bRet; + if (fAlertable) + { + // 使用 WaitForSingleObjectEx 进行等待触发 APC + auto _uStartTick = GetTickCount(); + for (;;) + { + const auto _uResult = WaitForSingleObjectEx(CompletionPort, dwMilliseconds, TRUE); + if (_uResult == WAIT_OBJECT_0) + { + // 完成端口有数据了 + auto _bRet = GetQueuedCompletionStatus(CompletionPort, &_Entry.dwNumberOfBytesTransferred, &_Entry.lpCompletionKey, &_Entry.lpOverlapped, 0); + if (_bRet) + { + *ulNumEntriesRemoved = 1; + break; + } + + if (GetLastError() != WAIT_TIMEOUT) + { + return FALSE; + } + + // 无限等待时无脑继续等即可。 + if (dwMilliseconds == INFINITE) + { + continue; + } + + // 计算剩余等待时间,如果剩余等待时间归零则返回 + const DWORD _uTickSpan = GetTickCount() - _uStartTick; + if (_uTickSpan >= dwMilliseconds) + { + SetLastError(WAIT_TIMEOUT); + return FALSE; + } + dwMilliseconds -= _uTickSpan; + _uStartTick += _uTickSpan; + continue; + } + else if (_uResult == WAIT_IO_COMPLETION || _uResult == WAIT_TIMEOUT) + { + // 很奇怪,微软原版遇到 APC唤醒直接会设置 LastError WAIT_IO_COMPLETION + // 遇到超时,LastError WAIT_TIMEOUT(注意不是预期的 ERROR_TIMEOUT)不知道是故意还是有意。 + SetLastError(_uResult); + return FALSE; + } + else if (_uResult == WAIT_ABANDONED) + { + SetLastError(ERROR_ABANDONED_WAIT_0); + return FALSE; + } + else if (_uResult == WAIT_FAILED) + { + // LastError + return FALSE; + } + else + { + // LastError ??? + return FALSE; + } + } + + return TRUE; + } + else + { + auto _bRet = GetQueuedCompletionStatus(CompletionPort, &_Entry.dwNumberOfBytesTransferred, &_Entry.lpCompletionKey, &_Entry.lpOverlapped, dwMilliseconds); + if (_bRet) + { + *ulNumEntriesRemoved = 1; + } + return _bRet; + } } #endif }//namespace Thunks -} //namespace YY \ No newline at end of file +} //namespace YY