diff --git a/ThunksList.md b/ThunksList.md index 1f54a27..6ab3320 100644 --- a/ThunksList.md +++ b/ThunksList.md @@ -308,6 +308,8 @@ | ---- | ----------- | PowerDeterminePlatformRole | 不存在时,返回PlatformRoleDesktop。 | PowerDeterminePlatformRoleEx | 不存在时,调用PlatformRoleDesktop。 +| PowerRegisterSuspendResumeNotification | 不存在时,使用窗口模拟。 +| PowerUnregisterSuspendResumeNotification | 内部实现。 ## psapi.dll | 函数 | Fallback @@ -364,6 +366,8 @@ | GetSystemMetricsForDpi | 不存在时,调用GetSystemMetrics。 | AdjustWindowRectExForDpi | 不存在时,调用AdjustWindowRectEx。 | SystemParametersInfoForDpi | 不存在时,调用SystemParametersInfoW。 +| RegisterSuspendResumeNotification | 不存在时,使用窗口模拟。 +| UnregisterSuspendResumeNotification | 不存在时,内部实现。 ## userenv.dll | 函数 | Fallback diff --git a/src/Thunks/ThreadRunner.h b/src/Thunks/ThreadRunner.h new file mode 100644 index 0000000..490bf51 --- /dev/null +++ b/src/Thunks/ThreadRunner.h @@ -0,0 +1,437 @@ +#pragma once + +namespace YY +{ + namespace Thunks + { + namespace internal + { + typedef BOOL(WINAPI TaskCallback)( + struct TaskItem* _pWork, + UINT _uMsg, + WPARAM _wParam, + LPARAM _lParam + ); + + struct TaskItem + { + TaskItem* pNext; + + TaskCallback* pfnCallback; + }; + + constexpr auto WM_RUNNER_CALL = WM_USER + 100; + + struct ThreadRunner + { + TaskItem* pCallbackList; + volatile TaskItem* pPendingCallbackList; + volatile HWND hWnd; + volatile ULONG uRef; + + static LRESULT WINAPI WindowProcW( + HWND _hWnd, + UINT _uMsg, + WPARAM _wParam, + LPARAM _lParam + ) + { + if (_uMsg == WM_CLOSE) + { + auto _pData = (ThreadRunner*)GetWindowLongPtrW(_hWnd, GWLP_USERDATA); + if (_pData) + { + if (!_pData->TryClean()) + { + if (_wParam == 0) + { + return 0; + } + else + { + // ʾǿʱȻͷʧܣΪ˱ + // ǽֹͣڡ + _pData->hWnd = NULL; + } + } + } + DestroyWindow(_hWnd); + return 0; + } + else if (_uMsg == WM_DESTROY) + { + PostQuitMessage(0); + } + else + { + auto _pData = (ThreadRunner*)GetWindowLongPtrW(_hWnd, GWLP_USERDATA); + if (_pData && (_pData->pCallbackList || _pData->pPendingCallbackList)) + { + const auto _hProcessHeap = ((TEB*)NtCurrentTeb())->ProcessEnvironmentBlock->ProcessHeap; + + // CallBack + TaskItem* _pLastCallbackPrev = (TaskItem*)&_pData->pCallbackList; + for (auto _pCallbackPrev = _pLastCallbackPrev; _pCallbackPrev;) + { + if (!_pCallbackPrev->pNext) + break; + + auto _pNext = _pCallbackPrev->pNext; + if (!_pNext) + break; + + if (auto _pfnCallback = _pNext->pfnCallback) + { + _pLastCallbackPrev = _pNext; + _pfnCallback(_pNext, _uMsg, _wParam, _lParam); + } + else + { + _pCallbackPrev->pNext = _pNext->pNext; + // ҪƳ + HeapFree(_hProcessHeap, 0, _pNext); + } + } + + // ϲ pPendingCallbackList pCallbackList + auto _pPendingCallbackList = (TaskItem*)InterlockedExchangePointer((PVOID*)&_pData->pPendingCallbackList, NULL); + if (_pPendingCallbackList) + { + // ˳ߵΪ˳ǵ + auto _pPendingCallbackListFirst = _pPendingCallbackList; + for (; _pPendingCallbackList->pNext; _pPendingCallbackList = _pPendingCallbackList->pNext) + { + _pPendingCallbackListFirst->pNext = _pPendingCallbackList->pNext; + _pPendingCallbackListFirst = _pPendingCallbackList->pNext; + } + _pPendingCallbackList->pNext = nullptr; + + _pLastCallbackPrev->pNext = _pPendingCallbackListFirst; + + // ʼ CallbackԪ + for (auto _pCallbackPrev = _pLastCallbackPrev; _pCallbackPrev;) + { + if (!_pCallbackPrev->pNext) + break; + + auto _pNext = _pCallbackPrev->pNext; + if (!_pNext) + break; + + if (auto _pfnCallback = _pNext->pfnCallback) + { + _pLastCallbackPrev = _pNext; + _pfnCallback(_pNext, _uMsg, _wParam, _lParam); + } + else + { + _pCallbackPrev->pNext = _pNext->pNext; + // ҪƳ + HeapFree(_hProcessHeap, 0, _pNext); + } + } + } + + + } + } + return ::DefWindowProcW(_hWnd, _uMsg, _wParam, _lParam); + } + + bool AddRef() + { + auto _uCurrentRef = uRef; + + for (;;) + { + if (_uCurrentRef == MAXUINT32) + { + SwitchToThread(); + _uCurrentRef = uRef; + continue; + } + else if (_uCurrentRef == 0) + { + // һΣԼ + const auto _uLastRef = InterlockedCompareExchange(&uRef, MAXUINT32, 0); + if (_uLastRef != 0) + { + _uCurrentRef = _uLastRef; + continue; + } + + struct ThreadInfo + { + HANDLE hEvent; + ThreadRunner* pWork; + }; + + LSTATUS _lStatus = ERROR_SUCCESS; + ThreadInfo _Info = { NULL, this }; + + do + { + _Info.hEvent = CreateEventW(nullptr, FALSE, FALSE, NULL); + if (!_Info.hEvent) + { + _lStatus = GetLastError(); + break; + } + + // ɹ ʼ + auto _hThread = CreateThread( + nullptr, + 0, + [](LPVOID lpParameter) -> DWORD + { + auto& _Info = *(ThreadInfo*)lpParameter; + + static bool bRegister = false; + if (!bRegister) + { + WNDCLASSW wc = {}; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = &DefWindowProcW; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = NULL; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = L"YY_Thunks_Work_Wnd"; + + if (RegisterClassW(&wc) == FALSE && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + return FALSE; + bRegister = true; + } + + auto _hWnd = CreateWindowExW(WS_EX_NOACTIVATE, L"YY_Thunks_Work_Wnd", nullptr, WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!_hWnd) + return GetLastError(); + + _Info.pWork->hWnd = _hWnd; + + SetWindowLongPtrW(_hWnd, GWLP_USERDATA, (LONG_PTR)_Info.pWork); + SetWindowLongPtrW(_hWnd, GWLP_WNDPROC, (LONG_PTR)&ThreadRunner::WindowProcW); + + SetEvent(_Info.hEvent); + + MSG _Msg; + + for (;;) + { + auto _bRet = GetMessageW(&_Msg, NULL, 0, 0); + if (_bRet == -1) + continue; + if (!_bRet) + break; + + TranslateMessage(&_Msg); + DispatchMessageW(&_Msg); + } + + return 0; + }, + &_Info, + 0, + nullptr); + + if (!_hThread) + { + _lStatus = GetLastError(); + break; + } + + HANDLE _Handles[] = { _Info.hEvent, _hThread }; + + const auto _uResult = WaitForMultipleObjects(_countof(_Handles), _Handles, FALSE, 15 * 1000); + + switch (_uResult) + { + case WAIT_OBJECT_0: + // ɹʼ + break; + case WAIT_OBJECT_0 + 1: + // ʧ + _lStatus = ERROR_FUNCTION_FAILED; + GetExitCodeThread(_hThread, (DWORD*)&_lStatus); + break; + default: + _lStatus = ERROR_FUNCTION_FAILED; + break; + } + + if(_hThread) + CloseHandle(_hThread); + + } while (false); + + if (_Info.hEvent) + { + CloseHandle(_Info.hEvent); + } + + if (_lStatus == ERROR_SUCCESS) + { + // ɹڶһ + InterlockedExchange(&uRef, 2); + break; + } + else + { + // ʧ + InterlockedExchange(&uRef, 0); + return false; + } + } + else + { + // һ + const auto _uLastRef = InterlockedCompareExchange(&uRef, _uCurrentRef + 1, _uCurrentRef); + if (_uLastRef != _uCurrentRef) + { + _uCurrentRef = _uLastRef; + continue; + } + break; + } + } + + return true; + } + + void Release() + { + // + auto _uCurrentRef = uRef; + + for (;;) + { + if (_uCurrentRef == MAXUINT32) + { + // ѾԺ + SwitchToThread(); + _uCurrentRef = uRef; + } + else if (_uCurrentRef == 0 || _uCurrentRef == 1) + { + // ǰûã + break; + } + else + { + // ɹ֪ͨ߳ͷš + if (InterlockedDecrement(&uRef) == 1) + { + // ù 1첽֪ͨڳ˳ + PostMessageW(hWnd, WM_CLOSE, 0, 0); + } + break; + } + } + } + + bool AddTask(TaskItem* _pWork) + { + if (_pWork == nullptr || _pWork->pfnCallback == nullptr) + return false; + + if (!AddRef()) + return nullptr; + + // ӵ pPendingCallbackListȻ̻߳Ὣϲ pCallbackList + auto _pLast = (TaskItem*)pPendingCallbackList; + + for (;;) + { + _pWork->pNext = _pLast; + + auto _pLastLast = (TaskItem*)InterlockedCompareExchangePointer((PVOID*)&pPendingCallbackList, _pWork, _pLast); + + if (_pLastLast == _pLast) + { + break; + } + _pLast = _pLastLast; + } + + return true; + } + + LSTATUS RemoveTask(TaskItem* _pWork) + { + if(!_pWork) + return ERROR_INVALID_PARAMETER; + + // CallbackΪ null ʺϵʱԶɾ + if (!InterlockedExchangePointer((PVOID*)&_pWork->pfnCallback, nullptr)) + return ERROR_INVALID_PARAMETER; + + Release(); + return ERROR_SUCCESS; + } + + bool TryClean() + { + const auto _uLastRef = InterlockedCompareExchange(&uRef, MAXUINT32, 1); + if (_uLastRef != 1) + { + // ѾӣԲҪɾˡ + return false; + } + // ɹһЩؼ + auto _hWnd = hWnd; + hWnd = NULL; + auto _pCallbackList = (TaskItem*)InterlockedExchangePointer((PVOID*)&pCallbackList, nullptr); + auto _pPendingCallbackList = (TaskItem*)InterlockedExchangePointer((PVOID*)&pPendingCallbackList, nullptr); + SetWindowLongPtrW(_hWnd, GWLP_USERDATA, NULL); + // + InterlockedExchange(&uRef, 0); + + const auto _hProcessHeap = ((TEB*)NtCurrentTeb())->ProcessEnvironmentBlock->ProcessHeap; + + // ɾListڴ + for (; _pCallbackList; ) + { + auto _pNext = _pCallbackList->pNext; + HeapFree(_hProcessHeap, 0, _pCallbackList); + _pCallbackList = _pNext; + } + + for (; _pPendingCallbackList; ) + { + auto _pNext = _pPendingCallbackList->pNext; + HeapFree(_hProcessHeap, 0, _pPendingCallbackList); + _pPendingCallbackList = _pNext; + } + + return true; + } + }; + + static ThreadRunner* __fastcall GetGlobalThreadRunner() + { + static ThreadRunner g_YYWork; + static LONG g_RunOnce; + + if (g_RunOnce == 0) + { + if (!InterlockedBitTestAndSet(&g_RunOnce, 0)) + { + atexit( + []() + { + if (auto _hWnd = GetGlobalThreadRunner()->hWnd) + { + SendMessageW(_hWnd, WM_CLOSE, 1, 0); + } + }); + } + } + return &g_YYWork; + } + } + } +} diff --git a/src/Thunks/YY_Thunks.cpp b/src/Thunks/YY_Thunks.cpp index 9159e17..07261a1 100644 --- a/src/Thunks/YY_Thunks.cpp +++ b/src/Thunks/YY_Thunks.cpp @@ -339,6 +339,8 @@ namespace YY } //namespace YY +#include "ThreadRunner.h" + //导入实际的实现 #define YY_Thunks_Implemented #define __DEFINE_THUNK(_MODULE, _SIZE, _RETURN_, _CONVENTION_, _FUNCTION, ...) \ diff --git a/src/Thunks/api-ms-win-power-base.hpp b/src/Thunks/api-ms-win-power-base.hpp index 8b09122..cbe3cdc 100644 --- a/src/Thunks/api-ms-win-power-base.hpp +++ b/src/Thunks/api-ms-win-power-base.hpp @@ -10,6 +10,87 @@ namespace YY { namespace Thunks { +#if (YY_Thunks_Support_Version < NTDDI_WIN8) && defined(YY_Thunks_Implemented) + + static + DWORD + WINAPI + PowerRegisterSuspendResumeNotificationDownlevel( + _In_ DWORD _fFlags, + _In_ HANDLE _hRecipient, + _Out_ PHPOWERNOTIFY _phRegistrationHandle + ) + { + if (!_phRegistrationHandle) + return ERROR_INVALID_PARAMETER; + *_phRegistrationHandle = NULL; + + const auto _hProcessHeap = ((TEB*)NtCurrentTeb())->ProcessEnvironmentBlock->ProcessHeap; + + + if (DEVICE_NOTIFY_CALLBACK == _fFlags) + { + auto _pDeviceNotifyInfo = (DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS*)_hRecipient; + if(!_pDeviceNotifyInfo) + return ERROR_INVALID_PARAMETER; + + struct DeviceNotifyTaskItem : public internal::TaskItem + { + DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS Parameters; + }; + + auto _pTask = (DeviceNotifyTaskItem*)HeapAlloc(_hProcessHeap, 0, sizeof(DeviceNotifyTaskItem)); + if (!_pTask) + return ERROR_OUTOFMEMORY; + + _pTask->Parameters = *_pDeviceNotifyInfo; + _pTask->pfnCallback = []( + internal::TaskItem* _pWork, + UINT _uMsg, + WPARAM _wParam, + LPARAM _lParam) -> BOOL + { + auto _pWork2 = (DeviceNotifyTaskItem*)_pWork; + + if (WM_POWERBROADCAST == _uMsg) + { + _pWork2->Parameters.Callback(_pWork2->Parameters.Context, _wParam, (PVOID)_lParam); + } + + return TRUE; + }; + + if (internal::GetGlobalThreadRunner()->AddTask(_pTask)) + { + *_phRegistrationHandle = _pTask; + return ERROR_SUCCESS; + } + + HeapFree(_hProcessHeap, 0, _pTask); + return ERROR_OUTOFMEMORY; + } + else + { + return ERROR_INVALID_PARAMETER; + } + } + + static + DWORD + WINAPI + PowerUnregisterSuspendResumeNotificationDownlevel( + _Inout_ HPOWERNOTIFY _hRegistrationHandle + ) + { + if (!_hRegistrationHandle) + return ERROR_INVALID_PARAMETER; + + return internal::GetGlobalThreadRunner()->RemoveTask((internal::TaskItem*)_hRegistrationHandle); + } + +#endif // (YY_Thunks_Support_Version < NTDDI_WIN8) && defined(YY_Thunks_Implemented) + + #if (YY_Thunks_Support_Version < NTDDI_WIN8) // ֵ֧Ŀͻ Windows 8 [Ӧ|UWP Ӧ] @@ -38,5 +119,51 @@ namespace YY } } #endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN8) + + // ֵ֧Ŀͻ Windows 8 [Ӧ] + // ֵ֧ķ Windows Server 2012[Ӧ] + __DEFINE_THUNK( + powrprof, + 12, + DWORD, + WINAPI, + PowerRegisterSuspendResumeNotification, + _In_ DWORD _fFlags, + _In_ HANDLE _hRecipient, + _Out_ PHPOWERNOTIFY _phRegistrationHandle + ) + { + if (auto const _pfnPowerRegisterSuspendResumeNotification = try_get_PowerRegisterSuspendResumeNotification()) + { + return _pfnPowerRegisterSuspendResumeNotification(_fFlags, _hRecipient, _phRegistrationHandle); + } + + return PowerRegisterSuspendResumeNotificationDownlevel(_fFlags, _hRecipient, _phRegistrationHandle); + } +#endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN8) + + // ֵ֧Ŀͻ Windows 8 [Ӧ] + // ֵ֧ķ Windows Server 2012[Ӧ] + __DEFINE_THUNK( + powrprof, + 4, + DWORD, + WINAPI, + PowerUnregisterSuspendResumeNotification, + _Inout_ HPOWERNOTIFY _hRegistrationHandle + ) + { + if (auto const _pfnPowerUnregisterSuspendResumeNotification = try_get_PowerUnregisterSuspendResumeNotification()) + { + return _pfnPowerUnregisterSuspendResumeNotification(_hRegistrationHandle); + } + + return PowerUnregisterSuspendResumeNotificationDownlevel(_hRegistrationHandle); + } +#endif } } diff --git a/src/Thunks/ext-ms-win-ntuser-powermanagement.hpp b/src/Thunks/ext-ms-win-ntuser-powermanagement.hpp new file mode 100644 index 0000000..e4e3c12 --- /dev/null +++ b/src/Thunks/ext-ms-win-ntuser-powermanagement.hpp @@ -0,0 +1,107 @@ +#if (YY_Thunks_Support_Version < NTDDI_WIN8) +#include +#endif + +namespace YY +{ + namespace Thunks + { +#ifndef CONST_DEVICE_NOTIFY_WINDOW_HANDLE +#define CONST_DEVICE_NOTIFY_WINDOW_HANDLE (HPOWERNOTIFY)0x1001 +#endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN8) + + // Windows 8 [Ӧ]Windows Server 2012 [Ӧ] + __DEFINE_THUNK( + user32, + 8, + HPOWERNOTIFY, + WINAPI, + RegisterSuspendResumeNotification, + IN HANDLE _hRecipient, + IN DWORD _fFlags + ) + { + if (const auto _pfnRegisterSuspendResumeNotification = try_get_RegisterSuspendResumeNotification()) + { + return _pfnRegisterSuspendResumeNotification(_hRecipient, _fFlags); + } + + if (DEVICE_NOTIFY_CALLBACK == _fFlags) + { + HPOWERNOTIFY _hRegistrationHandle = nullptr; + + auto _lStatus = PowerRegisterSuspendResumeNotificationDownlevel(DEVICE_NOTIFY_CALLBACK, _hRecipient, &_hRegistrationHandle); + if (_lStatus == ERROR_SUCCESS) + { + return _hRegistrationHandle; + } + else + { + SetLastError(_lStatus); + return nullptr; + } + } + else if (DEVICE_NOTIFY_WINDOW_HANDLE == _fFlags) + { + // Ҫ򴰿ڷ͵Դ֪ͨϰ汾ϵͳ֧ ִ˯ + // ǿȷġǺԲֱӷһdzɡ + UNREFERENCED_PARAMETER(_hRecipient); + + return CONST_DEVICE_NOTIFY_WINDOW_HANDLE; + } + else + { + // ֧ + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + } +#endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN8) + + // Windows 8 [Ӧ]Windows Server 2012 [Ӧ] + __DEFINE_THUNK( + user32, + 4, + BOOL, + WINAPI, + UnregisterSuspendResumeNotification, + IN HPOWERNOTIFY _hHandle + ) + { + if (const auto _pfnUnregisterSuspendResumeNotification = try_get_UnregisterSuspendResumeNotification()) + { + return _pfnUnregisterSuspendResumeNotification(_hHandle); + } + + if (_hHandle == nullptr) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + else if (_hHandle == CONST_DEVICE_NOTIFY_WINDOW_HANDLE) + { + // ԣ DEVICE_NOTIFY_WINDOW_HANDLE ij + return TRUE; + } + else + { + // DEVICE_NOTIFY_CALLBACKע + auto _lStatus = PowerUnregisterSuspendResumeNotificationDownlevel(_hHandle); + if (_lStatus == ERROR_SUCCESS) + { + return TRUE; + } + else + { + SetLastError(_lStatus); + return FALSE; + } + } + } +#endif + } +} diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 7e86768..2de2dd7 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj @@ -161,6 +161,7 @@ + @@ -188,12 +189,14 @@ + + diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters index 76543e5..19e5850 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters @@ -60,6 +60,9 @@ 源文件\UnitTest + + 源文件\UnitTest + @@ -206,6 +209,12 @@ Thunks + + Thunks + + + 源文件 + diff --git a/src/YY-Thunks.UnitTest/api-ms-win-power-base.UnitTest.cpp b/src/YY-Thunks.UnitTest/api-ms-win-power-base.UnitTest.cpp new file mode 100644 index 0000000..06e4943 --- /dev/null +++ b/src/YY-Thunks.UnitTest/api-ms-win-power-base.UnitTest.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "Thunks/api-ms-win-power-base.hpp" + +namespace api_ms_win_power_base +{ + TEST_CLASS(PowerRegisterSuspendResumeNotification) + { + public: + PowerRegisterSuspendResumeNotification() + { + YY::Thunks::aways_null_try_get_PowerRegisterSuspendResumeNotification = true; + YY::Thunks::aways_null_try_get_PowerUnregisterSuspendResumeNotification = true; + } + + TEST_METHOD(创建然后关闭) + { + DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS Parameters = {}; + Parameters.Callback = [](_In_opt_ PVOID Context, + _In_ ULONG Type, + _In_ PVOID Setting) -> ULONG + { + return ERROR_SUCCESS; + }; + + HPOWERNOTIFY RegistrationHandle = nullptr; + + auto _lStatus = ::PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, &Parameters, &RegistrationHandle); + Assert::AreEqual(_lStatus, (DWORD)ERROR_SUCCESS); + Assert::IsNotNull(RegistrationHandle); + + Sleep(500); + + _lStatus = ::PowerUnregisterSuspendResumeNotification(RegistrationHandle); + Assert::AreEqual(_lStatus, (DWORD)ERROR_SUCCESS); + } + }; +}