From 8579ba0410125e656a790df46ad856f0a88c2372 Mon Sep 17 00:00:00 2001 From: mingkuang Date: Sun, 1 Oct 2023 19:50:37 +0800 Subject: [PATCH] =?UTF-8?q?*=20Fea=20#60,=20=E6=B7=BB=E5=8A=A0SetFileCompl?= =?UTF-8?q?etionNotificationModes=E3=80=81GetQueuedCompletionStatusEx?= =?UTF-8?q?=E3=80=81NtCancelIoFileEx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ThunksList.md | 7 +++ src/Shared/km.h | 9 ++++ src/Thunks/YY_Thunks.cpp | 2 + src/Thunks/api-ms-win-core-io.hpp | 43 +++++++++++++++++++ .../api-ms-win-core-kernel32-legacy.hpp | 25 +++++++++++ src/Thunks/ntdll.hpp | 36 ++++++++++++++++ .../YY-Thunks.UnitTest.vcxproj | 1 + .../YY-Thunks.UnitTest.vcxproj.filters | 3 ++ .../api-ms-win-core-fibers.UnitTest.cpp | 4 +- .../api-ms-win-core-synch.UnitTest.cpp | 8 ++-- .../api-ms-win-core-threadpool.UnitTest.cpp | 28 ++++++------ src/YY-Thunks.UnitTest/pch.h | 8 ++++ 12 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 src/Thunks/ntdll.hpp diff --git a/ThunksList.md b/ThunksList.md index 43e169b..98e2e7f 100644 --- a/ThunksList.md +++ b/ThunksList.md @@ -290,6 +290,8 @@ | GetFirmwareType | 不存在时,调用NtQuerySystemInformation。 | IsNativeVhdBoot | 不存在时,调用NtQuerySystemInformation。 | RtlCaptureStackBackTrace | 调用ntdll.RtlCaptureStackBackTrace。 +| SetFileCompletionNotificationModes | 不存在时,什么也不做。 +| GetQueuedCompletionStatusEx | 不存在时,调用 GetQueuedCompletionStatus,注意:丢失可警报状态支持。 ## mfplat.dll | 函数 | Fallback @@ -305,6 +307,11 @@ | NetGetAadJoinInformation | 不存在时,始终认为没有加入 Azure AD 帐户 账号。 | NetFreeAadJoinInformation | 不存在时,什么也不做。 +## ntdll.dll +| 函数 | Fallback +| ---- | ----------- +| NtCancelIoFileEx | 不存在时,调用 NtCancelIoFile。注意:将取消此文件的所有IO请求。 + ## ole32.dll | 函数 | Fallback | ---- | ----------- diff --git a/src/Shared/km.h b/src/Shared/km.h index 0479a43..e1f96bc 100644 --- a/src/Shared/km.h +++ b/src/Shared/km.h @@ -5104,6 +5104,15 @@ LdrAddRefDll( IN PVOID BaseAddress ); +EXTERN_C NTSTATUS NTAPI NtCancelIoFileEx(HANDLE handle, IO_STATUS_BLOCK* io, IO_STATUS_BLOCK* io_status); + +EXTERN_C +NTSTATUS +NTAPI +NtCancelIoFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock); + #if defined __cplusplus && !defined _Disallow_YY_KM_Namespace } //namespace YY #endif diff --git a/src/Thunks/YY_Thunks.cpp b/src/Thunks/YY_Thunks.cpp index f0614c9..485d86b 100644 --- a/src/Thunks/YY_Thunks.cpp +++ b/src/Thunks/YY_Thunks.cpp @@ -1,5 +1,6 @@ // 忽略非标准的 0 数组警告。 #pragma warning(disable:4200) +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define _YY_APPLY_TO_LATE_BOUND_MODULES(_APPLY) \ _APPLY(ntdll, "ntdll" , USING_UNSAFE_LOAD ) \ @@ -57,6 +58,7 @@ _APPLY(LdrLoadDll, ntdll ) \ _APPLY(RtlDllShutdownInProgress, ntdll ) \ _APPLY(RtlCutoverTimeToSystemTime, ntdll ) \ + _APPLY(NtCancelIoFile, ntdll ) \ _APPLY(AddDllDirectory, kernel32 ) \ _APPLY(SystemFunction036, advapi32 ) diff --git a/src/Thunks/api-ms-win-core-io.hpp b/src/Thunks/api-ms-win-core-io.hpp index 0479596..8745b9d 100644 --- a/src/Thunks/api-ms-win-core-io.hpp +++ b/src/Thunks/api-ms-win-core-io.hpp @@ -52,6 +52,49 @@ namespace YY return FALSE; } #endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN6) + + // 最低受支持的客户端 Windows Vista [桌面应用 | UWP 应用] + // 最低受支持的服务器 Windows Server 2008[桌面应用 | UWP 应用] + __DEFINE_THUNK( + kernel32, + 24, + BOOL, + WINAPI, + GetQueuedCompletionStatusEx, + _In_ HANDLE CompletionPort, + _Out_writes_to_(ulCount,*ulNumEntriesRemoved) LPOVERLAPPED_ENTRY lpCompletionPortEntries, + _In_ ULONG ulCount, + _Out_ PULONG ulNumEntriesRemoved, + _In_ DWORD dwMilliseconds, + _In_ BOOL fAlertable + ) + { + if (const auto _pfnGetQueuedCompletionStatusEx = try_get_GetQueuedCompletionStatusEx()) + { + return _pfnGetQueuedCompletionStatusEx(CompletionPort, lpCompletionPortEntries, ulCount, ulNumEntriesRemoved, dwMilliseconds, fAlertable); + } + + if (ulCount == 0 || lpCompletionPortEntries == nullptr || ulNumEntriesRemoved == nullptr) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + *ulNumEntriesRemoved = 0; + + auto& _Entry = lpCompletionPortEntries[0]; + + // TODO: 已知问题:可警报状态丢失!(fAlertable) + 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 diff --git a/src/Thunks/api-ms-win-core-kernel32-legacy.hpp b/src/Thunks/api-ms-win-core-kernel32-legacy.hpp index 0a21278..2ac9f0f 100644 --- a/src/Thunks/api-ms-win-core-kernel32-legacy.hpp +++ b/src/Thunks/api-ms-win-core-kernel32-legacy.hpp @@ -394,6 +394,31 @@ namespace YY } #endif +#if (YY_Thunks_Support_Version < NTDDI_WIN6) + + // 支持的最低客户端 Windows Vista [桌面应用程序 |UWP 应用] + // 支持的最低服务器 Windows Server 2008 [桌面应用程序 |UWP 应用] + __DEFINE_THUNK( + kernel32, + 8, + BOOL, + WINAPI, + SetFileCompletionNotificationModes, + _In_ HANDLE FileHandle, + _In_ UCHAR Flags + ) + { + if (const auto _pfnSetFileCompletionNotificationModes = try_get_SetFileCompletionNotificationModes()) + { + return _pfnSetFileCompletionNotificationModes(FileHandle, Flags); + } + + // 初步看起来没有什么的,只是会降低完成端口的效率。 + // 至少需要 Vista才支持 FileIoCompletionNotificationInformation + // 只能假定先返回成功。 + return TRUE; + } +#endif }//namespace Thunks } //namespace YY \ No newline at end of file diff --git a/src/Thunks/ntdll.hpp b/src/Thunks/ntdll.hpp new file mode 100644 index 0000000..20a1df3 --- /dev/null +++ b/src/Thunks/ntdll.hpp @@ -0,0 +1,36 @@ +namespace YY +{ + namespace Thunks + { +#if (YY_Thunks_Support_Version < NTDDI_WIN6) + + // ֵ֧Ŀͻ Windows Vista [Ӧ|UWP Ӧ] + // ֵ֧ķ Windows Server 2008[Ӧ | UWP Ӧ] + __DEFINE_THUNK( + ntdll, + 12, + NTSTATUS, + NTAPI, + NtCancelIoFileEx, + HANDLE handle, + IO_STATUS_BLOCK* io, + IO_STATUS_BLOCK* io_status + ) + { + if (const auto _pfnNtCancelIoFileEx = try_get_NtCancelIoFileEx()) + { + return _pfnNtCancelIoFileEx(handle, io, io_status); + } + + // 㣬еĵ + if (const auto _pfnNtCancelIoFile = try_get_NtCancelIoFile()) + { + return _pfnNtCancelIoFile(handle, io_status); + } + + // ˵Ӧߵ + return STATUS_NOT_SUPPORTED; + } +#endif + } +} diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 9c9cd42..d1cf6ed 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj @@ -203,6 +203,7 @@ + diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters index 251c101..ab0b7ce 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters @@ -227,6 +227,9 @@ Thunks + + Thunks + diff --git a/src/YY-Thunks.UnitTest/api-ms-win-core-fibers.UnitTest.cpp b/src/YY-Thunks.UnitTest/api-ms-win-core-fibers.UnitTest.cpp index 4685658..7be4464 100644 --- a/src/YY-Thunks.UnitTest/api-ms-win-core-fibers.UnitTest.cpp +++ b/src/YY-Thunks.UnitTest/api-ms-win-core-fibers.UnitTest.cpp @@ -65,7 +65,7 @@ namespace api_ms_win_core_fibers for (auto hHandle : hHandles) { - Assert::AreEqual(WaitForSingleObject(hHandle, 1000), WAIT_OBJECT_0); + Assert::AreEqual(WaitForSingleObject(hHandle, 1000), (DWORD)WAIT_OBJECT_0); CloseHandle(hHandle); } @@ -158,7 +158,7 @@ namespace api_ms_win_core_fibers for (auto hHandle : hHandles) { - Assert::AreEqual(WaitForSingleObject(hHandle, 1000), WAIT_OBJECT_0); + Assert::AreEqual(WaitForSingleObject(hHandle, 1000), (DWORD)WAIT_OBJECT_0); CloseHandle(hHandle); } diff --git a/src/YY-Thunks.UnitTest/api-ms-win-core-synch.UnitTest.cpp b/src/YY-Thunks.UnitTest/api-ms-win-core-synch.UnitTest.cpp index 6109796..5f71061 100644 --- a/src/YY-Thunks.UnitTest/api-ms-win-core-synch.UnitTest.cpp +++ b/src/YY-Thunks.UnitTest/api-ms-win-core-synch.UnitTest.cpp @@ -98,7 +98,7 @@ namespace api_ms_win_core_synch for (auto hThreadHandle : hThreadHandles) { - Assert::AreEqual(WaitForSingleObject(hThreadHandle, 500), WAIT_OBJECT_0); + Assert::AreEqual(WaitForSingleObject(hThreadHandle, 500), (DWORD)WAIT_OBJECT_0); } Assert::AreEqual(100l, (long)Data.RunCount); @@ -157,7 +157,7 @@ namespace api_ms_win_core_synch for (auto hThreadHandle : hThreadHandles) { - Assert::AreEqual(WaitForSingleObject(hThreadHandle, 500), WAIT_OBJECT_0); + Assert::AreEqual(WaitForSingleObject(hThreadHandle, 500), (DWORD)WAIT_OBJECT_0); } Assert::AreEqual(100l, (long)Data.RunCount); @@ -205,7 +205,7 @@ namespace api_ms_win_core_synch auto _nRet = WaitForSingleObject(_hThreadHandle, 5 * 1000); ::ReleaseSRWLockExclusive(&_SRWLock); - Assert::AreEqual(WAIT_OBJECT_0, _nRet); + Assert::AreEqual((DWORD)WAIT_OBJECT_0, _nRet); DWORD _uCode = -1; GetExitCodeThread(_hThreadHandle, &_uCode); @@ -237,7 +237,7 @@ namespace api_ms_win_core_synch auto _nRet = WaitForSingleObject(_hThreadHandle, 5 * 1000); - Assert::AreEqual(WAIT_OBJECT_0, _nRet); + Assert::AreEqual((DWORD)WAIT_OBJECT_0, _nRet); DWORD _uCode = -1; GetExitCodeThread(_hThreadHandle, &_uCode); diff --git a/src/YY-Thunks.UnitTest/api-ms-win-core-threadpool.UnitTest.cpp b/src/YY-Thunks.UnitTest/api-ms-win-core-threadpool.UnitTest.cpp index 75ea608..0ebbc96 100644 --- a/src/YY-Thunks.UnitTest/api-ms-win-core-threadpool.UnitTest.cpp +++ b/src/YY-Thunks.UnitTest/api-ms-win-core-threadpool.UnitTest.cpp @@ -146,7 +146,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(h); - Assert::AreEqual(WaitForSingleObject(h, 2 * 1000), WAIT_OBJECT_0, L"2秒内必须完成,这是预期。"); + Assert::AreEqual(WaitForSingleObject(h, 2 * 1000), (DWORD)WAIT_OBJECT_0, L"2秒内必须完成,这是预期。"); CloseHandle(h); @@ -165,7 +165,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(h); - Assert::AreEqual(WaitForSingleObject(h, 6 * 1000), WAIT_OBJECT_0, L"6秒内必须完成,这是预期。"); + Assert::AreEqual(WaitForSingleObject(h, 6 * 1000), (DWORD)WAIT_OBJECT_0, L"6秒内必须完成,这是预期。"); CloseHandle(h); @@ -446,7 +446,7 @@ namespace api_ms_win_core_threadpool ::SetThreadpoolTimer(pTimer, &ftDueTime, 1'500, 0); - Assert::AreEqual(WaitForSingleObject(Data.hEvent, 1 * 1000), WAIT_OBJECT_0, L"1秒内必须完成,这是预期。"); + Assert::AreEqual(WaitForSingleObject(Data.hEvent, 1 * 1000), (DWORD)WAIT_OBJECT_0, L"1秒内必须完成,这是预期。"); auto h = (HANDLE)_beginthreadex(nullptr, 0, [](void* Work) -> unsigned { @@ -457,7 +457,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(h); - Assert::AreEqual(WaitForSingleObject(h, 30 * 1000), WAIT_OBJECT_0, L"6秒内必须完成,这是预期。"); + Assert::AreEqual(WaitForSingleObject(h, 30 * 1000), (DWORD)WAIT_OBJECT_0, L"6秒内必须完成,这是预期。"); CloseHandle(h); Assert::AreEqual(Data.RunCount, 1l); @@ -522,7 +522,7 @@ namespace api_ms_win_core_threadpool ::SubmitThreadpoolWork(Work); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); CloseThreadpoolWork(Work); @@ -550,7 +550,7 @@ namespace api_ms_win_core_threadpool FILETIME ftDueTime = {}; ::SetThreadpoolTimer(pTimer, &ftDueTime, 0, 0); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); ::CloseThreadpoolTimer(pTimer); @@ -612,7 +612,7 @@ namespace api_ms_win_core_threadpool ::SubmitThreadpoolWork(Work); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); CloseThreadpoolWork(Work); @@ -644,7 +644,7 @@ namespace api_ms_win_core_threadpool FILETIME ftDueTime = {}; ::SetThreadpoolTimer(pTimer, &ftDueTime, 0, 0); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); ::CloseThreadpoolTimer(pTimer); @@ -698,7 +698,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(Context); auto& Data = *(UserData*)Context; auto dwWaitResult = WaitForSingleObject(Data.hHandle, 1000); - Assert::AreEqual(dwWaitResult, WAIT_OBJECT_0); + Assert::AreEqual(dwWaitResult, (DWORD)WAIT_OBJECT_0); ::ReleaseMutexWhenCallbackReturns(Instance, Data.hHandle); InterlockedIncrement(&Data.RunCount); @@ -709,7 +709,7 @@ namespace api_ms_win_core_threadpool ::SubmitThreadpoolWork(Work); Sleep(100); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); CloseThreadpoolWork(Work); @@ -728,7 +728,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(Context); auto& Data = *(UserData*)Context; auto dwWaitResult = WaitForSingleObject(Data.hHandle, 1000); - Assert::AreEqual(dwWaitResult, WAIT_OBJECT_0); + Assert::AreEqual(dwWaitResult, (DWORD)WAIT_OBJECT_0); ::ReleaseMutexWhenCallbackReturns(Instance, Data.hHandle); InterlockedIncrement(&Data.RunCount); }, &Data, nullptr); @@ -739,7 +739,7 @@ namespace api_ms_win_core_threadpool ::SetThreadpoolTimer(pTimer, &ftDueTime, 0, 0); Sleep(100); - Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), WAIT_OBJECT_0, L"5秒内句柄必须有信号"); + Assert::AreEqual(WaitForSingleObject(Data.hHandle, 5 * 1000), (DWORD)WAIT_OBJECT_0, L"5秒内句柄必须有信号"); Assert::AreEqual(Data.RunCount, 1l); ::CloseThreadpoolTimer(pTimer); @@ -1040,7 +1040,7 @@ namespace api_ms_win_core_threadpool _In_ TP_WAIT_RESULT WaitResult ) { - Assert::AreEqual(WaitResult, WAIT_OBJECT_0); + Assert::AreEqual(WaitResult, (DWORD)WAIT_OBJECT_0); Assert::IsNotNull(Context); @@ -1353,7 +1353,7 @@ namespace api_ms_win_core_threadpool Assert::IsNotNull(h); - Assert::AreEqual(WaitForSingleObject(h, 10 * 1000), WAIT_OBJECT_0, L"10秒内必须完成,这是预期。"); + Assert::AreEqual(WaitForSingleObject(h, 10 * 1000), (DWORD)WAIT_OBJECT_0, L"10秒内必须完成,这是预期。"); diff --git a/src/YY-Thunks.UnitTest/pch.h b/src/YY-Thunks.UnitTest/pch.h index c015654..69426a0 100644 --- a/src/YY-Thunks.UnitTest/pch.h +++ b/src/YY-Thunks.UnitTest/pch.h @@ -7,10 +7,18 @@ #ifndef PCH_H #define PCH_H +#include + // 添加要在此处预编译的标头 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define UMDF_USING_NTSTATUS +#define _Disallow_YY_KM_Namespace +#include + #include + #include + #include "CppUnitTest.h" #include