From e6c745c5348f1166c3e018145b56aedde1711cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Selim=20SEL=C3=87UK=20=28int03=29?= Date: Tue, 22 Sep 2026 13:32:38 +0300 Subject: [PATCH 1/2] Fix silent startup deadlock on Windows 11 due to premature FreeLibrary In CPowerManager::Start, powrprof.dll was loaded, PowerRegisterSuspendResumeNotification was called, and ::FreeLibrary(powrprof) was immediately invoked. On modern Windows 11 builds, calling FreeLibrary on powrprof.dll while a suspend/resume notification callback is active causes powrprof to invoke UMPDC.dll, which executes WaitForSingleObjectEx during DLL unload under the loader lock, deadlocking the main UI thread synchronously during CMainFrame::OnCreate. As a result, Ditto hangs permanently on startup without creating its systray icon or showing any windows. In addition, PowerUnregisterSuspendResumeNotification takes HPOWERNOTIFY by value, not by pointer. This fix: 1. Retains HMODULE m_hPowrProf as a member of CPowerManager across the application lifetime. 2. Keeps powrprof.dll loaded while notifications are registered. 3. Corrects the function signature and invocation for PowerUnregisterSuspendResumeNotification. 4. Safely unregisters notifications and frees powrprof.dll only during CPowerManager::Close(). --- src/PowerManager.cpp | 39 ++++++++++++++++++++++----------------- src/PowerManager.h | 3 ++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/PowerManager.cpp b/src/PowerManager.cpp index b050ae25..45bb4500 100644 --- a/src/PowerManager.cpp +++ b/src/PowerManager.cpp @@ -30,11 +30,13 @@ ULONG PowerChanged(PVOID Context, ULONG Type, PVOID Setting) CPowerManager::CPowerManager() { m_registrationHandle = 0; + m_hPowrProf = NULL; } CPowerManager::~CPowerManager(void) { + Close(); } @@ -42,37 +44,40 @@ void CPowerManager::Start(HWND hWnd) { s_notifyHwnd = hWnd; - HMODULE powrprof = LoadLibrary( _T("powrprof.dll") ); - if( powrprof != NULL ) + if (m_hPowrProf == NULL) { - DWORD (_stdcall*PowerRegisterSuspendResumeNotification)(_In_ DWORD,_In_ HANDLE,_Out_ PHPOWERNOTIFY); - PowerRegisterSuspendResumeNotification = (DWORD(_stdcall*)(_In_ DWORD,_In_ HANDLE,_Out_ PHPOWERNOTIFY))GetProcAddress(powrprof, "PowerRegisterSuspendResumeNotification"); - if(PowerRegisterSuspendResumeNotification) + m_hPowrProf = LoadLibrary(_T("powrprof.dll")); + } + + if (m_hPowrProf != NULL && m_registrationHandle == 0) + { + DWORD (_stdcall*PowerRegisterSuspendResumeNotification)(_In_ DWORD, _In_ HANDLE, _Out_ PHPOWERNOTIFY); + PowerRegisterSuspendResumeNotification = (DWORD(_stdcall*)(_In_ DWORD, _In_ HANDLE, _Out_ PHPOWERNOTIFY))GetProcAddress(m_hPowrProf, "PowerRegisterSuspendResumeNotification"); + if (PowerRegisterSuspendResumeNotification) { static _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS testCallback = {PowerChanged, nullptr}; PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, &testCallback, &m_registrationHandle); } - - ::FreeLibrary(powrprof); } } void CPowerManager::Close() { - if(m_registrationHandle != 0) + if (m_hPowrProf != NULL) { - HMODULE powrprof = LoadLibrary( _T("powrprof.dll") ); - if( powrprof != NULL ) + if (m_registrationHandle != 0) { - DWORD (_stdcall*PowerUnregisterSuspendResumeNotification)(_Inout_ PHPOWERNOTIFY); - PowerUnregisterSuspendResumeNotification = (DWORD(_stdcall*)(_Inout_ PHPOWERNOTIFY))GetProcAddress(powrprof, "PowerUnregisterSuspendResumeNotification"); - if(PowerUnregisterSuspendResumeNotification) + DWORD (_stdcall*PowerUnregisterSuspendResumeNotification)(_Inout_ HPOWERNOTIFY); + PowerUnregisterSuspendResumeNotification = (DWORD(_stdcall*)(_Inout_ HPOWERNOTIFY))GetProcAddress(m_hPowrProf, "PowerUnregisterSuspendResumeNotification"); + if (PowerUnregisterSuspendResumeNotification) { - PowerUnregisterSuspendResumeNotification(&m_registrationHandle); + PowerUnregisterSuspendResumeNotification(m_registrationHandle); } - - ::FreeLibrary(powrprof); + m_registrationHandle = 0; } + + ::FreeLibrary(m_hPowrProf); + m_hPowrProf = NULL; } -} \ No newline at end of file +} diff --git a/src/PowerManager.h b/src/PowerManager.h index a66f7224..32aca497 100644 --- a/src/PowerManager.h +++ b/src/PowerManager.h @@ -26,9 +26,10 @@ class CPowerManager ~CPowerManager(void); void Start(HWND hWnd); - void CPowerManager::Close(); + void Close(); protected: HPOWERNOTIFY m_registrationHandle; + HMODULE m_hPowrProf; }; From 49d525469d8d3dd6c6a99f15d1c214ea81cf1f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Selim=20SEL=C3=87UK=20=28int03=29?= Date: Tue, 22 Sep 2026 14:17:52 +0300 Subject: [PATCH 2/2] Address review feedback: only unload powrprof if unregister succeeds --- src/PowerManager.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/PowerManager.cpp b/src/PowerManager.cpp index 45bb4500..f9dfcb47 100644 --- a/src/PowerManager.cpp +++ b/src/PowerManager.cpp @@ -72,12 +72,18 @@ void CPowerManager::Close() PowerUnregisterSuspendResumeNotification = (DWORD(_stdcall*)(_Inout_ HPOWERNOTIFY))GetProcAddress(m_hPowrProf, "PowerUnregisterSuspendResumeNotification"); if (PowerUnregisterSuspendResumeNotification) { - PowerUnregisterSuspendResumeNotification(m_registrationHandle); + DWORD ret = PowerUnregisterSuspendResumeNotification(m_registrationHandle); + if (ret == ERROR_SUCCESS) + { + m_registrationHandle = 0; + } } - m_registrationHandle = 0; } - ::FreeLibrary(m_hPowrProf); - m_hPowrProf = NULL; + if (m_registrationHandle == 0) + { + ::FreeLibrary(m_hPowrProf); + m_hPowrProf = NULL; + } } }