From 336a7e99f92b768ef55d3c8968ae8eb0c7d74d7d Mon Sep 17 00:00:00 2001 From: ALTaleX <2368730049@qq.com> Date: Sun, 6 Aug 2023 07:28:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E9=83=A8=E5=88=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E8=AF=BB=E5=85=A5=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=B8=B2=E6=9F=93=E9=97=AE=E9=A2=98=EF=BC=8C=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TFMain/DXHelper.cpp | 174 ++++-- TFMain/DXHelper.hpp | 29 +- TFMain/DwmThumbnailAPI.hpp | 126 ++++ TFMain/EffectHelper.hpp | 11 +- TFMain/Hooking.cpp | 259 +++++---- TFMain/Hooking.hpp | 2 +- TFMain/ImmersiveContextMenuPatcher.cpp | 223 +++---- TFMain/ImmersiveContextMenuPatcher.hpp | 1 + TFMain/MenuAnimation.cpp | 551 +++++++++++++++++- TFMain/MenuAnimation.hpp | 15 +- TFMain/MenuHandler.cpp | 771 +++++++++++++------------ TFMain/MenuHandler.hpp | 97 ++-- TFMain/MenuRendering.cpp | 127 +--- TFMain/RegHelper.cpp | 76 +-- TFMain/SymbolResolver.cpp | 22 +- TFMain/TFMain.cpp | 220 +++++++ TFMain/TFMain.hpp | 51 ++ TFMain/TFMain.vcxproj | 7 + TFMain/TFMain.vcxproj.filters | 9 + TFMain/ThemeHelper.hpp | 25 +- TFMain/Utils.hpp | 190 +++--- TFMain/UxThemePatcher.cpp | 203 ++++--- TFMain/UxThemePatcher.hpp | 1 + TFMain/dllmain.cpp | 197 +------ TFMain/dx.h | 1 + TFMain/framework.h | 5 +- TFMain/wil.h | 4 +- 27 files changed, 2147 insertions(+), 1250 deletions(-) create mode 100644 TFMain/DwmThumbnailAPI.hpp create mode 100644 TFMain/TFMain.cpp create mode 100644 TFMain/TFMain.hpp diff --git a/TFMain/DXHelper.cpp b/TFMain/DXHelper.cpp index 671ffca..e9c2884 100644 --- a/TFMain/DXHelper.cpp +++ b/TFMain/DXHelper.cpp @@ -2,15 +2,15 @@ #include "Hooking.hpp" #include "DXHelper.hpp" -namespace TranslucentFlyouts::DXHelper -{ - using namespace std; - using namespace D2D1; +using namespace std; +using namespace wil; +using namespace D2D1; +using namespace TranslucentFlyouts; +using namespace TranslucentFlyouts::DXHelper; - LazyDX::InternalHook LazyDX::g_internalHook{}; -} +LazyDX::InternalHook LazyDX::g_internalHook{}; -BOOL WINAPI TranslucentFlyouts::DXHelper::LazyDX::InternalHook::FreeLibrary( +BOOL WINAPI LazyDX::InternalHook::FreeLibrary( HMODULE hLibModule ) { @@ -50,7 +50,7 @@ BOOL WINAPI TranslucentFlyouts::DXHelper::LazyDX::InternalHook::FreeLibrary( return actualFreeLibrary(hLibModule); } -void TranslucentFlyouts::DXHelper::LazyDX::NotifyDeviceLost() +void LazyDX::NotifyDeviceLost() { auto& dxList{g_internalHook.dxList}; for (auto it = dxList.begin(); it != dxList.end(); it++) @@ -61,7 +61,7 @@ void TranslucentFlyouts::DXHelper::LazyDX::NotifyDeviceLost() } } -TranslucentFlyouts::DXHelper::LazyDX::InternalHook::InternalHook() +LazyDX::InternalHook::InternalHook() { actualFreeLibrary = reinterpret_cast(DetourFindFunction("kernel32.dll", "FreeLibrary")); if (!actualFreeLibrary) @@ -73,13 +73,13 @@ TranslucentFlyouts::DXHelper::LazyDX::InternalHook::InternalHook() StartupHook(); } -TranslucentFlyouts::DXHelper::LazyDX::InternalHook::~InternalHook() +LazyDX::InternalHook::~InternalHook() { ShutdownHook(); actualFreeLibrary = nullptr; } -void TranslucentFlyouts::DXHelper::LazyDX::InternalHook::StartupHook() +void LazyDX::InternalHook::StartupHook() { if (!hooked) { @@ -88,7 +88,7 @@ void TranslucentFlyouts::DXHelper::LazyDX::InternalHook::StartupHook() } } -void TranslucentFlyouts::DXHelper::LazyDX::InternalHook::ShutdownHook() +void LazyDX::InternalHook::ShutdownHook() { if (hooked) { @@ -97,12 +97,12 @@ void TranslucentFlyouts::DXHelper::LazyDX::InternalHook::ShutdownHook() } } -TranslucentFlyouts::DXHelper::LazyDX::LazyDX() +LazyDX::LazyDX() { g_internalHook.dxList.push_back(this); } -TranslucentFlyouts::DXHelper::LazyDX::~LazyDX() +LazyDX::~LazyDX() { auto& dxList{g_internalHook.dxList}; for (auto it = dxList.begin(); it != dxList.end();) @@ -121,13 +121,13 @@ TranslucentFlyouts::DXHelper::LazyDX::~LazyDX() /* ======================================================================================== */ -TranslucentFlyouts::DXHelper::LazyD2D& TranslucentFlyouts::DXHelper::LazyD2D::GetInstance() +LazyD2D& LazyD2D::GetInstance() { static LazyD2D instance{}; return instance; } -bool TranslucentFlyouts::DXHelper::LazyD2D::EnsureInitialized() +bool LazyD2D::EnsureInitialized() { auto& lazyD2D{GetInstance()}; @@ -142,22 +142,28 @@ bool TranslucentFlyouts::DXHelper::LazyD2D::EnsureInitialized() return (lazyD2D.m_dcRT && lazyD2D.m_factory ? true : false); } -TranslucentFlyouts::DXHelper::LazyD2D::LazyD2D() +LazyD2D::LazyD2D() { CreateDeviceIndependentResources(); CreateDeviceResources(); } -void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceIndependentResources() +LazyD2D::~LazyD2D() +{ + DestroyDeviceIndependentResources(); + DestroyDeviceResources(); +} + +void LazyD2D::CreateDeviceIndependentResources() { try { - wil::com_ptr factory{nullptr}; + com_ptr factory{nullptr}; THROW_IF_FAILED( D2D1CreateFactory( D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_MULTI_THREADED, &factory - ) + ) ); factory.copy_to(&m_factory); @@ -167,7 +173,7 @@ void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceIndependentResources() LOG_CAUGHT_EXCEPTION(); } } -void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceResources() +void LazyD2D::CreateDeviceResources() { try { @@ -184,7 +190,7 @@ void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceResources() ) }; - wil::com_ptr dcRT{nullptr}; + com_ptr dcRT{nullptr}; THROW_IF_FAILED( m_factory->CreateDCRenderTarget( &properties, @@ -193,7 +199,6 @@ void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceResources() ); dcRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE::D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - dcRT.copy_to(&m_dcRT); } catch (...) @@ -201,21 +206,124 @@ void TranslucentFlyouts::DXHelper::LazyD2D::CreateDeviceResources() LOG_CAUGHT_EXCEPTION(); } } -void TranslucentFlyouts::DXHelper::LazyD2D::DestroyDeviceIndependentResources() +void LazyD2D::DestroyDeviceIndependentResources() { m_factory.reset(); } -void TranslucentFlyouts::DXHelper::LazyD2D::DestroyDeviceResources() +void LazyD2D::DestroyDeviceResources() { m_dcRT.reset(); } -D2D1::ColorF TranslucentFlyouts::DXHelper::COLORREF2ColorF(COLORREF color, std::byte alpha) +/* ======================================================================================== */ + +LazyDComposition& LazyDComposition::GetInstance() +{ + static LazyDComposition instance{}; + return instance; +} + +bool LazyDComposition::EnsureInitialized() +{ + auto& lazyDComp{GetInstance()}; + + if (lazyDComp.m_dcompDevice) + { + try + { + BOOL valid{FALSE}; + THROW_IF_FAILED( + lazyDComp.m_dcompDevice.query()->CheckDeviceState(&valid) + ); + + if (!valid) + { + LazyDX::NotifyDeviceLost(); + } + } + CATCH_LOG() + } + + return (lazyDComp.m_dcompDevice ? true : false); +} + +LazyDComposition::LazyDComposition() { - return ColorF( - static_cast(GetRValue(color)) / 255.f, - static_cast(GetGValue(color)) / 255.f, - static_cast(GetBValue(color)) / 255.f, - static_cast(std::to_integer(alpha)) / 255.f - ); -} \ No newline at end of file + CreateDeviceIndependentResources(); + CreateDeviceResources(); +} + +LazyDComposition::~LazyDComposition() +{ + DestroyDeviceIndependentResources(); + DestroyDeviceResources(); +} + +void LazyDComposition::CreateDeviceIndependentResources() +{ +} +void LazyDComposition::CreateDeviceResources() +{ + try + { + com_ptr dxgiDevice{nullptr}; + com_ptr d3dDevice{nullptr}; + com_ptr dcompDevice{nullptr}; + + THROW_IF_FAILED( + D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + nullptr, + 0, + D3D11_SDK_VERSION, + &d3dDevice, + nullptr, + nullptr + ) + ); + d3dDevice.query_to(&dxgiDevice); + + THROW_IF_FAILED( + DCompositionCreateDevice3( + dxgiDevice.get(), + IID_PPV_ARGS(&dcompDevice) + ) + ); + + dcompDevice.copy_to(&m_dcompDevice); + d3dDevice.copy_to(&m_d3dDevice); + dxgiDevice.copy_to(&m_dxgiDevice); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + } +} +void LazyDComposition::DestroyDeviceIndependentResources() +{ +} + +void LazyDComposition::DestroyDeviceResources() +{ + m_dcompDevice.reset(); + m_d3dDevice.reset(); + m_dxgiDevice.reset(); +} + +ColorF TranslucentFlyouts::DXHelper::MakeColorF(DWORD argb) +{ + auto a{static_cast(argb >> 24) / 255.f}; + auto r{static_cast(argb >> 16 & 0xff) / 255.f}; + auto g{static_cast(argb >> 8 & 0xff) / 255.f}; + auto b{static_cast(argb & 0xff) / 255.f}; + + return ColorF{ + r, + g, + b, + a + }; +} diff --git a/TFMain/DXHelper.hpp b/TFMain/DXHelper.hpp index a18584b..53220b8 100644 --- a/TFMain/DXHelper.hpp +++ b/TFMain/DXHelper.hpp @@ -62,7 +62,7 @@ namespace TranslucentFlyouts public: static bool EnsureInitialized(); static LazyD2D& GetInstance(); - ~LazyD2D() noexcept = default; + ~LazyD2D() noexcept; LazyD2D(const LazyD2D&) = delete; LazyD2D& operator=(const LazyD2D&) = delete; @@ -80,6 +80,31 @@ namespace TranslucentFlyouts wil::com_ptr m_factory{nullptr}; }; - D2D1::ColorF COLORREF2ColorF(COLORREF color, std::byte alpha); + class LazyDComposition : public LazyDX + { + public: + static bool EnsureInitialized(); + static LazyDComposition& GetInstance(); + ~LazyDComposition() noexcept; + LazyDComposition(const LazyDComposition&) = delete; + LazyDComposition& operator=(const LazyDComposition&) = delete; + + wil::com_ptr GetDxgiDevice() const { return m_dxgiDevice; }; + wil::com_ptr GetD3DDevice() const { return m_d3dDevice; }; + wil::com_ptr GetDCompositionDevice() const { return m_dcompDevice; }; + protected: + void CreateDeviceIndependentResources() override; + void CreateDeviceResources() override; + void DestroyDeviceIndependentResources() override; + void DestroyDeviceResources() override; + private: + LazyDComposition(); + + wil::com_ptr m_dxgiDevice{nullptr}; + wil::com_ptr m_d3dDevice{nullptr}; + wil::com_ptr m_dcompDevice{nullptr}; + }; + + D2D1::ColorF MakeColorF(DWORD argb); } } \ No newline at end of file diff --git a/TFMain/DwmThumbnailAPI.hpp b/TFMain/DwmThumbnailAPI.hpp new file mode 100644 index 0000000..9a3b158 --- /dev/null +++ b/TFMain/DwmThumbnailAPI.hpp @@ -0,0 +1,126 @@ +#pragma once +#include "pch.h" + +namespace TranslucentFlyouts +{ + namespace DwmThumbnailAPI + { + enum class THUMBNAIL_TYPE + { + TT_DEFAULT = 0x0, + TT_SNAPSHOT = 0x1, + TT_ICONIC = 0x2, + TT_BITMAPPENDING = 0x3, + TT_BITMAP = 0x4 + }; + + constexpr UINT DWM_TNP_FREEZE{0x100000}; + constexpr UINT DWM_TNP_ENABLE3D{0x4000000}; + constexpr UINT DWM_TNP_DISABLE3D{0x8000000}; + constexpr UINT DWM_TNP_FORCECVI{0x40000000}; + constexpr UINT DWM_TNP_DISABLEFORCECVI{0x80000000}; + + static const auto g_actualDwmpQueryThumbnailType + { + reinterpret_cast( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(114)) + ) + }; + static const auto g_actualDwmpCreateSharedThumbnailVisual + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HWND hwndDestination, + IN HWND hwndSource, + IN DWORD dwThumbnailFlags, // Pass 1 to get a thumbnail visual without hwndSource + // 4 or 8 is invalid + IN DWM_THUMBNAIL_PROPERTIES * pThumbnailProperties, + IN VOID* pDCompDevice, + OUT VOID** ppVisual, + OUT PHTHUMBNAIL phThumbnailId + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(147)) + ) + }; + static const auto g_actualDwmpQueryWindowThumbnailSourceSize + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HWND hwndSource, + IN BOOL fSourceClientAreaOnly, + OUT SIZE * pSize + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(162)) + ) + }; + + // PRE-IRON + // 19043 or older + static const auto g_actualDwmpCreateSharedVirtualDesktopVisual + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HWND hwndDestination, + IN VOID* pDCompDevice, + OUT VOID** ppVisual, + OUT PHTHUMBNAIL phThumbnailId + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(163)) + ) + }; + static const auto g_actualDwmpUpdateSharedVirtualDesktopVisual + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HTHUMBNAIL hThumbnailId, + IN HWND * phwndsInclude, + IN DWORD chwndsInclude, + IN HWND * phwndsExclude, + IN DWORD chwndsExclude, + OUT RECT * prcSource, + OUT SIZE * pDestinationSize + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(164)) + ) + }; + + // 20xxx+ + // No changes except for the function name. + static const auto g_actualDwmpCreateSharedMultiWindowVisual + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HWND hwndDestination, + IN VOID* pDCompDevice, + OUT VOID** ppVisual, + OUT PHTHUMBNAIL phThumbnailId + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(163)) + ) + }; + // Change: function name + new DWORD parameter. + // Pass "1" in dwFlags. Feel free to explore other flags. + static const auto g_actualDwmpUpdateSharedMultiWindowVisual + { + reinterpret_cast < + HRESULT(WINAPI*)( + IN HTHUMBNAIL hThumbnailId, + IN HWND * phwndsInclude, + IN DWORD chwndsInclude, + IN HWND * phwndsExclude, + IN DWORD chwndsExclude, + OUT RECT * prcSource, + OUT SIZE * pDestinationSize, + IN DWORD dwFlags + ) + > ( + DetourFindFunction("dwmapi", MAKEINTRESOURCEA(164)) + ) + }; + } +} \ No newline at end of file diff --git a/TFMain/EffectHelper.hpp b/TFMain/EffectHelper.hpp index 5870119..b608e58 100644 --- a/TFMain/EffectHelper.hpp +++ b/TFMain/EffectHelper.hpp @@ -80,13 +80,10 @@ namespace TranslucentFlyouts DWORD dwAnimationId; }; - static const auto pfnSetWindowCompositionAttribute + static const auto g_actualSetWindowCompositionAttribute { reinterpret_cast( - GetProcAddress( - GetModuleHandleW(L"User32"), - "SetWindowCompositionAttribute" - ) + DetourFindFunction("user32", "SetWindowCompositionAttribute") ) }; @@ -215,9 +212,9 @@ namespace TranslucentFlyouts DwmSetWindowAttribute(hwnd, 1029, &mica, sizeof(mica)); DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(DWM_SYSTEMBACKDROP_TYPE)); DwmMakeWindowTransparent(hwnd, windowTransparent); - if (pfnSetWindowCompositionAttribute) + if (g_actualSetWindowCompositionAttribute) { - pfnSetWindowCompositionAttribute(hwnd, &data); + g_actualSetWindowCompositionAttribute(hwnd, &data); } EnableWindowNCRendering(hwnd, ncRendering); } diff --git a/TFMain/Hooking.cpp b/TFMain/Hooking.cpp index d353838..d103e15 100644 --- a/TFMain/Hooking.cpp +++ b/TFMain/Hooking.cpp @@ -2,23 +2,23 @@ #include "Utils.hpp" #include "Hooking.hpp" -namespace TranslucentFlyouts::Hooking -{ - using namespace std; +using namespace std; +using namespace TranslucentFlyouts; - namespace Detours - { - // Begin to install hooks - LONG Begin(); - // End attaching - LONG End(bool commit = true); - } +namespace TranslucentFlyouts::Hooking::Detours +{ + // Begin to install hooks + HRESULT Begin(); + // End attaching + HRESULT End(bool commit = true); } -void TranslucentFlyouts::Hooking::WriteMemory(PVOID memoryAddress, function callback) try +void Hooking::WriteMemory(PVOID memoryAddress, function callback) try { - THROW_HR_IF_NULL(E_INVALIDARG, memoryAddress); - THROW_HR_IF_NULL(E_INVALIDARG, callback); + if (!memoryAddress || !callback) + { + return; + } MEMORY_BASIC_INFORMATION mbi{}; THROW_LAST_ERROR_IF( @@ -48,7 +48,7 @@ void TranslucentFlyouts::Hooking::WriteMemory(PVOID memoryAddress, function callback) try +void Hooking::WalkIAT(PVOID baseAddress, std::string_view dllName, std::function callback) try { THROW_HR_IF(E_INVALIDARG, dllName.empty()); THROW_HR_IF_NULL(E_INVALIDARG, baseAddress); @@ -67,6 +67,7 @@ void TranslucentFlyouts::Hooking::WalkIAT(PVOID baseAddress, std::string_view dl ) }; + LOG_LAST_ERROR_IF_NULL(importDescriptor); THROW_HR_IF_NULL(E_INVALIDARG, importDescriptor); LPCSTR moduleName{nullptr}; @@ -82,8 +83,8 @@ void TranslucentFlyouts::Hooking::WalkIAT(PVOID baseAddress, std::string_view dl importDescriptor++; } - THROW_HR_IF_NULL_MSG(E_NOT_SET, moduleName, "Cannot find specific module for [%hs]!", dllName.data()); - THROW_HR_IF_MSG(E_NOT_SET, importDescriptor->Name == 0, "Cannot find specific module for [%hs]!", dllName.data()); + THROW_HR_IF_NULL_MSG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), moduleName, "Cannot find specific module for [%hs]!", dllName.data()); + THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), importDescriptor->Name == 0, "Cannot find specific module for [%hs]!", dllName.data()); auto thunk{reinterpret_cast(reinterpret_cast(baseAddress) + importDescriptor->FirstThunk)}; auto nameThunk = reinterpret_cast(reinterpret_cast(baseAddress) + importDescriptor->OriginalFirstThunk); @@ -114,9 +115,11 @@ void TranslucentFlyouts::Hooking::WalkIAT(PVOID baseAddress, std::string_view dl nameThunk++; } } -CATCH_LOG_RETURN() +catch (...) +{ +} -void TranslucentFlyouts::Hooking::WalkDeleyloadIAT(PVOID baseAddress, string_view dllName, function callback) try +void Hooking::WalkDeleyloadIAT(PVOID baseAddress, string_view dllName, function callback) try { THROW_HR_IF(E_INVALIDARG, dllName.empty()); THROW_HR_IF_NULL(E_INVALIDARG, baseAddress); @@ -135,6 +138,7 @@ void TranslucentFlyouts::Hooking::WalkDeleyloadIAT(PVOID baseAddress, string_vie ) }; + LOG_LAST_ERROR_IF_NULL(importDescriptor); THROW_HR_IF_NULL(E_INVALIDARG, importDescriptor); LPCSTR moduleName{nullptr}; @@ -150,11 +154,11 @@ void TranslucentFlyouts::Hooking::WalkDeleyloadIAT(PVOID baseAddress, string_vie importDescriptor++; } - THROW_HR_IF_NULL_MSG(E_NOT_SET, moduleName, "Cannot find specific module for [%hs]!", dllName.data()); - THROW_HR_IF_MSG(E_NOT_SET, importDescriptor->DllNameRVA == 0, "Cannot find specific module for [%hs]!", dllName.data()); + THROW_HR_IF_NULL_MSG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), moduleName, "Cannot find specific module for [%hs]!", dllName.data()); + THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), importDescriptor->DllNameRVA == 0, "Cannot find specific module for [%hs]!", dllName.data()); auto attributes{importDescriptor->Attributes.RvaBased}; - THROW_HR_IF_MSG(E_NOT_SET, attributes != 1, "Unsupported delay loaded dll![%hs]", dllName.data()); + THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), attributes != 1, "Unsupported delay loaded dll![%hs]", dllName.data()); auto moduleHandle = reinterpret_cast(reinterpret_cast(baseAddress) + importDescriptor->ModuleHandleRVA); auto thunk = reinterpret_cast(reinterpret_cast(baseAddress) + importDescriptor->ImportAddressTableRVA); @@ -186,9 +190,11 @@ void TranslucentFlyouts::Hooking::WalkDeleyloadIAT(PVOID baseAddress, string_vie nameThunk++; } } -CATCH_LOG_RETURN() +catch (...) +{ +} -size_t TranslucentFlyouts::Hooking::WriteIAT(PVOID baseAddress, std::string_view dllName, std::unordered_map hookMap) +size_t Hooking::WriteIAT(PVOID baseAddress, std::string_view dllName, std::unordered_map hookMap) { size_t result{hookMap.size()}; @@ -198,7 +204,7 @@ size_t TranslucentFlyouts::Hooking::WriteIAT(PVOID baseAddress, std::string_view for (auto [targetFunctionNameOrOrdinal, hookFunctionAddress] : tempHookMap) { if ( - (importedByName == TRUE && !strcmp(functionNameOrOrdinal, targetFunctionNameOrOrdinal)) || + (importedByName == TRUE && !Utils::IsBadReadPtr(targetFunctionNameOrOrdinal) && !strcmp(functionNameOrOrdinal, targetFunctionNameOrOrdinal)) || (importedByName == FALSE && functionNameOrOrdinal == targetFunctionNameOrOrdinal) ) { @@ -225,7 +231,7 @@ size_t TranslucentFlyouts::Hooking::WriteIAT(PVOID baseAddress, std::string_view return result; } -size_t TranslucentFlyouts::Hooking::WriteDelayloadIAT(PVOID baseAddress, std::string_view dllName, std::unordered_map hookMap) +size_t Hooking::WriteDelayloadIAT(PVOID baseAddress, std::string_view dllName, std::unordered_map hookMap) { size_t result{hookMap.size()}; @@ -235,7 +241,7 @@ size_t TranslucentFlyouts::Hooking::WriteDelayloadIAT(PVOID baseAddress, std::st for (auto [targetFunctionNameOrOrdinal, hookFunctionAddress] : tempHookMap) { if ( - (importedByName == TRUE && !strcmp(functionNameOrOrdinal, targetFunctionNameOrOrdinal)) || + (importedByName == TRUE && !Utils::IsBadReadPtr(targetFunctionNameOrOrdinal) && !strcmp(functionNameOrOrdinal, targetFunctionNameOrOrdinal)) || (importedByName == FALSE && functionNameOrOrdinal == targetFunctionNameOrOrdinal) ) { @@ -270,7 +276,7 @@ size_t TranslucentFlyouts::Hooking::WriteDelayloadIAT(PVOID baseAddress, std::st return result; } -LONG TranslucentFlyouts::Hooking::Detours::Begin() +HRESULT Hooking::Detours::Begin() { DetourSetIgnoreTooSmall(TRUE); RETURN_IF_WIN32_ERROR(DetourTransactionBegin()); @@ -278,14 +284,14 @@ LONG TranslucentFlyouts::Hooking::Detours::Begin() return ERROR_SUCCESS; } -LONG TranslucentFlyouts::Hooking::Detours::End(bool commit) +HRESULT Hooking::Detours::End(bool commit) { - return ((commit ? DetourTransactionCommit() : DetourTransactionAbort())); + return ((commit ? HRESULT_FROM_WIN32(DetourTransactionCommit()) : HRESULT_FROM_WIN32(DetourTransactionAbort()))); } -HRESULT TranslucentFlyouts::Hooking::Detours::Write(function callback) try +HRESULT Hooking::Detours::Write(function callback) try { - HRESULT hr{HRESULT_FROM_WIN32(Hooking::Detours::Begin())}; + HRESULT hr{Hooking::Detours::Begin()}; if (FAILED(hr)) { return hr; @@ -293,16 +299,16 @@ HRESULT TranslucentFlyouts::Hooking::Detours::Write(function callback) t callback(); - return HRESULT_FROM_WIN32(Hooking::Detours::End(true)); + return Hooking::Detours::End(true); } catch (...) { LOG_CAUGHT_EXCEPTION(); - Hooking::Detours::End(false); + LOG_IF_FAILED(Hooking::Detours::End(false)); return wil::ResultFromCaughtException(); } -void TranslucentFlyouts::Hooking::Detours::Attach(string_view dllName, string_view funcName, PVOID* realFuncAddr, PVOID hookFuncAddr) +void Hooking::Detours::Attach(string_view dllName, string_view funcName, PVOID* realFuncAddr, PVOID hookFuncAddr) { THROW_HR_IF_NULL(E_INVALIDARG, realFuncAddr); THROW_HR_IF_NULL(E_INVALIDARG, *realFuncAddr); @@ -312,7 +318,7 @@ void TranslucentFlyouts::Hooking::Detours::Attach(string_view dllName, string_vi Attach(realFuncAddr, hookFuncAddr); } -void TranslucentFlyouts::Hooking::Detours::Attach(PVOID* realFuncAddr, PVOID hookFuncAddr) +void Hooking::Detours::Attach(PVOID* realFuncAddr, PVOID hookFuncAddr) { THROW_HR_IF_NULL(E_INVALIDARG, realFuncAddr); THROW_HR_IF_NULL(E_INVALIDARG, *realFuncAddr); @@ -321,18 +327,18 @@ void TranslucentFlyouts::Hooking::Detours::Attach(PVOID* realFuncAddr, PVOID hoo THROW_IF_WIN32_ERROR(DetourAttach(realFuncAddr, hookFuncAddr)); } -void TranslucentFlyouts::Hooking::Detours::Detach(PVOID* realFuncAddr, PVOID hookFuncAddr) +void Hooking::Detours::Detach(PVOID* realFuncAddr, PVOID hookFuncAddr) { THROW_IF_WIN32_ERROR(DetourDetach(realFuncAddr, hookFuncAddr)); } -TranslucentFlyouts::Hooking::DllNotifyRoutine& TranslucentFlyouts::Hooking::DllNotifyRoutine::GetInstance() +Hooking::DllNotifyRoutine& Hooking::DllNotifyRoutine::GetInstance() { static DllNotifyRoutine instance{}; return instance; }; -TranslucentFlyouts::Hooking::DllNotifyRoutine::DllNotifyRoutine() +Hooking::DllNotifyRoutine::DllNotifyRoutine() { try { @@ -351,7 +357,7 @@ TranslucentFlyouts::Hooking::DllNotifyRoutine::DllNotifyRoutine() } } -TranslucentFlyouts::Hooking::DllNotifyRoutine::~DllNotifyRoutine() noexcept +Hooking::DllNotifyRoutine::~DllNotifyRoutine() noexcept { if (!m_actualLdrUnregisterDllNotification) { @@ -366,7 +372,7 @@ TranslucentFlyouts::Hooking::DllNotifyRoutine::~DllNotifyRoutine() noexcept m_cookie = nullptr; } -VOID CALLBACK TranslucentFlyouts::Hooking::DllNotifyRoutine::LdrDllNotification( +VOID CALLBACK Hooking::DllNotifyRoutine::LdrDllNotification( ULONG NotificationReason, PCLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context @@ -380,28 +386,26 @@ VOID CALLBACK TranslucentFlyouts::Hooking::DllNotifyRoutine::LdrDllNotification( if (callback) { bool load{NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED ? true : false}; - DllInfo info + callback( + load, { load ? NotificationData->Loaded.FullDllName : NotificationData->Unloaded.FullDllName, load ? NotificationData->Loaded.BaseDllName : NotificationData->Unloaded.BaseDllName, load ? NotificationData->Loaded.DllBase : NotificationData->Unloaded.DllBase, load ? NotificationData->Loaded.SizeOfImage : NotificationData->Unloaded.SizeOfImage, - }; - callback( - load, - info + } ); } } } } -void TranslucentFlyouts::Hooking::DllNotifyRoutine::AddCallback(Callback callback) +void Hooking::DllNotifyRoutine::AddCallback(Callback callback) { m_callbackList.push_back(callback); } -void TranslucentFlyouts::Hooking::DllNotifyRoutine::DeleteCallback(Callback callback) +void Hooking::DllNotifyRoutine::DeleteCallback(Callback callback) { for (auto it = m_callbackList.begin(); it != m_callbackList.end();) { @@ -418,18 +422,18 @@ void TranslucentFlyouts::Hooking::DllNotifyRoutine::DeleteCallback(Callback call } } -TranslucentFlyouts::Hooking::MsgHooks& TranslucentFlyouts::Hooking::MsgHooks::GetInstance() +Hooking::MsgHooks& Hooking::MsgHooks::GetInstance() { static MsgHooks instance{}; return instance; }; -TranslucentFlyouts::Hooking::MsgHooks::~MsgHooks() +Hooking::MsgHooks::~MsgHooks() { UninstallAll(); } -LRESULT CALLBACK TranslucentFlyouts::Hooking::MsgHooks::CallWndHookProc(int code, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK Hooking::MsgHooks::CallWndHookProc(int code, WPARAM wParam, LPARAM lParam) { auto& hookInfo{GetInstance().m_hookMap[GetCurrentThreadId()]}; auto& callbackList{GetInstance().m_callbackList}; @@ -447,10 +451,11 @@ LRESULT CALLBACK TranslucentFlyouts::Hooking::MsgHooks::CallWndHookProc(int code } } } + return CallNextHookEx(hookInfo.callWindowHook.get(), code, wParam, lParam); } -LRESULT CALLBACK TranslucentFlyouts::Hooking::MsgHooks::CallWndRetHookProc(int code, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK Hooking::MsgHooks::CallWndRetHookProc(int code, WPARAM wParam, LPARAM lParam) { auto& hookInfo{GetInstance().m_hookMap[GetCurrentThreadId()]}; auto& callbackList{GetInstance().m_callbackList}; @@ -471,12 +476,12 @@ LRESULT CALLBACK TranslucentFlyouts::Hooking::MsgHooks::CallWndRetHookProc(int c return CallNextHookEx(hookInfo.callWindowRetHook.get(), code, wParam, lParam); } -void TranslucentFlyouts::Hooking::MsgHooks::AddCallback(Callback callback) +void Hooking::MsgHooks::AddCallback(Callback callback) { m_callbackList.push_back(callback); } -void TranslucentFlyouts::Hooking::MsgHooks::DeleteCallback(Callback callback) +void Hooking::MsgHooks::DeleteCallback(Callback callback) { for (auto it = m_callbackList.begin(); it != m_callbackList.end();) { @@ -493,20 +498,24 @@ void TranslucentFlyouts::Hooking::MsgHooks::DeleteCallback(Callback callback) } } -void TranslucentFlyouts::Hooking::MsgHooks::Install(HWND hWnd) +void Hooking::MsgHooks::Install(HWND hWnd) { DWORD threadId{GetWindowThreadProcessId(hWnd, nullptr)}; if (m_hookMap.find(threadId) == m_hookMap.end()) { - m_hookMap[threadId].callWindowHook.reset(SetWindowsHookExW(WH_CALLWNDPROC, CallWndHookProc, nullptr, threadId)); - m_hookMap[threadId].callWindowRetHook.reset(SetWindowsHookExW(WH_CALLWNDPROCRET, CallWndRetHookProc, nullptr, threadId)); + auto& callWindowHook{m_hookMap[threadId].callWindowHook}; + auto& callWindowRetHook{m_hookMap[threadId].callWindowRetHook}; + callWindowHook.reset(SetWindowsHookExW(WH_CALLWNDPROC, CallWndHookProc, nullptr, threadId)); + LOG_LAST_ERROR_IF_NULL(callWindowHook); + callWindowRetHook.reset(SetWindowsHookExW(WH_CALLWNDPROCRET, CallWndRetHookProc, nullptr, threadId)); + LOG_LAST_ERROR_IF_NULL(callWindowRetHook); } m_hookMap[threadId].windowList.push_back(hWnd); } -void TranslucentFlyouts::Hooking::MsgHooks::Uninstall(HWND hWnd) +void Hooking::MsgHooks::Uninstall(HWND hWnd) { DWORD threadId{GetWindowThreadProcessId(hWnd, nullptr)}; @@ -536,27 +545,28 @@ void TranslucentFlyouts::Hooking::MsgHooks::Uninstall(HWND hWnd) } } -void TranslucentFlyouts::Hooking::MsgHooks::UninstallAll() +void Hooking::MsgHooks::UninstallAll() { m_hookMap.clear(); } -TranslucentFlyouts::Hooking::FunctionCallHook::~FunctionCallHook() +Hooking::FunctionCallHook::~FunctionCallHook() { Detach(); } -void TranslucentFlyouts::Hooking::FunctionCallHook::InitJumpRegion(PVOID startAddress) +void Hooking::FunctionCallHook::InitJumpRegion(PVOID startAddress) { m_jumpRegion.reset( static_cast(DetourAllocateRegionWithinJumpBounds(startAddress, &m_size)) ); + LOG_LAST_ERROR_IF_NULL(m_jumpRegion); m_current = m_jumpRegion.get(); m_end = m_current + m_size; } -std::byte* TranslucentFlyouts::Hooking::FunctionCallHook::AllocJmpCode(PVOID detourDestination) +std::byte* Hooking::FunctionCallHook::AllocJmpCode(PVOID detourDestination) { LOG_HR_IF(E_INVALIDARG, !m_jumpRegion); if (!m_jumpRegion) @@ -584,87 +594,78 @@ std::byte* TranslucentFlyouts::Hooking::FunctionCallHook::AllocJmpCode(PVOID det return startPos; } -void TranslucentFlyouts::Hooking::FunctionCallHook::Attach(PVOID functionThunkAddress, PVOID detourAddress, PVOID detourDestination, int callersCount) +int Hooking::FunctionCallHook::Attach(PVOID functionThunkAddress, PVOID detourAddress, PVOID detourDestination, int callersCount) { - LOG_HR_IF(E_INVALIDARG, !functionThunkAddress); - LOG_HR_IF(E_INVALIDARG, !detourAddress); - LOG_HR_IF(E_INVALIDARG, !detourDestination); - LOG_HR_IF(E_INVALIDARG, !callersCount); - - if (!functionThunkAddress) - { - return; - } - if (!detourAddress) - { - return; - } - if (!detourDestination) - { - return; - } - if (!m_jumpRegion) - { - InitJumpRegion(functionThunkAddress); - } - - auto jmpBytes{AllocJmpCode(detourDestination)}; - auto functionBytes{reinterpret_cast(functionThunkAddress)}; - auto functionIsEnd = [](std::byte * bytes, int offset) + try { + THROW_HR_IF_NULL(E_INVALIDARG, functionThunkAddress); + THROW_HR_IF_NULL(E_INVALIDARG, detourAddress); + THROW_HR_IF_NULL(E_INVALIDARG, detourDestination); + if (!m_jumpRegion) + { + InitJumpRegion(functionThunkAddress); + } + THROW_HR_IF_NULL(E_INVALIDARG, m_jumpRegion); + + auto jmpBytes{AllocJmpCode(detourDestination)}; + auto functionBytes{reinterpret_cast(functionThunkAddress)}; + auto functionIsEnd = [](std::byte * bytes, int offset) + { #ifdef _WIN64 - if ( - bytes[offset] == std::byte{0xC3} && - bytes[offset + 1] == std::byte{0xCC} && - bytes[offset + 2] == std::byte{0xCC} && - bytes[offset + 3] == std::byte{0xCC} && - bytes[offset + 4] == std::byte{0xCC} && - bytes[offset + 5] == std::byte{0xCC} && - bytes[offset + 6] == std::byte{0xCC} && - bytes[offset + 7] == std::byte{0xCC} && - bytes[offset + 8] == std::byte{0xCC} - ) + if ( + bytes[offset] == std::byte{0xC3} && + bytes[offset + 1] == std::byte{0xCC} && + bytes[offset + 2] == std::byte{0xCC} && + bytes[offset + 3] == std::byte{0xCC} && + bytes[offset + 4] == std::byte{0xCC} && + bytes[offset + 5] == std::byte{0xCC} && + bytes[offset + 6] == std::byte{0xCC} && + bytes[offset + 7] == std::byte{0xCC} && + bytes[offset + 8] == std::byte{0xCC} + ) #else - if ( - bytes[offset] == std::byte{0xC2} && - bytes[offset + 2] == std::byte{0x00} - ) + if ( + bytes[offset] == std::byte{0xC2} && + bytes[offset + 2] == std::byte{0x00} + ) #endif - { - return true; - } - return false; - }; + { + return true; + } + return false; + }; - LOG_HR_IF(E_INVALIDARG, !jmpBytes); - if (!jmpBytes) - { - return; - } + THROW_HR_IF_NULL(E_INVALIDARG, jmpBytes); - for (int i = 0; !functionIsEnd(functionBytes, i) && i < 65535 && callersCount; i++) - { - if (functionBytes[i] == std::byte{0xE8}) + for (int i = 0; !functionIsEnd(functionBytes, i) && i < 65535 && callersCount; i++) { - auto offsetAddress{reinterpret_cast(&functionBytes[i + 1])}; - auto offset{*offsetAddress}; - auto callBaseAddress{&functionBytes[i + 5]}; - auto targetAddress{reinterpret_cast(callBaseAddress + offset)}; - - if (targetAddress == detourAddress) + if (functionBytes[i] == std::byte{0xE8}) { - WriteMemory(offsetAddress, [&]() + auto offsetAddress{reinterpret_cast(&functionBytes[i + 1])}; + auto offset{*offsetAddress}; + auto callBaseAddress{&functionBytes[i + 5]}; + auto targetAddress{reinterpret_cast(callBaseAddress + offset)}; + + if (targetAddress == detourAddress) { - *offsetAddress = static_cast(jmpBytes - callBaseAddress); - m_hookedMap[offsetAddress] = offset; - callersCount--; - }); + WriteMemory(offsetAddress, [&]() + { + *offsetAddress = static_cast(jmpBytes - callBaseAddress); + m_hookedMap[offsetAddress] = offset; + callersCount--; + }); + } } } } + catch (...) + { + } + + return callersCount; } -void TranslucentFlyouts::Hooking::FunctionCallHook::Detach() +void Hooking::FunctionCallHook::Detach() { for (const auto [offsetAddress, offset] : m_hookedMap) { @@ -673,4 +674,8 @@ void TranslucentFlyouts::Hooking::FunctionCallHook::Detach() *offsetAddress = offset; }); } + + m_current = m_jumpRegion.get(); + m_end = m_current + m_size; + SecureZeroMemory(m_current, m_size); } diff --git a/TFMain/Hooking.hpp b/TFMain/Hooking.hpp index 9029f4e..a17a710 100644 --- a/TFMain/Hooking.hpp +++ b/TFMain/Hooking.hpp @@ -130,7 +130,7 @@ namespace TranslucentFlyouts FunctionCallHook() = default; ~FunctionCallHook(); - void Attach(PVOID targetAddress, PVOID detourAddress, PVOID detourDestination, int callersCount); + int Attach(PVOID targetAddress, PVOID detourAddress, PVOID detourDestination, int callersCount); void Detach(); private: void InitJumpRegion(PVOID startAddress); diff --git a/TFMain/ImmersiveContextMenuPatcher.cpp b/TFMain/ImmersiveContextMenuPatcher.cpp index 3109233..54b1f9e 100644 --- a/TFMain/ImmersiveContextMenuPatcher.cpp +++ b/TFMain/ImmersiveContextMenuPatcher.cpp @@ -7,42 +7,40 @@ #include "DXHelper.hpp" #include "ImmersiveContextMenuPatcher.hpp" -namespace TranslucentFlyouts -{ - using namespace std; - - // A list of modules that contain the symbol of C++ class ImmersiveContextMenuHelper, - // which it means these modules provide methods to create a immersive context menu - const array g_hookModuleList - { - L"explorer.exe"sv, - L"Narrator.exe"sv, - L"MusNotifyIcon.exe"sv, - L"ApplicationFrame.dll"sv, - L"ExplorerFrame.dll"sv, - L"InputSwitch.dll"sv, - L"pnidui.dll"sv, - L"SecurityHealthSSO.dll"sv, - L"shell32.dll"sv, - L"SndVolSSO.dll"sv, - L"twinui.dll"sv, - L"twinui.pcshell.dll"sv, - L"bthprops.cpl"sv, - // Windows 11 - L"Taskmgr.exe"sv, - L"museuxdocked.dll"sv, - L"SecurityHealthSsoUdk.dll"sv, - L"Taskbar.dll"sv, - L"Windows.UI.FileExplorer.dll"sv, - L"Windows.UI.FileExplorer.WASDK.dll"sv, - L"stobject.dll"sv, - // Third-party apps - L"StartIsBack64.dll"sv, - L"StartIsBack32.dll"sv - }; -} +using namespace std; +using namespace TranslucentFlyouts; -TranslucentFlyouts::ImmersiveContextMenuPatcher::ImmersiveContextMenuPatcher() +// A list of modules that contain the symbol of C++ class ImmersiveContextMenuHelper, +// which it means these modules provide methods to create a immersive context menu +const array g_hookModuleList +{ + L"explorer.exe"sv, + L"Narrator.exe"sv, + L"MusNotifyIcon.exe"sv, + L"ApplicationFrame.dll"sv, + L"ExplorerFrame.dll"sv, + L"InputSwitch.dll"sv, + L"pnidui.dll"sv, + L"SecurityHealthSSO.dll"sv, + L"shell32.dll"sv, + L"SndVolSSO.dll"sv, + L"twinui.dll"sv, + L"twinui.pcshell.dll"sv, + L"bthprops.cpl"sv, + // Windows 11 + L"Taskmgr.exe"sv, + L"museuxdocked.dll"sv, + L"SecurityHealthSsoUdk.dll"sv, + L"Taskbar.dll"sv, + L"Windows.UI.FileExplorer.dll"sv, + L"Windows.UI.FileExplorer.WASDK.dll"sv, + L"stobject.dll"sv, + // Third-party apps + L"StartIsBack64.dll"sv, + L"StartIsBack32.dll"sv +}; + +ImmersiveContextMenuPatcher::ImmersiveContextMenuPatcher() { try { @@ -65,19 +63,19 @@ TranslucentFlyouts::ImmersiveContextMenuPatcher::ImmersiveContextMenuPatcher() } } -TranslucentFlyouts::ImmersiveContextMenuPatcher::~ImmersiveContextMenuPatcher() noexcept +ImmersiveContextMenuPatcher::~ImmersiveContextMenuPatcher() noexcept { Hooking::DllNotifyRoutine::GetInstance().DeleteCallback(DllNotificationCallback); ShutdownHook(); } -TranslucentFlyouts::ImmersiveContextMenuPatcher& TranslucentFlyouts::ImmersiveContextMenuPatcher::GetInstance() +ImmersiveContextMenuPatcher& ImmersiveContextMenuPatcher::GetInstance() { static ImmersiveContextMenuPatcher instance{}; return instance; } -HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackground( +HRESULT WINAPI ImmersiveContextMenuPatcher::DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, @@ -106,14 +104,6 @@ HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackgro RETURN_IF_FAILED( ThemeHelper::GetThemeClass(hTheme, themeClassName, MAX_PATH) ); - RETURN_HR_IF_EXPECTED( - E_NOTIMPL, !(!_wcsicmp(themeClassName, L"Menu")) - ); - - bool darkMode{ThemeHelper::DetermineThemeMode(hTheme, L"ImmersiveStart", L"Menu", MENU_POPUPBACKGROUND, 0, TMT_FILLCOLOR)}; - - MenuHandler::NotifyUxThemeRendering(); - MenuHandler::NotifyMenuDarkMode(darkMode); RECT clipRect{*pRect}; if (pClipRect != nullptr) @@ -121,42 +111,45 @@ HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackgro IntersectRect(&clipRect, &clipRect, pClipRect); } - auto& menuRendering{MenuRendering::GetInstance()}; - DWORD customRendering + if (!_wcsicmp(themeClassName, L"ListViewPopup")) { - RegHelper::GetDword( - L"Menu", - L"EnableCustomRendering", - 0, - false - ) - }; - - // Separator - if (iPartId == MENU_POPUPSEPARATOR) - { - if (customRendering) - { - if (SUCCEEDED(menuRendering.DoCustomThemeRendering(hdc, darkMode, iPartId, iStateId, clipRect, *pRect))) - { - return S_OK; - } - } + MenuHandler::NotifyUxThemeRendering(); + MenuHandler::NotifyMenuDarkMode(false); + + RETURN_IF_WIN32_BOOL_FALSE( + PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS) + ); + + return S_OK; } - // Focusing - if (iPartId == MENU_POPUPITEMKBFOCUS) + + if (!_wcsicmp(themeClassName, L"Menu")) { - if (customRendering) + bool darkMode{ThemeHelper::DetermineThemeMode(hTheme, L"ImmersiveStart", L"Menu", MENU_POPUPBACKGROUND, 0, TMT_FILLCOLOR)}; + + MenuHandler::NotifyUxThemeRendering(); + MenuHandler::NotifyMenuDarkMode(darkMode); + MenuHandler::NotifyMenuStyle(true); + + COLORREF color{DWMWA_COLOR_NONE}; + if (SUCCEEDED(GetThemeColor(hTheme, MENU_POPUPBORDERS, 0, TMT_FILLCOLORHINT, &color))) { - if (SUCCEEDED(menuRendering.DoCustomThemeRendering(hdc, darkMode, iPartId, iStateId, clipRect, *pRect))) - { - return S_OK; - } + MenuHandler::NotifyMenuBorderColor(color); } - } - if ((iPartId == MENU_POPUPITEM || iPartId == MENU_POPUPITEM_FOCUSABLE)) - { - if (iStateId == MPI_DISABLEDHOT) + + auto& menuRendering{MenuRendering::GetInstance()}; + DWORD customRendering + { + RegHelper::GetDword( + L"Menu", + L"EnableCustomRendering", + 0, + false + ) + }; + + // Separator + if (iPartId == MENU_POPUPSEPARATOR) { if (customRendering) { @@ -166,7 +159,8 @@ HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackgro } } } - if (iStateId == MPI_HOT) + // Focusing + if (iPartId == MENU_POPUPITEMKBFOCUS) { if (customRendering) { @@ -175,28 +169,53 @@ HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackgro return S_OK; } } + } + if ((iPartId == MENU_POPUPITEM || iPartId == MENU_POPUPITEM_FOCUSABLE)) + { + if (iStateId == MPI_DISABLEDHOT) + { + if (customRendering) + { + if (SUCCEEDED(menuRendering.DoCustomThemeRendering(hdc, darkMode, iPartId, iStateId, clipRect, *pRect))) + { + return S_OK; + } + } + } + if (iStateId == MPI_HOT) + { + if (customRendering) + { + if (SUCCEEDED(menuRendering.DoCustomThemeRendering(hdc, darkMode, iPartId, iStateId, clipRect, *pRect))) + { + return S_OK; + } + } - // System default - return E_NOTIMPL; + // System default + return E_NOTIMPL; + } } - } - { - RETURN_HR_IF_EXPECTED( - E_NOTIMPL, - iPartId != MENU_POPUPBACKGROUND && - iPartId != MENU_POPUPBORDERS && - iPartId != MENU_POPUPGUTTER && - iPartId != MENU_POPUPITEM && - iPartId != MENU_POPUPITEM_FOCUSABLE - ); + { + RETURN_HR_IF_EXPECTED( + E_NOTIMPL, + iPartId != MENU_POPUPBACKGROUND && + iPartId != MENU_POPUPBORDERS && + iPartId != MENU_POPUPGUTTER && + iPartId != MENU_POPUPITEM && + iPartId != MENU_POPUPITEM_FOCUSABLE + ); + + RETURN_IF_WIN32_BOOL_FALSE( + PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS) + ); + } - RETURN_IF_WIN32_BOOL_FALSE( - PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS) - ); + return S_OK; } - return S_OK; + return E_NOTIMPL; }(); if (FAILED(hr)) { @@ -213,7 +232,7 @@ HRESULT WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawThemeBackgro return hr; } -int WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawTextW( +int WINAPI ImmersiveContextMenuPatcher::DrawTextW( HDC hdc, LPCWSTR lpchText, int cchText, @@ -250,7 +269,7 @@ int WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::DrawTextW( return result; } -BOOL WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::BitBlt( +BOOL WINAPI ImmersiveContextMenuPatcher::BitBlt( HDC hdc, int x, int y, @@ -297,7 +316,7 @@ BOOL WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::BitBlt( return result; } -BOOL WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::StretchBlt( +BOOL WINAPI ImmersiveContextMenuPatcher::StretchBlt( HDC hdcDest, int xDest, int yDest, @@ -347,7 +366,7 @@ BOOL WINAPI TranslucentFlyouts::ImmersiveContextMenuPatcher::StretchBlt( return result; } -void TranslucentFlyouts::ImmersiveContextMenuPatcher::DoIATHook(PVOID moduleBaseAddress) +void ImmersiveContextMenuPatcher::DoIATHook(PVOID moduleBaseAddress) { if (m_actualDrawThemeBackground) { @@ -417,7 +436,7 @@ void TranslucentFlyouts::ImmersiveContextMenuPatcher::DoIATHook(PVOID moduleBase } } -void TranslucentFlyouts::ImmersiveContextMenuPatcher::UndoIATHook(PVOID moduleBaseAddress) +void ImmersiveContextMenuPatcher::UndoIATHook(PVOID moduleBaseAddress) { if (m_actualDrawThemeBackground) { @@ -487,7 +506,7 @@ void TranslucentFlyouts::ImmersiveContextMenuPatcher::UndoIATHook(PVOID moduleBa } } -void TranslucentFlyouts::ImmersiveContextMenuPatcher::DllNotificationCallback(bool load, Hooking::DllNotifyRoutine::DllInfo info) +void ImmersiveContextMenuPatcher::DllNotificationCallback(bool load, Hooking::DllNotifyRoutine::DllInfo info) { auto& immersiveContextMenuPatcher{GetInstance()}; if (load) @@ -502,7 +521,7 @@ void TranslucentFlyouts::ImmersiveContextMenuPatcher::DllNotificationCallback(bo } } -void TranslucentFlyouts::ImmersiveContextMenuPatcher::StartupHook() +void ImmersiveContextMenuPatcher::StartupHook() { if (m_startup) { @@ -532,7 +551,7 @@ void TranslucentFlyouts::ImmersiveContextMenuPatcher::StartupHook() m_startup = true; } -void TranslucentFlyouts::ImmersiveContextMenuPatcher::ShutdownHook() +void ImmersiveContextMenuPatcher::ShutdownHook() { if (!m_startup) { diff --git a/TFMain/ImmersiveContextMenuPatcher.hpp b/TFMain/ImmersiveContextMenuPatcher.hpp index 0a133b8..0f02e0e 100644 --- a/TFMain/ImmersiveContextMenuPatcher.hpp +++ b/TFMain/ImmersiveContextMenuPatcher.hpp @@ -55,6 +55,7 @@ namespace TranslucentFlyouts int hSrc, DWORD rop ); + static void DllNotificationCallback(bool load, Hooking::DllNotifyRoutine::DllInfo info); void DoIATHook(PVOID moduleBaseAddress); diff --git a/TFMain/MenuAnimation.cpp b/TFMain/MenuAnimation.cpp index dfe4da0..eb16faa 100644 --- a/TFMain/MenuAnimation.cpp +++ b/TFMain/MenuAnimation.cpp @@ -1,13 +1,22 @@ #include "pch.h" +#include "DXHelper.hpp" #include "ThemeHelper.hpp" +#include "MenuHandler.hpp" #include "MenuAnimation.hpp" +#include "DwmThumbnailAPI.hpp" +#include "EffectHelper.hpp" +#include "RegHelper.hpp" + +using namespace std; +using namespace wil; +using namespace TranslucentFlyouts; namespace TranslucentFlyouts::MenuAnimation { - using namespace std; - struct AnimationInfo { + static UINT WM_MAFINISHED; + AnimationInfo() = default; AnimationInfo(chrono::milliseconds animationDuration) : duration{animationDuration}, endTimeStamp{startTimeStamp + duration.count()} { @@ -19,6 +28,12 @@ namespace TranslucentFlyouts::MenuAnimation duration = animationDuration; endTimeStamp = startTimeStamp + duration.count(); } + void Restart(chrono::milliseconds animationDuration) + { + startTimeStamp = GetTickCount64(); + duration = animationDuration; + endTimeStamp = startTimeStamp + duration.count(); + } virtual void Animator(ULONGLONG currentTimeStamp) {} @@ -27,6 +42,7 @@ namespace TranslucentFlyouts::MenuAnimation ULONGLONG startTimeStamp{GetTickCount64()}; ULONGLONG endTimeStamp{startTimeStamp + duration.count()}; }; + UINT AnimationInfo::WM_MAFINISHED{RegisterWindowMessageW(L"TranslucentFlyouts.MenuAnimation.Finished")}; class AnimationWorker { @@ -45,7 +61,7 @@ namespace TranslucentFlyouts::MenuAnimation static DWORD WINAPI ThreadProc(LPVOID lpThreadParameter); AnimationWorker() = default; - wil::srwlock m_lock{}; + srwlock m_lock{}; DWORD m_threadId{}; vector> m_animationStorage{}; }; @@ -53,7 +69,7 @@ namespace TranslucentFlyouts::MenuAnimation class FadeOut : public AnimationInfo { public: - FadeOut(HWND hWnd, MENUBARINFO mbi, chrono::milliseconds animationDuration) try : AnimationInfo(animationDuration) + FadeOut(HWND hWnd, MENUBARINFO mbi, chrono::milliseconds animationDuration) try : AnimationInfo{animationDuration} { HRESULT hr{S_OK}; POINT source{0, 0}; @@ -73,19 +89,19 @@ namespace TranslucentFlyouts::MenuAnimation THROW_IF_WIN32_BOOL_FALSE(GetWindowRect(hWnd, &windowRect)); window = CreateWindowExW( - WS_EX_NOREDIRECTIONBITMAP | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, // Spy++ - L"Static", - nullptr, - WS_POPUP, - destination.x, - destination.x, - size.cx, - size.cy, - Utils::GetCurrentMenuOwner(), - nullptr, - nullptr, - nullptr - ); + WS_EX_NOREDIRECTIONBITMAP | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, + L"Static", + L"FadeOut Animation", + WS_POPUP, + destination.x, + destination.x, + size.cx, + size.cy, + Utils::GetCurrentMenuOwner(), + nullptr, + nullptr, + nullptr + ); THROW_LAST_ERROR_IF_NULL(window); auto menuDC{wil::GetWindowDC(hWnd)}; @@ -139,7 +155,7 @@ namespace TranslucentFlyouts::MenuAnimation std::byte{0}, false, false - ); + ); THROW_IF_FAILED(hr); } catch (...) @@ -149,7 +165,6 @@ namespace TranslucentFlyouts::MenuAnimation DestroyWindow(window); window = nullptr; } - LOG_CAUGHT_EXCEPTION(); } ~FadeOut() noexcept { @@ -160,12 +175,470 @@ namespace TranslucentFlyouts::MenuAnimation } } + void Animator(ULONGLONG currentTimeStamp) override; + }; + + class PopupIn : public AnimationInfo + { + private: + bool m_started{false}; + float m_ratio{0.f}; + POINT m_cursorPos{}; + + HWND m_backdropWindow{nullptr}; + HWND m_menuWindow{nullptr}; + + chrono::milliseconds m_fadeInDuration{0ms}; + chrono::milliseconds m_popupInDuration{0ms}; + chrono::milliseconds m_totDuration{0ms}; + + com_ptr m_dcompTopTarget{nullptr}; + com_ptr m_dcompBottomTarget{nullptr}; + + HTHUMBNAIL m_thumbnail{nullptr}; + com_ptr m_thumbnailVisual{nullptr}; + + HTHUMBNAIL m_backdropThumbnail{nullptr}; + com_ptr m_backdropThumbnailVisual{nullptr}; + + void Start(bool reverse) try + { + THROW_HR_IF( + E_FAIL, + !DXHelper::LazyDComposition::EnsureInitialized() + ); + + THROW_HR_IF( + E_FAIL, + m_started + ); + + RECT windowRect{}; + THROW_IF_WIN32_BOOL_FALSE( + GetWindowRect(m_menuWindow, &windowRect) + ); + + SIZE size{}; + if (m_thumbnail) + { + THROW_IF_FAILED( + DwmThumbnailAPI::g_actualDwmpQueryWindowThumbnailSourceSize( + m_menuWindow, + FALSE, + &size + ) + ); + DWM_THUMBNAIL_PROPERTIES thumbnailProperties + { + DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DwmThumbnailAPI::DWM_TNP_ENABLE3D, + {0, 0, size.cx, size.cy}, + {}, + 255, + TRUE, + FALSE + }; + DwmUpdateThumbnailProperties(m_thumbnail, &thumbnailProperties); + } + if (m_backdropThumbnail) + { + SetWindowPos( + m_backdropWindow, window, + windowRect.left, windowRect.top, size.cx, size.cy, + SWP_NOACTIVATE | SWP_SHOWWINDOW + ); + DWM_THUMBNAIL_PROPERTIES thumbnailProperties + { + DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DwmThumbnailAPI::DWM_TNP_ENABLE3D, + {0, 0, size.cx, size.cy}, + {}, + 255, + TRUE, + FALSE + }; + DwmUpdateThumbnailProperties(m_backdropThumbnail, &thumbnailProperties); + } + + auto& menuHandler{MenuHandler::GetInstance()}; + auto info{menuHandler.GetMenuRenderingInfo(m_menuWindow)}; + if (info.useUxTheme) + { + DWORD borderColor{DWMWA_COLOR_NONE}; + menuHandler.HandleRoundCorners(L"Menu"sv, window); + DwmSetWindowAttribute(window, DWMWA_BORDER_COLOR, &borderColor, sizeof(borderColor)); + + menuHandler.HandleRoundCorners(L"Menu"sv, m_backdropWindow); + menuHandler.ApplyEffect(L"Menu"sv, m_backdropWindow, info.useDarkMode, true); + DwmSetWindowAttribute(m_backdropWindow, DWMWA_BORDER_COLOR, &borderColor, sizeof(borderColor)); + + DwmTransitionOwnedWindow(window, DWMTRANSITION_OWNEDWINDOW_REPOSITION); + DwmTransitionOwnedWindow(m_backdropWindow, DWMTRANSITION_OWNEDWINDOW_REPOSITION); + } + + auto dcompDevice{DXHelper::LazyDComposition::GetInstance().GetDCompositionDevice()}; + + THROW_IF_FAILED( + m_dcompTopTarget->SetRoot(m_thumbnailVisual.get()) + ); + THROW_IF_FAILED( + m_dcompBottomTarget->SetRoot(m_backdropThumbnailVisual.get()) + ); + + com_ptr dcompAnimation{nullptr}; + THROW_IF_FAILED( + dcompDevice->CreateAnimation(&dcompAnimation) + ); + + auto cleanUp{Utils::RoInit()}; + { + com_ptr manager + { + wil::CoCreateInstance(CLSID_UIAnimationManager2) + }; + com_ptr library + { + wil::CoCreateInstance(CLSID_UIAnimationTransitionLibrary2) + }; + + constexpr float endValue{0.f}; + float beginValue{0.f}; + + if (reverse) + { + beginValue = float(size.cy) - float(size.cy) * m_ratio; + } + else + { + beginValue = -float(size.cy) + float(size.cy) * m_ratio; + } + + // Synchronizing WAM and DirectComposition time such that when WAM Update is called, + // the value reflects the DirectComposition value at the given time. + DCOMPOSITION_FRAME_STATISTICS frameStatistics{}; + THROW_IF_FAILED( + dcompDevice->GetFrameStatistics(&frameStatistics) + ); + UI_ANIMATION_SECONDS timeNow{static_cast(frameStatistics.nextEstimatedFrameTime.QuadPart) / static_cast(frameStatistics.timeFrequency.QuadPart)}; + + { + com_ptr variable{nullptr}; + com_ptr storyboard{nullptr}; + com_ptr transition{nullptr}; + THROW_IF_FAILED( + manager->CreateAnimationVariable(beginValue, &variable) + ); + THROW_IF_FAILED( + manager->CreateStoryboard(&storyboard) + ); + THROW_IF_FAILED( + library->CreateCubicBezierLinearTransition(static_cast(m_popupInDuration.count()) / 1000.f, endValue, 0, 0, 0, 1, &transition) + ); + THROW_IF_FAILED( + storyboard->AddTransition(variable.get(), transition.get()) + ); + THROW_IF_FAILED( + manager->Update(timeNow) + ); + THROW_IF_FAILED( + storyboard->Schedule(timeNow) + ); + THROW_IF_FAILED( + variable->GetCurve(dcompAnimation.get()) + ); + + THROW_IF_FAILED(m_thumbnailVisual->SetOffsetX(0.f)); + THROW_IF_FAILED(m_thumbnailVisual->SetOffsetY(dcompAnimation.get())); + + THROW_IF_FAILED(m_backdropThumbnailVisual->SetOffsetX(0.f)); + THROW_IF_FAILED(m_backdropThumbnailVisual->SetOffsetY(dcompAnimation.get())); + } + + { + com_ptr variable{nullptr}; + com_ptr storyboard{nullptr}; + com_ptr transition{nullptr}; + THROW_IF_FAILED( + manager->CreateAnimationVariable(0.f, &variable) + ); + THROW_IF_FAILED( + manager->CreateStoryboard(&storyboard) + ); + THROW_IF_FAILED( + library->CreateLinearTransition(static_cast(m_fadeInDuration.count()) / 1000.f, 1.f, &transition) + ); + THROW_IF_FAILED( + storyboard->AddTransition(variable.get(), transition.get()) + ); + THROW_IF_FAILED( + manager->Update(timeNow) + ); + THROW_IF_FAILED( + storyboard->Schedule(timeNow) + ); + THROW_IF_FAILED( + variable->GetCurve(dcompAnimation.get()) + ); + + THROW_IF_FAILED(m_thumbnailVisual.query()->SetOpacity(dcompAnimation.get())); + THROW_IF_FAILED(m_backdropThumbnailVisual.query()->SetOpacity(dcompAnimation.get())); + } + } + + THROW_IF_FAILED( + dcompDevice->Commit() + ); + + if (GetClassLongPtr(m_menuWindow, GCL_STYLE) & CS_DROPSHADOW) + { + SetClassLongPtr(window, GCL_STYLE, GetClassLongPtr(window, GCL_STYLE) | CS_DROPSHADOW); + } + + DwmTransitionOwnedWindow(m_menuWindow, DWMTRANSITION_OWNEDWINDOW_REPOSITION); + SetWindowPos( + window, nullptr, + windowRect.left, windowRect.top, size.cx, size.cy, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_SHOWWINDOW + ); + Restart(m_totDuration); + + m_started = true; + } + CATCH_LOG_RETURN() + + // The animation is still running, but we need to stop it right now! + void Abort() + { + m_dcompTopTarget->SetRoot(nullptr); + m_dcompBottomTarget->SetRoot(nullptr); + SetWindowPos( + window, nullptr, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_HIDEWINDOW + ); + SetWindowPos( + m_backdropWindow, nullptr, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_HIDEWINDOW + ); + BOOL cloak{FALSE}; + DwmSetWindowAttribute(m_menuWindow, DWMWA_CLOAK, &cloak, sizeof(cloak)); +#pragma push_macro("max") +#undef max + // Interupt the animation + SetDuration(0ms); +#pragma pop_macro("max") + } + + void Attach() + { + THROW_HR_IF( + E_FAIL, + !SetWindowSubclass(m_menuWindow, SubclassProc, 0, reinterpret_cast(this)) + ); + } + void Detach() + { + if (m_menuWindow) + { + BOOL cloak{FALSE}; + DwmSetWindowAttribute(m_menuWindow, DWMWA_CLOAK, &cloak, sizeof(cloak)); + + if (RemoveWindowSubclass(m_menuWindow, SubclassProc, 0)) + { + m_menuWindow = nullptr; + } + } + } + + static LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) + { + PopupIn& popupInAnimation{*reinterpret_cast(dwRefData)}; + + if (uMsg == WM_WINDOWPOSCHANGED) + { + const auto& windowPos{*reinterpret_cast(lParam)}; + if (windowPos.flags & SWP_SHOWWINDOW) + { + popupInAnimation.Start( + abs(popupInAnimation.m_cursorPos.y - windowPos.y) > + abs(popupInAnimation.m_cursorPos.y - (windowPos.y + windowPos.cy)) + ? true : false + ); + } + + if (windowPos.flags & SWP_HIDEWINDOW) + { + popupInAnimation.Abort(); + } + } + + if (uMsg == WM_NCDESTROY) + { + popupInAnimation.Abort(); + } + + // ~PopupIn() sends WM_MAFINISHED to the SubclassProc + if (uMsg == WM_MAFINISHED) + { + popupInAnimation.Detach(); + } + + return DefSubclassProc(hWnd, uMsg, wParam, lParam); + } + public: +#pragma push_macro("max") +#undef max + PopupIn(HWND hWnd, float startPosRatio, std::chrono::milliseconds popupInDuration, std::chrono::milliseconds fadeInDuration) try : AnimationInfo{chrono::milliseconds::max()}, m_totDuration{max(popupInDuration, fadeInDuration)}, m_menuWindow{hWnd}, m_fadeInDuration{fadeInDuration}, m_popupInDuration{popupInDuration}, m_ratio{startPosRatio} +#pragma pop_macro("max") + { + THROW_HR_IF( + E_FAIL, + !DXHelper::LazyDComposition::EnsureInitialized() + ); + + Attach(); + + BOOL cloak{TRUE}; + THROW_IF_FAILED( + DwmSetWindowAttribute(hWnd, DWMWA_CLOAK, &cloak, sizeof(cloak)) + ); + + window = CreateWindowExW( + WS_EX_NOREDIRECTIONBITMAP | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, + L"Static", + L"PopupIn Animation", + WS_POPUP, + 0, + 0, + 0, + 0, + Utils::GetCurrentMenuOwner(), + nullptr, + nullptr, + nullptr + ); + THROW_LAST_ERROR_IF_NULL(window); + THROW_IF_WIN32_BOOL_FALSE(SetLayeredWindowAttributes(window, 0, 255, LWA_ALPHA)); + + + m_backdropWindow = CreateWindowExW( + WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, + L"Static", + L"PopupIn Animation Backdrop", + WS_POPUP, + 0, + 0, + 0, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + THROW_LAST_ERROR_IF_NULL(m_backdropWindow); + THROW_IF_WIN32_BOOL_FALSE(SetLayeredWindowAttributes(m_backdropWindow, 0, 0, LWA_ALPHA)); + + auto dcompDevice{DXHelper::LazyDComposition::GetInstance().GetDCompositionDevice()}; + + THROW_IF_FAILED( + dcompDevice->CreateTargetForHwnd(window, TRUE, &m_dcompTopTarget) + ); + THROW_IF_FAILED( + dcompDevice->CreateTargetForHwnd(window, FALSE, &m_dcompBottomTarget) + ); + + DWM_THUMBNAIL_PROPERTIES thumbnailProperties + { + DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DwmThumbnailAPI::DWM_TNP_ENABLE3D, + {}, + {}, + 255, + TRUE, + FALSE + }; + THROW_IF_FAILED( + DwmThumbnailAPI::g_actualDwmpCreateSharedThumbnailVisual( + window, + m_menuWindow, + 0, + &thumbnailProperties, + dcompDevice.get(), + m_thumbnailVisual.put_void(), + &m_thumbnail + ) + ); + // Create backdrop thumbnail + THROW_IF_FAILED( + DwmThumbnailAPI::g_actualDwmpCreateSharedThumbnailVisual( + window, + m_backdropWindow, + 0, + &thumbnailProperties, + dcompDevice.get(), + m_backdropThumbnailVisual.put_void(), + &m_backdropThumbnail + ) + ); + + THROW_IF_WIN32_BOOL_FALSE( + GetCursorPos(&m_cursorPos) + ); + } + catch (...) + { + if (m_menuWindow) + { + SendMessageW(m_menuWindow, WM_MAFINISHED, 0, 0); + } + if (window) + { + if (GetClassLongPtr(m_menuWindow, GCL_STYLE) & CS_DROPSHADOW) + { + SetClassLongPtr(window, GCL_STYLE, GetClassLongPtr(window, GCL_STYLE) & ~CS_DROPSHADOW); + } + DestroyWindow(window); + window = nullptr; + } + if (m_backdropWindow) + { + DestroyWindow(m_backdropWindow); + m_backdropWindow = nullptr; + } + if (m_thumbnail) + { + DwmUnregisterThumbnail(m_thumbnail); + m_thumbnail = nullptr; + } + } + + ~PopupIn() noexcept + { + if (m_menuWindow) + { + SendMessageW(m_menuWindow, WM_MAFINISHED, 0, 0); + } + if (window) + { + SetClassLongPtr(window, GCL_STYLE, GetClassLongPtr(window, GCL_STYLE) & ~CS_DROPSHADOW); + SendNotifyMessageW(window, WM_CLOSE, 0, 0); + window = nullptr; + } + if (m_backdropWindow) + { + SendNotifyMessageW(m_backdropWindow, WM_CLOSE, 0, 0); + m_backdropWindow = nullptr; + } + if (m_thumbnail) + { + DwmUnregisterThumbnail(m_thumbnail); + m_thumbnail = nullptr; + } + } void Animator(ULONGLONG currentTimeStamp) override; }; } -DWORD WINAPI TranslucentFlyouts::MenuAnimation::AnimationWorker::ThreadProc(LPVOID lpThreadParameter) +DWORD WINAPI MenuAnimation::AnimationWorker::ThreadProc(LPVOID lpThreadParameter) { auto& animationWorker{*reinterpret_cast(lpThreadParameter)}; @@ -182,7 +655,7 @@ DWORD WINAPI TranslucentFlyouts::MenuAnimation::AnimationWorker::ThreadProc(LPVO auto currentTimeStamp{GetTickCount64()}; delayExitingTicks = currentTimeStamp - delayExitingStartTimeStamp; - + // No animation tasks left, now leaving if (animationStorage.empty()) { @@ -195,7 +668,7 @@ DWORD WINAPI TranslucentFlyouts::MenuAnimation::AnimationWorker::ThreadProc(LPVO else { delayExitingStartTimeStamp = currentTimeStamp; - + // Now it is time to process animation... for (auto it = animationStorage.begin(); it != animationStorage.end();) { @@ -214,7 +687,7 @@ DWORD WINAPI TranslucentFlyouts::MenuAnimation::AnimationWorker::ThreadProc(LPVO } } } - + } // We have completed several animation tasks, let's wait for the next batch! if (FAILED(DwmFlush())) @@ -228,7 +701,7 @@ DWORD WINAPI TranslucentFlyouts::MenuAnimation::AnimationWorker::ThreadProc(LPVO return 0; } -void TranslucentFlyouts::MenuAnimation::AnimationWorker::Schedule(shared_ptr animationInfo) +void MenuAnimation::AnimationWorker::Schedule(shared_ptr animationInfo) { auto cleanUp{m_lock.lock_exclusive()}; m_animationStorage.push_back(animationInfo); @@ -239,11 +712,11 @@ void TranslucentFlyouts::MenuAnimation::AnimationWorker::Schedule(shared_ptr(HINST_THISCOMPONENT), &moduleHandle)); - wil::unique_handle threadHandle{CreateThread(nullptr, 0, ThreadProc, this, 0, &m_threadId)}; + unique_handle threadHandle{CreateThread(nullptr, 0, ThreadProc, this, 0, &m_threadId)}; } } -void TranslucentFlyouts::MenuAnimation::FadeOut::Animator(ULONGLONG currentTimeStamp) +void MenuAnimation::FadeOut::Animator(ULONGLONG currentTimeStamp) { BLENDFUNCTION blendFunction { @@ -275,7 +748,12 @@ void TranslucentFlyouts::MenuAnimation::FadeOut::Animator(ULONGLONG currentTimeS ); } -HRESULT TranslucentFlyouts::MenuAnimation::CreateFadeOut( +void MenuAnimation::PopupIn::Animator(ULONGLONG currentTimeStamp) +{ + +} + +HRESULT MenuAnimation::CreateFadeOut( HWND hWnd, MENUBARINFO mbi, std::chrono::milliseconds duration @@ -288,5 +766,22 @@ HRESULT TranslucentFlyouts::MenuAnimation::CreateFadeOut( } catch (...) { - return wil::ResultFromCaughtException(); + return ResultFromCaughtException(); } + +HRESULT MenuAnimation::CreatePopupIn( + HWND hWnd, + float startPosRatio, + std::chrono::milliseconds popupInDuration, + std::chrono::milliseconds fadeInDuration +) try +{ + auto& animationWorker{AnimationWorker::GetInstance()}; + animationWorker.Schedule(make_shared(hWnd, startPosRatio, popupInDuration, fadeInDuration)); + + return S_OK; +} +catch (...) +{ + return ResultFromCaughtException(); +} \ No newline at end of file diff --git a/TFMain/MenuAnimation.hpp b/TFMain/MenuAnimation.hpp index 636e676..0842852 100644 --- a/TFMain/MenuAnimation.hpp +++ b/TFMain/MenuAnimation.hpp @@ -7,9 +7,15 @@ namespace TranslucentFlyouts { namespace { + using namespace std::chrono; using namespace std::chrono_literals; // Defined in win32kfull.sys!zzzMNFadeSelection - constexpr std::chrono::milliseconds standardFadeoutDuration{350ms}; + constexpr milliseconds standardFadeoutDuration{350ms}; + // Defined in https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/motion + constexpr milliseconds standardPopupInDuration{333ms}; + constexpr milliseconds standardFadeInDuration{87ms}; + // Defined in WinUI + constexpr float standardStartPosRatio{0.666f}; } HRESULT CreateFadeOut( @@ -17,5 +23,12 @@ namespace TranslucentFlyouts MENUBARINFO mbi, std::chrono::milliseconds duration ); + + HRESULT CreatePopupIn( + HWND hWnd, + float startPosRatio, + std::chrono::milliseconds popInDuration, + std::chrono::milliseconds fadeInDuration + ); }; } \ No newline at end of file diff --git a/TFMain/MenuHandler.cpp b/TFMain/MenuHandler.cpp index 3b72fa9..a567b65 100644 --- a/TFMain/MenuHandler.cpp +++ b/TFMain/MenuHandler.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#include "TFMain.hpp" #include "Utils.hpp" #include "Hooking.hpp" #include "RegHelper.hpp" @@ -7,32 +8,31 @@ #include "MenuHandler.hpp" #include "MenuAnimation.hpp" -namespace TranslucentFlyouts -{ - using namespace std; - thread_local decltype(MenuHandler::g_sharedDC) MenuHandler::g_sharedDC{nullptr, nullptr}; - thread_local decltype(MenuHandler::g_sharedMenuInfo) MenuHandler::g_sharedMenuInfo{false, false}; +using namespace std; +using namespace wil; +using namespace TranslucentFlyouts; - const UINT MenuHandler::WM_MHDETACH{RegisterWindowMessageW(L"TranslucentFlyouts.MenuHandler.Detach")}; -} +thread_local decltype(MenuHandler::g_sharedContext) MenuHandler::g_sharedContext{nullptr, nullptr}; +thread_local decltype(MenuHandler::g_sharedMenuInfo) MenuHandler::g_sharedMenuInfo{false, false}; +const UINT MenuHandler::WM_MHDETACH{RegisterWindowMessageW(L"TranslucentFlyouts.MenuHandler.Detach")}; -TranslucentFlyouts::MenuHandler& TranslucentFlyouts::MenuHandler::GetInstance() +MenuHandler& MenuHandler::GetInstance() { static MenuHandler instance{}; return instance; } -TranslucentFlyouts::MenuHandler::MenuHandler() +MenuHandler::MenuHandler() { } -TranslucentFlyouts::MenuHandler::~MenuHandler() noexcept +MenuHandler::~MenuHandler() noexcept { ShutdownHook(); } -void TranslucentFlyouts::MenuHandler::MenuOwnerMsgCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT lResult, bool returnResultValid) +void MenuHandler::MenuOwnerMsgCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT lResult, bool returnResultValid) { if (message == WM_DRAWITEM) { @@ -42,11 +42,8 @@ void TranslucentFlyouts::MenuHandler::MenuOwnerMsgCallback(HWND hwnd, UINT messa { if (wParam == 0 && drawItemStruct.CtlType == ODT_MENU) { - g_sharedDC.menuDC = drawItemStruct.hDC; - } - if (drawItemStruct.CtlType == ODT_LISTVIEW && Utils::IsWindowClass(GetParent(drawItemStruct.hwndItem), L"Listviewpopup")) - { - g_sharedDC.listviewDC = drawItemStruct.hDC; + g_sharedContext.menuDC = drawItemStruct.hDC; + g_sharedMenuInfo.Reset(); } } @@ -54,17 +51,18 @@ void TranslucentFlyouts::MenuHandler::MenuOwnerMsgCallback(HWND hwnd, UINT messa { if (wParam == 0 && drawItemStruct.CtlType == ODT_MENU) { - g_sharedDC.menuDC = nullptr; - } - if (drawItemStruct.CtlType == ODT_LISTVIEW && Utils::IsWindowClass(GetParent(drawItemStruct.hwndItem), L"Listviewpopup")) - { - g_sharedDC.listviewDC = nullptr; + g_sharedContext.menuDC = nullptr; + + HWND menuWindow{WindowFromDC(drawItemStruct.hDC)}; + + GetInstance().HandleSysBorderColors(L"Menu"sv, menuWindow, g_sharedMenuInfo.useDarkMode, g_sharedMenuInfo.borderColor); + GetInstance().HandleRoundCorners(L"Menu"sv, menuWindow); } } } } -void TranslucentFlyouts::MenuHandler::ListviewpopupMsgCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT lResult, bool returnResultValid) +void MenuHandler::ListviewpopupMsgCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT lResult, bool returnResultValid) { DWORD itemDisabled { @@ -77,7 +75,7 @@ void TranslucentFlyouts::MenuHandler::ListviewpopupMsgCallback(HWND hwnd, UINT m if (itemDisabled) { - g_sharedDC.listviewDC = nullptr; + g_sharedContext.listviewDC = nullptr; return; } @@ -86,9 +84,9 @@ void TranslucentFlyouts::MenuHandler::ListviewpopupMsgCallback(HWND hwnd, UINT m if (Utils::IsWindowClass(hwnd, L"Listviewpopup")) { HDC hdc{reinterpret_cast(wParam)}; - RECT paintRect{}; - GetClipBox(hdc, &paintRect); - FillRect(hdc, &paintRect, GetStockBrush(BLACK_BRUSH)); + RECT clipRect{}; + GetClipBox(hdc, &clipRect); + PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS); } } @@ -98,41 +96,278 @@ void TranslucentFlyouts::MenuHandler::ListviewpopupMsgCallback(HWND hwnd, UINT m if (!returnResultValid) { - if (drawItemStruct.CtlType == ODT_LISTVIEW && hwnd == GetParent(drawItemStruct.hwndItem)) + if (drawItemStruct.CtlType == ODT_LISTVIEW) { - g_sharedDC.listviewDC = drawItemStruct.hDC; + g_sharedContext.listviewDC = drawItemStruct.hDC; + + PatBlt(drawItemStruct.hDC, drawItemStruct.rcItem.left, drawItemStruct.rcItem.top, drawItemStruct.rcItem.right - drawItemStruct.rcItem.left, drawItemStruct.rcItem.bottom - drawItemStruct.rcItem.top, BLACKNESS); } } if (returnResultValid) { - if (drawItemStruct.CtlType == ODT_LISTVIEW && hwnd == GetParent(drawItemStruct.hwndItem)) + if (drawItemStruct.CtlType == ODT_LISTVIEW) + { + g_sharedContext.listviewDC = nullptr; + } + } + } +} + +void MenuHandler::HandleSysBorderColors(std::wstring_view keyName, HWND hWnd, bool useDarkMode, COLORREF color) +{ + DWORD noBorderColor + { + RegHelper::GetDword( + keyName, + L"NoBorderColor", + 0 + ) + }; + + DWORD borderColor{color}; + if (!noBorderColor) + { + try + { + DWORD enableThemeColorization + { + RegHelper::GetDword( + keyName, + L"EnableThemeColorization", + 0 + ) + }; + + THROW_HR_IF(E_NOTIMPL, !enableThemeColorization); + THROW_IF_FAILED(Utils::GetDwmThemeColor(borderColor)); + } + catch (...) + { + if (ResultFromCaughtException() != E_NOTIMPL) + { + LOG_CAUGHT_EXCEPTION(); + } + } + + if (useDarkMode) + { + borderColor = RegHelper::GetDword( + keyName, + L"DarkMode_BorderColor", + borderColor + ); + } + else + { + borderColor = RegHelper::GetDword( + keyName, + L"LightMode_BorderColor", + borderColor + ); + } + + borderColor = Utils::MakeCOLORREF(borderColor); + } + else + { + borderColor = DWMWA_COLOR_NONE; + } + + DwmSetWindowAttribute(hWnd, DWMWA_BORDER_COLOR, &borderColor, sizeof(borderColor)); +} + +bool MenuHandler::HandlePopupMenuNCBorderColors(HDC hdc, bool useDarkMode, const RECT& paintRect) +{ + DWORD noBorderColor + { + RegHelper::GetDword( + L"Menu", + L"NoBorderColor", + 0 + ) + }; + + DWORD borderColor{DWMWA_COLOR_NONE}; + + // Border color is enabled. + if (!noBorderColor) + { + try + { + DWORD enableThemeColorization + { + RegHelper::GetDword( + L"Menu", + L"EnableThemeColorization", + 0 + ) + }; + + THROW_HR_IF(E_NOTIMPL, !enableThemeColorization); + THROW_IF_FAILED(Utils::GetDwmThemeColor(borderColor)); + } + catch (...) + { + if (ResultFromCaughtException() != E_NOTIMPL) { - g_sharedDC.listviewDC = nullptr; + LOG_CAUGHT_EXCEPTION(); } } + + if (useDarkMode) + { + borderColor = RegHelper::GetDword( + L"Menu", + L"DarkMode_BorderColor", + borderColor + ); + } + else + { + borderColor = RegHelper::GetDword( + L"Menu", + L"LightMode_BorderColor", + borderColor + ); + } + + if (borderColor == DWMWA_COLOR_NONE) + { + return false; + } + + unique_hbrush brush{Utils::CreateSolidColorBrushWithAlpha(Utils::MakeCOLORREF(borderColor), Utils::GetAlpha(borderColor))}; + LOG_LAST_ERROR_IF_NULL(brush); + if (brush) + { + LOG_LAST_ERROR_IF(!FrameRect(hdc, &paintRect, brush.get())); + } + } + + return true; +} + +void MenuHandler::HandleRoundCorners(std::wstring_view keyName, HWND hWnd) +{ + DWORD cornerType + { + RegHelper::GetDword( + keyName, + L"CornerType", + 3 + ) + }; + if (cornerType != 0) + { + DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerType, sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } +} + +void MenuHandler::ApplyEffect(std::wstring_view keyName, HWND hWnd, bool darkMode, bool noDropShadow) +{ + DWORD effectType + { + RegHelper::GetDword( + keyName, + L"EffectType", + static_cast(EffectHelper::EffectType::ModernAcrylicBlur) + ) + }; + DWORD enableDropShadow + { + RegHelper::GetDword( + keyName, + L"EnableDropShadow", + 0 + ) + }; + // Set effect for the popup menu + DWORD gradientColor{0}; + if (darkMode) + { + gradientColor = RegHelper::GetDword( + keyName, + L"DarkMode_GradientColor", + darkMode_GradientColor + ); + + EffectHelper::EnableWindowDarkMode(hWnd, TRUE); + } + else + { + gradientColor = RegHelper::GetDword( + keyName, + L"LightMode_GradientColor", + lightMode_GradientColor + ); + + } + EffectHelper::SetWindowBackdrop(hWnd, noDropShadow ? FALSE : enableDropShadow, gradientColor, effectType); + DwmTransitionOwnedWindow(hWnd, DWMTRANSITION_OWNEDWINDOW_REPOSITION); +} + +bool MenuHandler::IsMenuPartlyOwnerDrawn(HMENU hMenu) +{ + auto ownerDrawnMenuItem{0}; + auto totMenuItem{GetMenuItemCount(hMenu)}; + for (auto i = 0; i < totMenuItem; i++) + { + MENUITEMINFOW mii{sizeof(mii), MIIM_FTYPE}; + if (GetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + ownerDrawnMenuItem += (mii.fType & MFT_OWNERDRAW ? 1 : 0); + } } + + return (totMenuItem != ownerDrawnMenuItem && ownerDrawnMenuItem != 0); } -LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +MenuHandler::MenuRenderingInfo MenuHandler::GetMenuRenderingInfo(HWND hWnd) try +{ + auto info{g_sharedMenuInfo}; + g_sharedMenuInfo.Reset(); + + unique_hdc memoryDC{CreateCompatibleDC(nullptr)}; + THROW_LAST_ERROR_IF_NULL(memoryDC); + unique_hbitmap bitmap{CreateCompatibleBitmap(memoryDC.get(), 1, 1)}; + THROW_LAST_ERROR_IF_NULL(bitmap); + + { + auto selectedObject{wil::SelectObject(memoryDC.get(), bitmap.get())}; + SendMessageW(hWnd, WM_PRINT, reinterpret_cast(memoryDC.get()), PRF_CHILDREN | PRF_NONCLIENT); + } + + swap(g_sharedMenuInfo, info); + return info; +} +catch (...) +{ + return{}; +} + +LRESULT CALLBACK MenuHandler::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { bool handled{false}; LRESULT result{0}; + MenuHandler& menuHandler{GetInstance()}; // Popup menu if (uIdSubclass == popupMenuSubclassId) { // If we can handle WM_UAHDRAWMENU, WM_UAHDRAWMENUITEM, WM_UAHNCPAINTMENUPOPUP properly, (like StartAllBack) - // then we don't need TranslucentFlyouts::UxThemePatcher anymore!!! + // then we don't need UxThemePatcher anymore!!! // However, this method may break the vanilla behaviour of popup menu + + // If this menu is a immersive context menu, then it won't receive following 4 messages... if (uMsg == WM_UAHDRAWMENU) { handled = true; auto& uah{*reinterpret_cast(lParam)}; - g_sharedDC.menuDC = uah.hdc; + g_sharedContext.menuDC = uah.hdc; result = DefSubclassProc(hWnd, uMsg, wParam, lParam); - g_sharedDC.menuDC = nullptr; + g_sharedContext.menuDC = nullptr; } if (uMsg == WM_UAHDRAWMENUITEM) @@ -140,9 +375,14 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u handled = true; auto& uah{*reinterpret_cast(lParam)}; - g_sharedDC.menuDC = uah.dis.hDC; + g_sharedContext.menuDC = uah.dis.hDC; + // This is a patch which in order to remove the ugly white line at the end of the popup menu... + if (uah.dis.CtlID == GetMenuItemID(uah.um.hMenu, GetMenuItemCount(uah.um.hMenu) - 1)) + { + PatBlt(uah.dis.hDC, uah.dis.rcItem.left, uah.dis.rcItem.top, uah.dis.rcItem.right - uah.dis.rcItem.left, uah.dis.rcItem.bottom - uah.dis.rcItem.top, BLACKNESS); + } result = DefSubclassProc(hWnd, uMsg, wParam, lParam); - g_sharedDC.menuDC = nullptr; + g_sharedContext.menuDC = nullptr; } if (uMsg == WM_UAHINITMENU) @@ -156,9 +396,12 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u handled = true; auto& uah{*reinterpret_cast(lParam)}; - g_sharedDC.menuDC = uah.hdc; + g_sharedContext.menuDC = uah.hdc; result = DefSubclassProc(hWnd, uMsg, wParam, lParam); - g_sharedDC.menuDC = nullptr; + g_sharedContext.menuDC = nullptr; + + GetInstance().HandleSysBorderColors(L"Menu"sv, hWnd, g_sharedMenuInfo.useDarkMode, g_sharedMenuInfo.borderColor); + GetInstance().HandleRoundCorners(L"Menu"sv, hWnd); } // We need to fix the menu selection fade animation... @@ -214,183 +457,36 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u handled = true; result = DefSubclassProc(hWnd, uMsg, wParam, lParam); - HWND menuOwner{Utils::GetCurrentMenuOwner()}; - - if (menuOwner != nullptr) - { - SetWindowSubclass(hWnd, SubclassProc, popupMenuSubclassId, reinterpret_cast(menuOwner)); - Hooking::MsgHooks::GetInstance().Install(menuOwner); - GetInstance().m_hookedWindowList.push_back(menuOwner); - } + menuHandler.AttachPopupMenuOwner(hWnd); // This menu is using unknown owner drawn technique, // in order to prevent broken visual content, we need to detach and remove menu backdrop - try + auto info{menuHandler.GetMenuRenderingInfo(hWnd)}; + if (!info.useUxTheme || menuHandler.IsMenuPartlyOwnerDrawn(reinterpret_cast(DefSubclassProc(hWnd, MN_GETHMENU, 0, 0)))) { - g_sharedMenuInfo.useUxTheme = false; - g_sharedMenuInfo.useDarkMode = false; - - { - wil::unique_hdc memoryDC{CreateCompatibleDC(nullptr)}; - THROW_LAST_ERROR_IF_NULL(memoryDC); - wil::unique_hbitmap bitmap{CreateCompatibleBitmap(memoryDC.get(), 1, 1)}; - THROW_LAST_ERROR_IF_NULL(bitmap); - - { - auto selectedObject{wil::SelectObject(memoryDC.get(), bitmap.get())}; - THROW_IF_WIN32_BOOL_FALSE(PrintWindow(hWnd, memoryDC.get(), 0)); - } - - THROW_HR_IF(E_NOTIMPL, !g_sharedMenuInfo.useUxTheme); - } - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); SendNotifyMessage(hWnd, WM_MHDETACH, 0, 0); - return result; - } - - DWORD effectType - { - RegHelper::GetDword( - L"Menu", - L"EffectType", - static_cast(EffectHelper::EffectType::ModernAcrylicBlur) - ) - }; - - DWORD enableDropShadow - { - RegHelper::GetDword( - L"Menu", - L"EnableDropShadow", - 0 - ) - }; - - // Set effect for the popup menu - DWORD opacity{0}; - DWORD gradientColor{0}; - if (g_sharedMenuInfo.useDarkMode) - { - opacity = RegHelper::GetDword( - L"Menu", - L"DarkMode_Opacity", - 65 - ); - gradientColor = RegHelper::GetDword( - L"Menu", - L"DarkMode_GradientColor", - 0x2B2B2B - ); - - EffectHelper::EnableWindowDarkMode(hWnd, TRUE); - EffectHelper::SetWindowBackdrop(hWnd, enableDropShadow, (gradientColor | (opacity << 24)), effectType); } else { - opacity = RegHelper::GetDword( - L"Menu", - L"LightMode_Opacity", - 158 - ); - gradientColor = RegHelper::GetDword( - L"Menu", - L"LightMode_GradientColor", - 0xDDDDDD - ); - - EffectHelper::SetWindowBackdrop(hWnd, enableDropShadow, (gradientColor | (opacity << 24)), effectType); + menuHandler.ApplyEffect(L"Menu"sv, hWnd, info.useDarkMode); + GetInstance().HandleSysBorderColors(L"Menu"sv, hWnd, info.useDarkMode, info.borderColor); + GetInstance().HandleRoundCorners(L"Menu"sv, hWnd); } - // Whether to remove the system outline? - DWORD noOutline + DWORD enableFluentAnimation { RegHelper::GetDword( L"Menu", - L"NoSystemOutline", + L"EnableFluentAnimation", 0, false ) }; - if (noOutline) + if (enableFluentAnimation) { - SetPropW(hWnd, L"IsZachMenuDWMAttributeSet", reinterpret_cast(HANDLE_FLAG_INHERIT)); - - DWORD noBorderColor - { - RegHelper::GetDword( - L"Menu\\Border", - L"NoBorderColor", - 0, - false - ) - }; - - DWORD borderColor{DWMWA_COLOR_NONE}; - if (noBorderColor != 1) - { - if (g_sharedMenuInfo.useDarkMode) - { - borderColor = RegHelper::GetDword( - L"Menu\\Border", - L"DarkMode_Color", - 0x303030 - ); - } - else - { - borderColor = RegHelper::GetDword( - L"Menu\\Border", - L"LightMode_Color", - 0xD9D9D9 - ); - } - - try - { - DWORD enableThemeColorization - { - RegHelper::GetDword( - L"Menu\\Border", - L"EnableThemeColorization", - 0, - false - ) - }; - - DWORD opacity{0}; - - THROW_HR_IF(E_NOTIMPL, !enableThemeColorization); - THROW_IF_FAILED(Utils::GetDwmThemeColor(borderColor, opacity)); - } - catch (...) - { - if (wil::ResultFromCaughtException() != E_NOTIMPL) - { - LOG_CAUGHT_EXCEPTION(); - } - } - } - - DwmSetWindowAttribute(hWnd, DWMWA_BORDER_COLOR, &borderColor, sizeof(borderColor)); - - DWORD cornerType - { - RegHelper::GetDword( - L"Menu\\Border", - L"CornerType", - 3, - false - ) - }; - if (cornerType == 0) - { - cornerType = 3; - } - auto roundCorner{static_cast(cornerType)}; - DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &roundCorner, sizeof(roundCorner)); + MenuAnimation::CreatePopupIn( + hWnd, MenuAnimation::standardStartPosRatio, MenuAnimation::standardPopupInDuration, MenuAnimation::standardFadeInDuration + ); } } @@ -403,64 +499,26 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u POINT pt{}; Utils::unique_ext_hdc hdc{reinterpret_cast(wParam)}; - RECT paintRect{}; - GetClipBox(hdc.get(), &paintRect); - FillRect(hdc.get(), &paintRect, GetStockBrush(BLACK_BRUSH)); + RECT windowRect{}; + GetWindowRect(hWnd, &windowRect); + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + + { + Utils::unique_ext_hdc dc{hdc.get()}; + ExcludeClipRect(dc.get(), windowRect.left + nonClientMarginSize, windowRect.top + nonClientMarginSize, windowRect.right - nonClientMarginSize, windowRect.bottom - nonClientMarginSize); + FillRect(dc.get(), &windowRect, GetStockBrush(BLACK_BRUSH)); + } SetViewportOrgEx(hdc.get(), nonClientMarginSize, nonClientMarginSize, &pt); result = DefSubclassProc(hWnd, WM_PRINTCLIENT, wParam, lParam); + SetViewportOrgEx(hdc.get(), pt.x, pt.y, nullptr); - // Whether to keep the system outline? - DWORD noOutline - { - RegHelper::GetDword( - L"Menu", - L"NoSystemOutline", - 0, - false - ) - }; - if (noOutline == 0) + if (!menuHandler.HandlePopupMenuNCBorderColors(hdc.get(), g_sharedMenuInfo.useDarkMode, windowRect)) { Utils::unique_ext_hdc dc{hdc.get()}; - SetViewportOrgEx(dc.get(), pt.x, pt.y, nullptr); - ExcludeClipRect(dc.get(), paintRect.left + systemOutlineSize, paintRect.top + systemOutlineSize, paintRect.right - systemOutlineSize, paintRect.bottom - systemOutlineSize); + ExcludeClipRect(dc.get(), windowRect.left + systemOutlineSize, windowRect.top + systemOutlineSize, windowRect.right - systemOutlineSize, windowRect.bottom - systemOutlineSize); result = DefSubclassProc(hWnd, WM_PRINT, wParam, lParam); } - else - { - SetViewportOrgEx(hdc.get(), pt.x, pt.y, nullptr); - hdc.reset(reinterpret_cast(wParam)); - - try - { - DWORD enableThemeColorization - { - RegHelper::GetDword( - L"Menu\\Border", - L"EnableThemeColorization", - 0, - false - ) - }; - - COLORREF color{0}; - DWORD opacity{0}; - - THROW_HR_IF(E_NOTIMPL, !enableThemeColorization); - THROW_IF_FAILED(Utils::GetDwmThemeColor(color, opacity)); - wil::unique_hbrush brush{Utils::CreateSolidColorBrushWithAlpha(color, static_cast(opacity))}; - THROW_LAST_ERROR_IF_NULL(brush); - THROW_LAST_ERROR_IF(FrameRect(hdc.get(), &paintRect, brush.get()) == 0); - } - catch (...) - { - if (wil::ResultFromCaughtException() != E_NOTIMPL) - { - LOG_CAUGHT_EXCEPTION(); - } - } - } } } @@ -477,56 +535,21 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u SelectClipRgn(hdc.get(), reinterpret_cast(wParam)); } - RECT paintRect{}; - GetClipBox(hdc.get(), &paintRect); - FillRect(hdc.get(), &paintRect, GetStockBrush(BLACK_BRUSH)); + RECT windowRect{}; + GetWindowRect(hWnd, &windowRect); + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); - // Whether to keep the system outline? - DWORD noOutline - { - RegHelper::GetDword( - L"Menu", - L"NoSystemOutline", - 0, - false - ) - }; - if (noOutline == 0) { Utils::unique_ext_hdc dc{hdc.get()}; - ExcludeClipRect(dc.get(), paintRect.left + systemOutlineSize, paintRect.top + systemOutlineSize, paintRect.right - systemOutlineSize, paintRect.bottom - systemOutlineSize); - result = DefSubclassProc(hWnd, WM_PRINT, reinterpret_cast(dc.get()), lParam); + ExcludeClipRect(dc.get(), windowRect.left + nonClientMarginSize, windowRect.top + nonClientMarginSize, windowRect.right - nonClientMarginSize, windowRect.bottom - nonClientMarginSize); + FillRect(dc.get(), &windowRect, GetStockBrush(BLACK_BRUSH)); } - else + + if (!menuHandler.HandlePopupMenuNCBorderColors(hdc.get(), g_sharedMenuInfo.useDarkMode, windowRect)) { - try - { - DWORD enableThemeColorization - { - RegHelper::GetDword( - L"Menu\\Border", - L"EnableThemeColorization", - 0, - false - ) - }; - - COLORREF color{0}; - DWORD opacity{0}; - - THROW_HR_IF(E_NOTIMPL, !enableThemeColorization); - THROW_IF_FAILED(Utils::GetDwmThemeColor(color, opacity)); - wil::unique_hbrush brush{Utils::CreateSolidColorBrushWithAlpha(color, static_cast(opacity))}; - THROW_LAST_ERROR_IF_NULL(brush); - THROW_LAST_ERROR_IF(FrameRect(hdc.get(), &paintRect, brush.get()) == 0); - } - catch (...) - { - if (wil::ResultFromCaughtException() != E_NOTIMPL) - { - LOG_CAUGHT_EXCEPTION(); - } - } + Utils::unique_ext_hdc dc{hdc.get()}; + ExcludeClipRect(dc.get(), windowRect.left + systemOutlineSize, windowRect.top + systemOutlineSize, windowRect.right - systemOutlineSize, windowRect.bottom - systemOutlineSize); + DefSubclassProc(hWnd, WM_PRINT, reinterpret_cast(dc.get()), lParam); } } } @@ -536,56 +559,43 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u handled = true; HDC hdc{reinterpret_cast(wParam)}; - if (IsImmersiveContextMenu(hWnd)) - { - RECT paintRect{}; - GetClipBox(hdc, &paintRect); - FillRect(hdc, &paintRect, GetStockBrush(BLACK_BRUSH)); - } - g_sharedDC.menuDC = hdc; + g_sharedContext.menuDC = hdc; result = DefSubclassProc(hWnd, uMsg, wParam, lParam); - g_sharedDC.menuDC = nullptr; - } + g_sharedContext.menuDC = nullptr; - if (uMsg == WM_WINDOWPOSCHANGED) - { - WINDOWPOS& wp{*reinterpret_cast(lParam)}; - if (!(wp.flags & SWP_NOMOVE)) + if (IsImmersiveContextMenu(hWnd)) { - InvalidateRect(hWnd, nullptr, TRUE); - DwmTransitionOwnedWindow(hWnd, DWMTRANSITION_OWNEDWINDOW_TARGET::DWMTRANSITION_OWNEDWINDOW_REPOSITION); + RECT clipRect{}; + GetClipBox(hdc, &clipRect); + PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS); } } if (uMsg == WM_NCDESTROY || uMsg == WM_MHDETACH) { - HWND menuOwner{reinterpret_cast(dwRefData)}; if (uMsg == WM_MHDETACH) { - handled = true; RemovePropW(hWnd, L"IsZachMenuDWMAttributeSet"); EffectHelper::SetWindowBackdrop(hWnd, FALSE, 0, static_cast(EffectHelper::EffectType::None)); InvalidateRect(hWnd, nullptr, TRUE); } - GetInstance().DetachPopupMenu(hWnd); - Hooking::MsgHooks::GetInstance().Uninstall(menuOwner); - GetInstance().m_hookedWindowList.remove(menuOwner); + + menuHandler.DetachPopupMenuOwner(hWnd); + menuHandler.DetachPopupMenu(hWnd); } } - if (uIdSubclass == dropDownSubclassId) { if (uMsg == WM_NCDESTROY || uMsg == WM_MHDETACH) { if (uMsg == WM_MHDETACH) { - handled = true; EffectHelper::SetWindowBackdrop(hWnd, FALSE, 0, static_cast(EffectHelper::EffectType::None)); InvalidateRect(hWnd, nullptr, TRUE); } - GetInstance().DetachDropDown(hWnd); + menuHandler.DetachDropDown(hWnd); } } @@ -597,7 +607,7 @@ LRESULT CALLBACK TranslucentFlyouts::MenuHandler::SubclassProc(HWND hWnd, UINT u return result; } -void TranslucentFlyouts::MenuHandler::AttachPopupMenu(HWND hWnd) +void MenuHandler::AttachPopupMenu(HWND hWnd) { DWORD itemDisabled { @@ -616,59 +626,53 @@ void TranslucentFlyouts::MenuHandler::AttachPopupMenu(HWND hWnd) SetWindowSubclass(hWnd, SubclassProc, popupMenuSubclassId, 0); } -void TranslucentFlyouts::MenuHandler::DetachPopupMenu(HWND hWnd) +void MenuHandler::DetachPopupMenu(HWND hWnd) { RemoveWindowSubclass(hWnd, SubclassProc, popupMenuSubclassId); m_menuList.remove(hWnd); } -void TranslucentFlyouts::MenuHandler::AttachDropDown(HWND hWnd) +void MenuHandler::AttachDropDown(HWND hWnd) { - // DropDown doesn't support dark mode, so here we only get the values used in light mode - DWORD effectType - { - RegHelper::GetDword( - L"DropDown", - L"EffectType", - static_cast(EffectHelper::EffectType::ModernAcrylicBlur) - ) - }; - DWORD enableDropShadow - { - RegHelper::GetDword( - L"DropDown", - L"EnableDropShadow", - 0 - ) - }; - DWORD opacity - { - RegHelper::GetDword( - L"DropDown", - L"LightMode_Opacity", - 158 - ) - }; - DWORD gradientColor - { - RegHelper::GetDword( - L"DropDown", - L"LightMode_GradientColor", - 0xDDDDDD - ) - }; + auto info{GetMenuRenderingInfo(hWnd)}; + ApplyEffect(L"DropDown"sv, hWnd, info.useDarkMode); + GetInstance().HandleSysBorderColors(L"DropDown"sv, hWnd, info.useDarkMode, DWMWA_COLOR_NONE); + GetInstance().HandleRoundCorners(L"DropDown"sv, hWnd); + m_menuList.push_back(hWnd); SetWindowSubclass(hWnd, SubclassProc, dropDownSubclassId, 0); - EffectHelper::SetWindowBackdrop(hWnd, enableDropShadow, (gradientColor | (opacity << 24)), effectType); } -void TranslucentFlyouts::MenuHandler::DetachDropDown(HWND hWnd) +void MenuHandler::DetachDropDown(HWND hWnd) { RemoveWindowSubclass(hWnd, SubclassProc, dropDownSubclassId); m_menuList.remove(hWnd); } -void TranslucentFlyouts::MenuHandler::AttachListViewPopup(HWND hWnd) +void MenuHandler::AttachPopupMenuOwner(HWND hWnd) +{ + HWND menuOwner{Utils::GetCurrentMenuOwner()}; + + if (menuOwner) + { + SetWindowSubclass(hWnd, SubclassProc, popupMenuSubclassId, reinterpret_cast(menuOwner)); + Hooking::MsgHooks::GetInstance().Install(menuOwner); + m_hookedWindowList.push_back(menuOwner); + } +} + +void MenuHandler::DetachPopupMenuOwner(HWND hWnd) +{ + HWND menuOwner{nullptr}; + + if (GetWindowSubclass(hWnd, SubclassProc, popupMenuSubclassId, reinterpret_cast(&menuOwner)) && menuOwner) + { + Hooking::MsgHooks::GetInstance().Uninstall(menuOwner); + m_hookedWindowList.remove(menuOwner); + } +} + +void MenuHandler::AttachListViewPopup(HWND hWnd) { DWORD itemDisabled { @@ -684,41 +688,53 @@ void TranslucentFlyouts::MenuHandler::AttachListViewPopup(HWND hWnd) return; } - HWND dropDown{GetAncestor(hWnd, GA_ROOT)}; - AttachDropDown(dropDown); Hooking::MsgHooks::GetInstance().Install(hWnd); m_hookedWindowList.push_back(hWnd); + + HWND dropDown{GetAncestor(hWnd, GA_ROOT)}; + AttachDropDown(dropDown); } -void TranslucentFlyouts::MenuHandler::DetachListViewPopup(HWND hWnd) +void MenuHandler::DetachListViewPopup(HWND hWnd) { HWND dropDown{GetAncestor(hWnd, GA_ROOT)}; DetachDropDown(dropDown); + Hooking::MsgHooks::GetInstance().Uninstall(hWnd); m_hookedWindowList.remove(hWnd); } -HDC TranslucentFlyouts::MenuHandler::GetCurrentMenuDC() +HDC MenuHandler::GetCurrentMenuDC() { - return g_sharedDC.menuDC; + return g_sharedContext.menuDC; } -HDC TranslucentFlyouts::MenuHandler::GetCurrentListviewDC() +HDC MenuHandler::GetCurrentListviewDC() { - return g_sharedDC.listviewDC; + return g_sharedContext.listviewDC; } -void TranslucentFlyouts::MenuHandler::NotifyUxThemeRendering() +void MenuHandler::NotifyUxThemeRendering() { g_sharedMenuInfo.useUxTheme = true; } -void TranslucentFlyouts::MenuHandler::NotifyMenuDarkMode(bool darkMode) +void MenuHandler::NotifyMenuDarkMode(bool darkMode) { g_sharedMenuInfo.useDarkMode = darkMode; } -bool TranslucentFlyouts::MenuHandler::IsImmersiveContextMenu(HWND hWnd) +void MenuHandler::NotifyMenuStyle(bool immersive) +{ + g_sharedMenuInfo.immersive = immersive; +} + +void MenuHandler::NotifyMenuBorderColor(COLORREF color) +{ + g_sharedMenuInfo.borderColor = color; +} + +bool MenuHandler::IsImmersiveContextMenu(HWND hWnd) { bool result{false}; auto menu{reinterpret_cast(DefSubclassProc(hWnd, MN_GETHMENU, 0, 0))}; @@ -735,14 +751,37 @@ bool TranslucentFlyouts::MenuHandler::IsImmersiveContextMenu(HWND hWnd) return result; } -void TranslucentFlyouts::MenuHandler::StartupHook() +void MenuHandler::WinEventCallback(HWND hWnd, DWORD event) +{ + auto& menuHandler{GetInstance()}; + + if (event == EVENT_OBJECT_CREATE) + { + if (Utils::IsWin32PopupMenu(hWnd)) + { + menuHandler.AttachPopupMenu(hWnd); + } + + HWND parentWindow{GetParent(hWnd)}; + if (Utils::IsWindowClass(hWnd, L"Listviewpopup") && Utils::IsWindowClass(parentWindow, L"DropDown")) + { + menuHandler.AttachListViewPopup(hWnd); + } + } +} + +void MenuHandler::StartupHook() { Hooking::MsgHooks::GetInstance().AddCallback(MenuOwnerMsgCallback); Hooking::MsgHooks::GetInstance().AddCallback(ListviewpopupMsgCallback); + + MainDLL::GetInstance().AddCallback(WinEventCallback); } -void TranslucentFlyouts::MenuHandler::ShutdownHook() +void MenuHandler::ShutdownHook() { + MainDLL::GetInstance().DeleteCallback(WinEventCallback); + Hooking::MsgHooks::GetInstance().DeleteCallback(MenuOwnerMsgCallback); Hooking::MsgHooks::GetInstance().DeleteCallback(ListviewpopupMsgCallback); // Remove all the hooks diff --git a/TFMain/MenuHandler.hpp b/TFMain/MenuHandler.hpp index 007fd53..cbd4cf9 100644 --- a/TFMain/MenuHandler.hpp +++ b/TFMain/MenuHandler.hpp @@ -64,6 +64,16 @@ namespace TranslucentFlyouts UAHMENUITEM umi; }; + struct MenuRenderingInfo + { + bool useUxTheme{false}; + bool useDarkMode{false}; + bool immersive{false}; + COLORREF borderColor{DWMWA_COLOR_NONE}; + + inline void Reset() { useUxTheme = false; useDarkMode = false; immersive = false; } + }; + static MenuHandler& GetInstance(); ~MenuHandler() noexcept; MenuHandler(const MenuHandler&) = delete; @@ -76,38 +86,50 @@ namespace TranslucentFlyouts static void NotifyUxThemeRendering(); // Indicate that the menu window is using dark mode or light mode static void NotifyMenuDarkMode(bool darkMode); - - void AttachPopupMenu(HWND hWnd); - void AttachListViewPopup(HWND hWnd); + // Indicate that it is a immersive style menu + static void NotifyMenuStyle(bool immersive); + // Indicate its border color (Windows 11) + static void NotifyMenuBorderColor(COLORREF color); void StartupHook(); void ShutdownHook(); - static constexpr COLORREF lightMode_HotColor{RGB(0, 0, 0)}; - static constexpr COLORREF lightMode_HotOpacity{48}; - static constexpr COLORREF darkMode_HotColor{RGB(128, 128, 128)}; - static constexpr COLORREF darkMode_HotOpacity{65}; - - static constexpr COLORREF lightMode_DisabledHotColor{RGB(0, 0, 0)}; - static constexpr COLORREF lightMode_DisabledHotOpacity{0}; - static constexpr COLORREF darkMode_DisabledHotColor{RGB(0, 0, 0)}; - static constexpr COLORREF darkMode_DisabledHotOpacity{0}; - - static constexpr COLORREF lightMode_SeparatorColor{RGB(38, 38, 38)}; - static constexpr COLORREF lightMode_SeparatorOpacity{48}; - static constexpr COLORREF darkMode_SeparatorColor{RGB(217, 217, 217)}; - static constexpr COLORREF darkMode_SeparatorOpacity{48}; - - static constexpr COLORREF lightMode_FocusingColor{RGB(0, 0, 0)}; - static constexpr COLORREF lightMode_FocusingOpacity{255}; - static constexpr COLORREF darkMode_FocusingColor{RGB(255, 255, 255)}; - static constexpr COLORREF darkMode_FocusingOpacity{255}; - static constexpr DWORD focusingWidth{1}; - - static constexpr DWORD cornerRadius{5}; - static constexpr DWORD separatorWidth{1}; + + MenuRenderingInfo GetMenuRenderingInfo(HWND hWnd); + void ApplyEffect(std::wstring_view keyName, HWND hWnd, bool darkMode, bool noDropShadow = false); + void HandleSysBorderColors(std::wstring_view keyName, HWND hWnd, bool useDarkMode, COLORREF color); + void HandleRoundCorners(std::wstring_view keyName, HWND hWnd); + bool HandlePopupMenuNCBorderColors(HDC hdc, bool useDarkMode, const RECT& paintRect); + + static constexpr DWORD lightMode_GradientColor{0x9EDDDDDD}; + static constexpr DWORD darkMode_GradientColor{0x412B2B2B}; + + static constexpr DWORD lightMode_HotColor{0x30000000}; + static constexpr DWORD darkMode_HotColor{0x41808080}; + + static constexpr DWORD lightMode_DisabledHotColor{0x00000000}; + static constexpr DWORD darkMode_DisabledHotColor{0x00000000}; + + static constexpr DWORD lightMode_SeparatorColor{0x30262626}; + static constexpr DWORD darkMode_SeparatorColor{0x30D9D9D9}; + + static constexpr DWORD lightMode_FocusingColor{0xFF000000}; + static constexpr DWORD darkMode_FocusingColor{0xFFFFFFFF}; + + static constexpr DWORD focusingWidth{1000}; + static constexpr DWORD cornerRadius{8}; + static constexpr DWORD separatorWidth{1000}; + + static constexpr int systemOutlineSize{1}; private: + struct MenuRenderingContext + { + HDC menuDC{nullptr}; + HDC listviewDC{nullptr}; + }; + MenuHandler(); + static void WinEventCallback(HWND hWnd, DWORD event); // In certain situations, using SetWindowSubclass can't receive WM_DRAWITEM (eg. Windows 11 Taskmgr), // so here we use hooks instead static void MenuOwnerMsgCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT lResult, bool returnResultValid); @@ -118,9 +140,18 @@ namespace TranslucentFlyouts void AttachDropDown(HWND hWnd); void DetachDropDown(HWND hWnd); + + void AttachPopupMenuOwner(HWND hWnd); + void DetachPopupMenuOwner(HWND hWnd); + + void AttachPopupMenu(HWND hWnd); void DetachPopupMenu(HWND hWnd); + + void AttachListViewPopup(HWND hWnd); void DetachListViewPopup(HWND hWnd); + bool IsMenuPartlyOwnerDrawn(HMENU hMenu); + static constexpr UINT WM_UAHDESTROYWINDOW{0x0090}; static constexpr UINT WM_UAHDRAWMENU{0x0091}; // lParam is UAHMENU, return TRUE after handling it static constexpr UINT WM_UAHDRAWMENUITEM{0x0092}; // lParam is UAHDRAWMENUITEM, return TRUE after handling it @@ -151,21 +182,11 @@ namespace TranslucentFlyouts static constexpr UINT MN_ENDMENU2{0x01F4}; static constexpr int nonClientMarginSize{3}; - static constexpr int systemOutlineSize{1}; static constexpr int popupMenuSubclassId{0}; static constexpr int dropDownSubclassId{0}; - static thread_local struct - { - HDC menuDC; - HDC listviewDC; - } g_sharedDC; - - static thread_local struct - { - bool useUxTheme; - bool useDarkMode; - } g_sharedMenuInfo; + static thread_local MenuRenderingContext g_sharedContext; + static thread_local MenuRenderingInfo g_sharedMenuInfo; static const UINT WM_MHDETACH; diff --git a/TFMain/MenuRendering.cpp b/TFMain/MenuRendering.cpp index ea038e1..ce0afee 100644 --- a/TFMain/MenuRendering.cpp +++ b/TFMain/MenuRendering.cpp @@ -5,24 +5,24 @@ #include "MenuHandler.hpp" #include "MenuRendering.hpp" -TranslucentFlyouts::MenuRendering& TranslucentFlyouts::MenuRendering::GetInstance() +using namespace std; +using namespace wil; +using namespace TranslucentFlyouts; + +MenuRendering& MenuRendering::GetInstance() { static MenuRendering instance{}; return instance; } -HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool darkMode, int partId, int stateId, const RECT& clipRect, const RECT& paintRect) +HRESULT MenuRendering::DoCustomThemeRendering(HDC hdc, bool darkMode, int partId, int stateId, const RECT& clipRect, const RECT& paintRect) { - if (!DXHelper::LazyD2D::EnsureInitialized()) - { - return E_FAIL; - } + RETURN_HR_IF(E_FAIL, !DXHelper::LazyD2D::EnsureInitialized()); auto& lazyD2D{DXHelper::LazyD2D::GetInstance()}; auto renderTarget{lazyD2D.GetRenderTarget()}; COLORREF color{0}; - DWORD opacity{0}; Utils::unique_ext_hdc dc{hdc}; IntersectClipRect(dc.get(), paintRect.left, paintRect.top, paintRect.right, paintRect.bottom); @@ -51,7 +51,7 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool { RegHelper::GetDword( L"Menu\\Separator", - L"SeparatorWidth", + L"Width", MenuHandler::separatorWidth, false ) @@ -65,12 +65,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::darkMode_SeparatorColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Separator", - L"DarkMode_Opacity", - MenuHandler::darkMode_SeparatorOpacity, - false - ); } else { @@ -80,12 +74,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::lightMode_SeparatorColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Separator", - L"LightMode_Opacity", - MenuHandler::lightMode_SeparatorOpacity, - false - ); } DWORD enableThemeColorization @@ -100,13 +88,13 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool if (enableThemeColorization) { - Utils::GetDwmThemeColor(color, opacity); + RETURN_IF_FAILED(Utils::GetDwmThemeColor(color)); } - wil::com_ptr brush{nullptr}; + com_ptr brush{nullptr}; RETURN_IF_FAILED( renderTarget->CreateSolidColorBrush( - DXHelper::COLORREF2ColorF(color, static_cast(opacity)), + DXHelper::MakeColorF(color), &brush ) ); @@ -117,7 +105,7 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool D2D1::Point2F(0.f, static_cast(paintRect.bottom - paintRect.top) / 2.f), D2D1::Point2F(static_cast(paintRect.right - paintRect.left), static_cast(paintRect.bottom - paintRect.top) / 2.f), brush.get(), - static_cast(separatorWidth) + static_cast(separatorWidth) / 1000.f ); renderTarget->EndDraw(); } @@ -155,7 +143,7 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool { RegHelper::GetDword( L"Menu\\Focusing", - L"FocusingWidth", + L"Width", MenuHandler::focusingWidth, false ) @@ -169,12 +157,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::darkMode_FocusingColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Focusing", - L"DarkMode_Opacity", - MenuHandler::darkMode_FocusingOpacity, - false - ); } else { @@ -184,12 +166,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::lightMode_FocusingColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Focusing", - L"LightMode_Opacity", - MenuHandler::lightMode_FocusingOpacity, - false - ); } DWORD enableThemeColorization @@ -204,13 +180,13 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool if (enableThemeColorization) { - Utils::GetDwmThemeColor(color, opacity); + RETURN_IF_FAILED(Utils::GetDwmThemeColor(color)); } - wil::com_ptr brush{nullptr}; + com_ptr brush{nullptr}; RETURN_IF_FAILED( renderTarget->CreateSolidColorBrush( - DXHelper::COLORREF2ColorF(color, static_cast(opacity)), + DXHelper::MakeColorF(color), &brush ) ); @@ -228,7 +204,7 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool static_cast(cornerRadius) ), brush.get(), - static_cast(focusingWidth) + static_cast(focusingWidth) / 1000.f ); renderTarget->EndDraw(); } @@ -273,12 +249,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::darkMode_DisabledHotColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\DisabledHot", - L"DarkMode_Opacity", - MenuHandler::darkMode_DisabledHotOpacity, - false - ); } else { @@ -288,12 +258,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::lightMode_DisabledHotColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\DisabledHot", - L"LightMode_Opacity", - MenuHandler::lightMode_DisabledHotOpacity, - false - ); } DWORD enableThemeColorization @@ -308,13 +272,13 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool if (enableThemeColorization) { - Utils::GetDwmThemeColor(color, opacity); + RETURN_IF_FAILED(Utils::GetDwmThemeColor(color)); } - wil::com_ptr brush{nullptr}; + com_ptr brush{nullptr}; RETURN_IF_FAILED( renderTarget->CreateSolidColorBrush( - DXHelper::COLORREF2ColorF(color, static_cast(opacity)), + DXHelper::MakeColorF(color), &brush ) ); @@ -374,12 +338,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::darkMode_HotColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Hot", - L"DarkMode_Opacity", - MenuHandler::darkMode_HotOpacity, - false - ); } else { @@ -389,12 +347,6 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool MenuHandler::lightMode_HotColor, false ); - opacity = RegHelper::GetDword( - L"Menu\\Hot", - L"LightMode_Opacity", - MenuHandler::lightMode_HotOpacity, - false - ); } DWORD enableThemeColorization @@ -409,13 +361,13 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool if (enableThemeColorization) { - Utils::GetDwmThemeColor(color, opacity); + RETURN_IF_FAILED(Utils::GetDwmThemeColor(color)); } - wil::com_ptr brush{nullptr}; + com_ptr brush{nullptr}; RETURN_IF_FAILED( renderTarget->CreateSolidColorBrush( - DXHelper::COLORREF2ColorF(color, static_cast(opacity)), + DXHelper::MakeColorF(color), &brush ) ); @@ -441,41 +393,20 @@ HRESULT TranslucentFlyouts::MenuRendering::DoCustomThemeRendering(HDC hdc, bool } } - if (partId == MENU_POPUPBORDERS) - { - DWORD enableThemeColorization - { - RegHelper::GetDword( - L"Menu\\Border", - L"EnableThemeColorization", - 0, - false - ) - }; - - RETURN_HR_IF(E_NOTIMPL, !enableThemeColorization); - RETURN_IF_FAILED(Utils::GetDwmThemeColor(color, opacity)); - wil::unique_hbrush brush{Utils::CreateSolidColorBrushWithAlpha(color, static_cast(opacity))}; - RETURN_LAST_ERROR_IF_NULL(brush); - RETURN_LAST_ERROR_IF(FrameRect(dc.get(), &clipRect, brush.get()) == 0); - - return S_OK; - } - return E_NOTIMPL; } -std::optional TranslucentFlyouts::MenuRendering::PromiseAlpha(HBITMAP bitmap) +optional MenuRendering::PromiseAlpha(HBITMAP bitmap) { if (SUCCEEDED(Utils::PrepareAlpha(bitmap))) { - return std::nullopt; + return nullopt; } - return wil::shared_hbitmap{ThemeHelper::ConvertTo32BPP(bitmap)}; + return shared_hbitmap{ThemeHelper::ConvertTo32BPP(bitmap)}; } -HRESULT TranslucentFlyouts::MenuRendering::BltWithAlpha( +HRESULT MenuRendering::BltWithAlpha( HDC hdcDest, int xDest, int yDest, @@ -491,7 +422,7 @@ HRESULT TranslucentFlyouts::MenuRendering::BltWithAlpha( RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdcDest); RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdcSrc); HBITMAP hBitmap{reinterpret_cast(GetCurrentObject(hdcSrc, OBJ_BITMAP))}; - RETURN_HR_IF_NULL(E_INVALIDARG, hBitmap); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hBitmap); RETURN_HR_IF_EXPECTED( E_NOTIMPL, ThemeHelper::IsOemBitmap(hBitmap) @@ -501,7 +432,7 @@ HRESULT TranslucentFlyouts::MenuRendering::BltWithAlpha( RETURN_LAST_ERROR_IF(bitmap && !bitmap.value().get()); auto selectedObject { - (bitmap ? wil::SelectObject(hdcSrc, bitmap.value().get()) : std::optional{std::nullopt}) + (bitmap ? wil::SelectObject(hdcSrc, bitmap.value().get()) : optional{nullopt}) }; RETURN_IF_WIN32_BOOL_FALSE( GdiAlphaBlend( diff --git a/TFMain/RegHelper.cpp b/TFMain/RegHelper.cpp index 0445eb3..71570bf 100644 --- a/TFMain/RegHelper.cpp +++ b/TFMain/RegHelper.cpp @@ -1,60 +1,60 @@ #include "pch.h" #include "RegHelper.hpp" -namespace TranslucentFlyouts::RegHelper -{ - using namespace std; +using namespace std; +using namespace wil; +using namespace::TranslucentFlyouts; - wstring_view g_regPath{L"Software\\TranslucentFlyouts"}; -} +wstring_view g_regPath{L"Software\\TranslucentFlyouts"}; -DWORD TranslucentFlyouts::RegHelper::GetDword(std::wstring_view subItemName, std::wstring_view valueName, DWORD defaultValue, bool useFallback) try +DWORD RegHelper::GetDword(std::wstring_view subItemName, std::wstring_view valueName, DWORD defaultValue, bool useFallback) { - THROW_HR_IF(E_INVALIDARG, subItemName.empty() && !useFallback); - - HRESULT hr{S_OK}; DWORD regValue{defaultValue}; - - if (!subItemName.empty()) + + [&]() { - hr = wil::reg::get_value_dword_nothrow( - HKEY_CURRENT_USER, format(L"{}\\{}", g_regPath, subItemName).c_str(), valueName.data(), ®Value - ); + HRESULT hr{S_OK}; + if (subItemName.empty() && !useFallback) + { + return; + } - if (FAILED(hr)) + if (!subItemName.empty()) { - hr = wil::reg::get_value_dword_nothrow( - HKEY_LOCAL_MACHINE, format(L"{}\\{}", g_regPath, subItemName).c_str(), valueName.data(), ®Value - ); + auto subKeyName + { + format(L"{}\\{}", g_regPath, subItemName) + }; + hr = reg::get_value_dword_nothrow( + HKEY_CURRENT_USER, subKeyName.c_str(), valueName.data(), ®Value + ); if (FAILED(hr)) { - THROW_HR_IF(hr, !useFallback); + hr = reg::get_value_dword_nothrow( + HKEY_LOCAL_MACHINE, subKeyName.c_str(), valueName.data(), ®Value + ); } - else + LOG_HR_IF(hr, FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + + if (!useFallback || SUCCEEDED(hr)) { - return regValue; + return; } } - else + + hr = reg::get_value_dword_nothrow( + HKEY_CURRENT_USER, g_regPath.data(), valueName.data(), ®Value + ); + if (FAILED(hr)) { - return regValue; + reg::get_value_dword_nothrow( + HKEY_LOCAL_MACHINE, g_regPath.data(), valueName.data(), ®Value + ); } - } - hr = wil::reg::get_value_dword_nothrow( - HKEY_CURRENT_USER, g_regPath.data(), valueName.data(), ®Value - ); - if (FAILED(hr)) - { - return wil::reg::get_value_dword( - HKEY_LOCAL_MACHINE, g_regPath.data(), valueName.data() - ); - } + LOG_HR_IF(hr, FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + }(); + return regValue; -} -catch (...) -{ - LOG_CAUGHT_EXCEPTION(); - return defaultValue; } \ No newline at end of file diff --git a/TFMain/SymbolResolver.cpp b/TFMain/SymbolResolver.cpp index 3474563..7cb850b 100644 --- a/TFMain/SymbolResolver.cpp +++ b/TFMain/SymbolResolver.cpp @@ -4,8 +4,10 @@ #include "SymbolResolver.hpp" using namespace std; +using namespace wil; +using namespace TranslucentFlyouts; -BOOL CALLBACK TranslucentFlyouts::SymbolResolver::SymCallback( +BOOL CALLBACK SymbolResolver::SymCallback( HANDLE hProcess, ULONG ActionCode, ULONG64 CallbackData, @@ -44,7 +46,7 @@ BOOL CALLBACK TranslucentFlyouts::SymbolResolver::SymCallback( return FALSE; } -TranslucentFlyouts::SymbolResolver::SymbolResolver() +SymbolResolver::SymbolResolver() { try { @@ -67,18 +69,18 @@ TranslucentFlyouts::SymbolResolver::SymbolResolver() } } -TranslucentFlyouts::SymbolResolver::~SymbolResolver() noexcept +SymbolResolver::~SymbolResolver() noexcept { SymCleanup(GetCurrentProcess()); } -HRESULT TranslucentFlyouts::SymbolResolver::Walk(std::wstring_view dllName, string_view mask, function callback) try +HRESULT SymbolResolver::Walk(std::wstring_view dllName, string_view mask, function callback) try { DWORD64 dllBase{0}; WCHAR filePath[MAX_PATH + 1]{}, symFile[MAX_PATH + 1]{}; MODULEINFO modInfo{}; - auto cleanUp = wil::scope_exit([&] + auto cleanUp = scope_exit([&] { if (dllBase != 0) { @@ -89,7 +91,7 @@ HRESULT TranslucentFlyouts::SymbolResolver::Walk(std::wstring_view dllName, stri THROW_HR_IF(E_INVALIDARG, dllName.empty()); - wil::unique_hmodule moduleHandle{LoadLibraryExW(dllName.data(), nullptr, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_SEARCH_SYSTEM32)}; + unique_hmodule moduleHandle{LoadLibraryExW(dllName.data(), nullptr, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_SEARCH_SYSTEM32)}; THROW_LAST_ERROR_IF_NULL(moduleHandle); THROW_LAST_ERROR_IF(GetModuleFileNameW(moduleHandle.get(), filePath, MAX_PATH) == 0); THROW_IF_WIN32_BOOL_FALSE(GetModuleInformation(GetCurrentProcess(), moduleHandle.get(), &modInfo, sizeof(modInfo))); @@ -107,7 +109,7 @@ HRESULT TranslucentFlyouts::SymbolResolver::Walk(std::wstring_view dllName, stri DWORD options = SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG); - auto cleanUp = wil::scope_exit([&] + auto cleanUp = scope_exit([&] { SymSetOptions(options); }); @@ -123,14 +125,14 @@ HRESULT TranslucentFlyouts::SymbolResolver::Walk(std::wstring_view dllName, stri return S_OK; } -CATCH_LOG_RETURN_HR(wil::ResultFromCaughtException()) +CATCH_LOG_RETURN_HR(ResultFromCaughtException()) -bool TranslucentFlyouts::SymbolResolver::GetLastSymbolSource() +bool SymbolResolver::GetLastSymbolSource() { return m_symbolsOK; } -BOOL TranslucentFlyouts::SymbolResolver::EnumSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) +BOOL SymbolResolver::EnumSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) { auto& callback{*reinterpret_cast*>(UserContext)}; diff --git a/TFMain/TFMain.cpp b/TFMain/TFMain.cpp new file mode 100644 index 0000000..ca501f0 --- /dev/null +++ b/TFMain/TFMain.cpp @@ -0,0 +1,220 @@ +#include "pch.h" +#include "resource.h" +#include "TFMain.hpp" + +using namespace TranslucentFlyouts; +using namespace std; + +// TranslucentFlyouts won't be loaded into one of these process +// These processes are quite annoying because TranslucentFlyouts will not be automatically unloaded by them +// Some of them even have no chance to show flyouts and other UI elements +const array g_blockList +{ + L"sihost.exe"sv, + L"WSHost.exe"sv, + L"spoolsv.exe"sv, + L"dllhost.exe"sv, + L"svchost.exe"sv, + L"taskhostw.exe"sv, + L"searchhost.exe"sv, + L"RuntimeBroker.exe"sv, + L"smartscreen.exe"sv, + L"Widgets.exe"sv, + L"WidgetService.exe"sv, + L"GameBar.exe"sv, + L"GameBarFTServer.exe"sv, + L"ShellExperienceHost.exe"sv, + L"StartMenuExperienceHost.exe"sv, + L"msedgewebview2.exe"sv +}; + +#pragma data_seg("hook") +HWINEVENTHOOK MainDLL::g_hHook {nullptr}; +#pragma data_seg() +#pragma comment(linker,"/SECTION:hook,RWS") + +MainDLL& MainDLL::GetInstance() +{ + static MainDLL instance{}; + return instance; +} + +MainDLL::MainDLL() +{ + wil::SetResultLoggingCallback([](wil::FailureInfo const & failure) noexcept + { + WCHAR logString[MAX_PATH + 1] {}; + SecureZeroMemory(logString, _countof(logString)); + if (SUCCEEDED(wil::GetFailureLogString(logString, _countof(logString), failure))) + { + //OutputDebugStringW(logString); + + /*wil::unique_hfile file + { + CreateFile2( + Utils::make_current_folder_file_str(L"debug.log").c_str(), + FILE_APPEND_DATA, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + OPEN_EXISTING, + nullptr + ) + }; + if (!file) + { + file.reset( + CreateFile2( + Utils::make_current_folder_file_str(L"debug.log").c_str(), + FILE_APPEND_DATA, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + OPEN_ALWAYS, + nullptr + ) + ); + auto header{0xFEFF}; + WriteFile(file.get(), &header, sizeof(header), nullptr, nullptr); + } + + if (file) + { + WriteFile(file.get(), logString, wcslen(logString), nullptr, nullptr); + }*/ + } + }); +} + +HRESULT MainDLL::InstallHook() +{ + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), IsHookGlobalInstalled()); + + UxThemePatcher::PrepareUxTheme(); + { + if (GetConsoleWindow()) + { + Utils::OutputModuleString(IDS_STRING104); + system("pause>nul"); + + Utils::ShutdownConsole(); + } + } + g_hHook = SetWinEventHook( + EVENT_MIN, EVENT_MAX, + HINST_THISCOMPONENT, + HandleWinEvent, + 0, 0, + WINEVENT_INCONTEXT + ); + RETURN_LAST_ERROR_IF_NULL(g_hHook); + + return S_OK; +} + +HRESULT MainDLL::UninstallHook() +{ + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_HOOK_NOT_INSTALLED), !IsHookGlobalInstalled()); + RETURN_IF_WIN32_BOOL_FALSE(UnhookWinEvent(g_hHook)); + g_hHook = nullptr; + + BroadcastSystemMessageW( + BSF_FORCEIFHUNG | BSF_FLUSHDISK | BSF_POSTMESSAGE, + nullptr, + WM_POWERBROADCAST, + PBT_APMRESUMESUSPEND, + 0 + ); + + return S_OK; +} + +bool MainDLL::IsCurrentProcessInBlockList() +{ + for (auto processName : g_blockList) + { + if (GetModuleHandleW(processName.data())) + { + return true; + } + } + + return false; +} + +void MainDLL::Startup() +{ + if (m_startup) + { + return; + } + + m_menuHandler.StartupHook(); + m_uxthemePatcher.StartupHook(); + m_immersiveContextMenuPatcher.StartupHook(); + + m_startup = true; +} +void MainDLL::Shutdown() +{ + if (!m_startup) + { + return; + } + + m_immersiveContextMenuPatcher.ShutdownHook(); + m_uxthemePatcher.ShutdownHook(); + m_menuHandler.ShutdownHook(); + + m_startup = false; +} + +void CALLBACK MainDLL::HandleWinEvent( + HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hWnd, + LONG idObject, LONG idChild, + DWORD dwEventThread, DWORD dwmsEventTime +) +{ + auto& mainDLL{GetInstance()}; + + if ( + !IsHookGlobalInstalled() || + idObject != OBJID_WINDOW || + idChild != CHILDID_SELF || + !hWnd || !IsWindow(hWnd) + ) + { + return; + } + + auto& callbackList{GetInstance().m_callbackList}; + + if (!callbackList.empty()) + { + for (const auto& callback : callbackList) + { + if (callback) + { + callback(hWnd, dwEvent); + } + } + } +} + +void MainDLL::AddCallback(Callback callback) +{ + m_callbackList.push_back(callback); +} + +void MainDLL::DeleteCallback(Callback callback) +{ + for (auto it = m_callbackList.begin(); it != m_callbackList.end();) + { + auto& callback{*it}; + if (*callback.target() == *callback.target()) + { + it = m_callbackList.erase(it); + break; + } + else + { + it++; + } + } +} \ No newline at end of file diff --git a/TFMain/TFMain.hpp b/TFMain/TFMain.hpp new file mode 100644 index 0000000..6375af2 --- /dev/null +++ b/TFMain/TFMain.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "pch.h" +#include "Utils.hpp" +#include "Hooking.hpp" +#include "ThemeHelper.hpp" +#include "EffectHelper.hpp" +#include "MenuHandler.hpp" +#include "UxThemePatcher.hpp" +#include "ImmersiveContextMenuPatcher.hpp" + +namespace TranslucentFlyouts +{ + class MainDLL + { + public: + static MainDLL& GetInstance(); + ~MainDLL() noexcept = default; + MainDLL(const MainDLL&) = delete; + MainDLL& operator=(const MainDLL&) = delete; + + static inline bool IsHookGlobalInstalled() + { + return g_hHook != nullptr; + } + static HRESULT InstallHook(); + static HRESULT UninstallHook(); + static bool IsCurrentProcessInBlockList(); + void Startup(); + void Shutdown(); + + using Callback = std::function; + + void AddCallback(Callback callback); + void DeleteCallback(Callback callback); + private: + MainDLL(); + static void CALLBACK HandleWinEvent( + HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hWnd, + LONG idObject, LONG idChild, + DWORD dwEventThread, DWORD dwmsEventTime + ); + static HWINEVENTHOOK g_hHook; + + bool m_startup{false}; + std::vector m_callbackList{}; + + MenuHandler& m_menuHandler{MenuHandler::GetInstance()}; + UxThemePatcher& m_uxthemePatcher{UxThemePatcher::GetInstance()}; + ImmersiveContextMenuPatcher& m_immersiveContextMenuPatcher{ImmersiveContextMenuPatcher::GetInstance()}; + }; +} \ No newline at end of file diff --git a/TFMain/TFMain.vcxproj b/TFMain/TFMain.vcxproj index 961bbec..73b36d4 100644 --- a/TFMain/TFMain.vcxproj +++ b/TFMain/TFMain.vcxproj @@ -120,6 +120,7 @@ true false GlobalFunctions.def + dcomp.dll;d2d1.dll;d3d11.dll;%(DelayLoadDLLs) @@ -150,6 +151,7 @@ DebugFull false GlobalFunctions.def + dcomp.dll;d2d1.dll;d3d11.dll;%(DelayLoadDLLs) @@ -168,6 +170,7 @@ true false GlobalFunctions.def + dcomp.dll;d2d1.dll;d3d11.dll;%(DelayLoadDLLs) @@ -199,10 +202,12 @@ DebugFull false GlobalFunctions.def + dcomp.dll;d2d1.dll;d3d11.dll;%(DelayLoadDLLs) + @@ -216,6 +221,7 @@ + @@ -237,6 +243,7 @@ + diff --git a/TFMain/TFMain.vcxproj.filters b/TFMain/TFMain.vcxproj.filters index 738accf..8ce1190 100644 --- a/TFMain/TFMain.vcxproj.filters +++ b/TFMain/TFMain.vcxproj.filters @@ -69,6 +69,12 @@ 头文件 + + 头文件 + + + 头文件 + @@ -104,6 +110,9 @@ 源文件 + + 源文件 + diff --git a/TFMain/ThemeHelper.hpp b/TFMain/ThemeHelper.hpp index 9321047..f26d0f9 100644 --- a/TFMain/ThemeHelper.hpp +++ b/TFMain/ThemeHelper.hpp @@ -6,6 +6,13 @@ namespace TranslucentFlyouts { namespace ThemeHelper { + static inline bool IsHighContrast() + { + HIGHCONTRASTW hc{sizeof(hc)}; + LOG_IF_WIN32_BOOL_FALSE(SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0)); + return hc.dwFlags & HCF_HIGHCONTRASTON; + } + static inline bool IsThemeAvailable(HWND hWnd = nullptr) { bool bThemeAvailable{false}; @@ -23,15 +30,15 @@ namespace TranslucentFlyouts static HRESULT GetThemeClass(HTHEME hTheme, LPCWSTR pszClassIdList, int cchClass) { - static const auto pfnGetThemeClass{reinterpret_cast(GetProcAddress(GetModuleHandleW(L"UxTheme"), MAKEINTRESOURCEA(74)))}; + static const auto actualGetThemeClass{reinterpret_cast(GetProcAddress(GetModuleHandleW(L"UxTheme"), MAKEINTRESOURCEA(74)))}; - if (pfnGetThemeClass) + if (actualGetThemeClass) { - return pfnGetThemeClass(hTheme, pszClassIdList, cchClass); + return actualGetThemeClass(hTheme, pszClassIdList, cchClass); } else { - LOG_HR_MSG(E_POINTER, "pfnGetThemeClass is invalid!"); + LOG_HR_MSG(E_POINTER, "actualGetThemeClass is invalid!"); } return E_FAIL; @@ -78,19 +85,19 @@ namespace TranslucentFlyouts UINT nGlowRadius, UINT nGlowIntensity, BOOL bPreMultiply, - DTT_CALLBACK_PROC pfnDrawTextCallback, + DTT_CALLBACK_PROC actualDrawTextCallback, LPARAM lParam ) { - static const auto pfnDrawTextWithGlow{reinterpret_cast(GetProcAddress(GetModuleHandle(TEXT("UxTheme")), MAKEINTRESOURCEA(126)))}; + static const auto actualDrawTextWithGlow{reinterpret_cast(GetProcAddress(GetModuleHandle(TEXT("UxTheme")), MAKEINTRESOURCEA(126)))}; - if (pfnDrawTextWithGlow) + if (actualDrawTextWithGlow) { - return pfnDrawTextWithGlow(hdc, pszText, cchText, prc, dwFlags, crText, crGlow, nGlowRadius, nGlowIntensity, bPreMultiply, pfnDrawTextCallback, lParam); + return actualDrawTextWithGlow(hdc, pszText, cchText, prc, dwFlags, crText, crGlow, nGlowRadius, nGlowIntensity, bPreMultiply, actualDrawTextCallback, lParam); } else { - LOG_HR_MSG(E_POINTER, "pfnDrawTextWithGlow is invalid!"); + LOG_HR_MSG(E_POINTER, "actualDrawTextWithGlow is invalid!"); } return E_FAIL; diff --git a/TFMain/Utils.hpp b/TFMain/Utils.hpp index b6c5735..7db1154 100644 --- a/TFMain/Utils.hpp +++ b/TFMain/Utils.hpp @@ -8,9 +8,10 @@ namespace TranslucentFlyouts struct external_dc { HDC dc{nullptr}; - external_dc(HDC hdc = nullptr) : dc{hdc} { if (hdc) { SaveDC(hdc); } } + int saved{-1}; + external_dc(HDC hdc = nullptr) : dc{hdc} { if (hdc) { saved = SaveDC(hdc); } } WI_NODISCARD inline operator HDC() const WI_NOEXCEPT { return dc; } - static inline void close(external_dc pdc) WI_NOEXCEPT { if (pdc.dc) { ::RestoreDC(pdc.dc, -1); } } + static inline void close(external_dc pdc) WI_NOEXCEPT { if (pdc.dc) { ::RestoreDC(pdc.dc, pdc.saved); } } }; typedef wil::unique_any unique_ext_hdc; @@ -26,11 +27,26 @@ namespace TranslucentFlyouts return rf.fake_ptr; } + static inline std::wstring make_current_folder_file_str(std::wstring_view baseFileName) + { + WCHAR filePath[MAX_PATH + 1]{L""}; + [&]() + { + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, HINST_THISCOMPONENT); + RETURN_LAST_ERROR_IF(GetModuleFileNameW(HINST_THISCOMPONENT, filePath, _countof(filePath)) == 0); + RETURN_IF_FAILED(PathCchRemoveFileSpec(filePath, _countof(filePath))); + RETURN_IF_FAILED(PathCchAppend(filePath, _countof(filePath), baseFileName.data())); + return S_OK; + } (); + + return std::wstring{filePath}; + } + static inline std::optional RoInit() { - HRESULT hr{RoInitialize(RO_INIT_MULTITHREADED)}; + HRESULT hr{::RoInitialize(RO_INIT_MULTITHREADED)}; - if (SUCCEEDED(hr) || hr == S_FALSE || hr == RPC_E_CHANGED_MODE) + if (SUCCEEDED(hr) || hr == S_FALSE) { return wil::unique_rouninitialize_call{}; } @@ -42,7 +58,7 @@ namespace TranslucentFlyouts { bool result{false}; MEMORY_BASIC_INFORMATION mbi{}; - if (::VirtualQuery(ptr, &mbi, sizeof(mbi))) + if (VirtualQuery(ptr, &mbi, sizeof(mbi))) { DWORD mask{PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY}; result = !(mbi.Protect & mask); @@ -61,28 +77,25 @@ namespace TranslucentFlyouts return result; } - static inline BOOL IsTopLevelWindow(HWND hWnd) + static inline bool IsWindowClass(HWND hWnd, std::wstring_view className = L"", std::wstring_view windowText = L"") { - static const auto pfnIsTopLevelWindow = (BOOL(WINAPI*)(HWND))GetProcAddress(GetModuleHandle(TEXT("User32")), "IsTopLevelWindow"); - - if (pfnIsTopLevelWindow) + bool classNameOK{true}; + if (!className.empty()) { - return pfnIsTopLevelWindow(hWnd); + WCHAR pszClass[MAX_PATH + 1]{}; + GetClassNameW(hWnd, pszClass, MAX_PATH); + classNameOK = (!_wcsicmp(pszClass, className.data())); } - else + + bool windowTextOK{true}; + if (!windowText.empty()) { - LOG_HR_MSG(E_POINTER, "pfnIsTopLevelWindow is invalid!"); + WCHAR pszText[MAX_PATH + 1]{}; + InternalGetWindowText(hWnd, pszText, MAX_PATH); + windowTextOK = (!_wcsicmp(pszText, windowText.data())); } - return FALSE; - } - - static inline bool IsWindowClass(HWND hWnd, std::wstring_view className) - { - WCHAR pszClass[MAX_PATH + 1] {}; - GetClassNameW(hWnd, pszClass, MAX_PATH); - - return !_wcsicmp(pszClass, className.data()); + return classNameOK && windowTextOK; } static bool IsWin32PopupMenu(HWND hWnd) @@ -98,75 +111,13 @@ namespace TranslucentFlyouts return false; } - static inline PVOID GetModuleBase(HMODULE moduleHandle) try - { - MODULEINFO modInfo{}; - - THROW_HR_IF_NULL(E_INVALIDARG, moduleHandle); - THROW_IF_WIN32_BOOL_FALSE(GetModuleInformation(GetCurrentProcess(), moduleHandle, &modInfo, sizeof(modInfo))); - - return modInfo.lpBaseOfDll; - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - return nullptr; - } - - static inline HRESULT GetModuleFolder(HMODULE moduleHandle, LPWSTR filePath, DWORD size) try - { - THROW_HR_IF_NULL(E_INVALIDARG, moduleHandle); - THROW_LAST_ERROR_IF(GetModuleFileNameW(moduleHandle, filePath, size) == 0); - THROW_IF_FAILED(PathCchRemoveFileSpec(filePath, size)); - - return S_OK; - } - CATCH_LOG_RETURN_HR(wil::ResultFromCaughtException()) - - static inline HWND GetCurrentMenuOwner() try + static inline HWND GetCurrentMenuOwner() { GUITHREADINFO guiThreadInfo{sizeof(GUITHREADINFO)}; - - THROW_IF_WIN32_BOOL_FALSE(GetGUIThreadInfo(GetCurrentThreadId(), &guiThreadInfo)); + LOG_IF_WIN32_BOOL_FALSE(GetGUIThreadInfo(GetCurrentThreadId(), &guiThreadInfo)); return guiThreadInfo.hwndMenuOwner; } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - return nullptr; - } - - [[maybe_unused]] static void EnumProcessThreads(std::function callback) try - { - wil::unique_handle snapShot{nullptr}; - THROW_HR_IF_NULL(E_INVALIDARG, callback); - - snapShot.reset(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); - THROW_LAST_ERROR_IF(snapShot.get() == INVALID_HANDLE_VALUE); - - THREADENTRY32 entry{sizeof(THREADENTRY32)}; - THROW_LAST_ERROR_IF(Thread32First(snapShot.get(), &entry) == FALSE); - - if (entry.th32OwnerProcessID == GetCurrentProcessId()) - { - callback(entry.th32ThreadID); - } - while (Thread32Next(snapShot.get(), &entry)) - { - if (entry.th32OwnerProcessID == GetCurrentProcessId()) - { - callback(entry.th32ThreadID); - } - } - - DWORD lastError{GetLastError()}; - if (lastError != ERROR_NO_MORE_FILES) - { - THROW_IF_WIN32_ERROR(lastError); - } - } - CATCH_LOG_RETURN() static inline std::byte PremultiplyColor(std::byte color, std::byte alpha = std::byte{255}) { @@ -184,18 +135,17 @@ namespace TranslucentFlyouts return CreateDIBSection(nullptr, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast(bits), nullptr, 0); } - static inline HRESULT GetBrushColor(HBRUSH brush, COLORREF& color) try + static inline HRESULT GetBrushColor(HBRUSH brush, COLORREF& color) { LOGBRUSH logBrush{}; - THROW_HR_IF_NULL(E_INVALIDARG, brush); - THROW_LAST_ERROR_IF(!GetObject(brush, sizeof(LOGBRUSH), &logBrush)); - THROW_HR_IF(E_INVALIDARG, logBrush.lbStyle != BS_SOLID); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, brush); + RETURN_LAST_ERROR_IF_EXPECTED(!GetObject(brush, sizeof(LOGBRUSH), &logBrush)); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, logBrush.lbStyle != BS_SOLID); color = logBrush.lbColor; return S_OK; } - CATCH_LOG_RETURN_HR(wil::ResultFromCaughtException()) static HBRUSH CreateSolidColorBrushWithAlpha(COLORREF color, std::byte alpha) { @@ -205,6 +155,7 @@ namespace TranslucentFlyouts bitmap.reset(CreateDIB(1, -1, &bits)); if (!bitmap) { + LOG_LAST_ERROR(); return nullptr; } @@ -216,40 +167,55 @@ namespace TranslucentFlyouts return CreatePatternBrush(bitmap.get()); } - static HBRUSH CreateSolidColorBrushWithAlpha(HBRUSH brush, std::byte alpha) try + static HBRUSH CreateSolidColorBrushWithAlpha(HBRUSH brush, std::byte alpha) { COLORREF color{0}; - THROW_HR_IF_NULL(E_INVALIDARG, brush); - THROW_IF_FAILED(GetBrushColor(brush, color)); + if (!brush) + { + return nullptr; + } + if (FAILED(GetBrushColor(brush, color))) + { + return nullptr; + } return CreateSolidColorBrushWithAlpha(color, alpha); } - catch (...) + + static COLORREF MakeCOLORREF(DWORD argb) + { + auto r{argb >> 16 & 0xff}; + auto g{argb >> 8 & 0xff}; + auto b{argb & 0xff}; + + return RGB(r, g, b); + } + + static std::byte GetAlpha(DWORD argb) { - LOG_CAUGHT_EXCEPTION(); - return nullptr; + return std::byte{argb >> 24}; } // Adjust alpha channel of the bitmap, return E_NOTIMPL if the bitmap is unsupported - static HRESULT PrepareAlpha(HBITMAP bitmap) try + static HRESULT PrepareAlpha(HBITMAP bitmap) { - THROW_HR_IF_NULL(E_INVALIDARG, bitmap); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, bitmap); BITMAPINFO bitmapInfo{sizeof(bitmapInfo.bmiHeader)}; auto hdc{wil::GetDC(nullptr)}; - THROW_LAST_ERROR_IF_NULL(hdc); + RETURN_LAST_ERROR_IF_NULL(hdc); - THROW_HR_IF(E_INVALIDARG, GetObjectType(bitmap) != OBJ_BITMAP); - THROW_LAST_ERROR_IF(GetDIBits(hdc.get(), bitmap, 0, 0, nullptr, &bitmapInfo, DIB_RGB_COLORS) == 0); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, GetObjectType(bitmap) != OBJ_BITMAP); + RETURN_LAST_ERROR_IF(GetDIBits(hdc.get(), bitmap, 0, 0, nullptr, &bitmapInfo, DIB_RGB_COLORS) == 0); bitmapInfo.bmiHeader.biCompression = BI_RGB; auto pixelBits{std::make_unique(bitmapInfo.bmiHeader.biSizeImage)}; - THROW_LAST_ERROR_IF(GetDIBits(hdc.get(), bitmap, 0, bitmapInfo.bmiHeader.biHeight, pixelBits.get(), &bitmapInfo, DIB_RGB_COLORS) == 0); + RETURN_LAST_ERROR_IF(GetDIBits(hdc.get(), bitmap, 0, bitmapInfo.bmiHeader.biHeight, pixelBits.get(), &bitmapInfo, DIB_RGB_COLORS) == 0); // Nothing we can do to a bitmap whose bit count isn't 32 :( - THROW_HR_IF_MSG(E_NOTIMPL, bitmapInfo.bmiHeader.biBitCount != 32, "Unsuported bit count: %d!", bitmapInfo.bmiHeader.biBitCount); + RETURN_HR_IF_EXPECTED(E_NOTIMPL, bitmapInfo.bmiHeader.biBitCount != 32); bool bHasAlpha{false}; for (size_t i = 0; i < bitmapInfo.bmiHeader.biSizeImage; i += 4) @@ -271,28 +237,16 @@ namespace TranslucentFlyouts pixelBits[i + 3] = std::byte{255}; //Alpha } - THROW_LAST_ERROR_IF(SetDIBits(hdc.get(), bitmap, 0, bitmapInfo.bmiHeader.biHeight, pixelBits.get(), &bitmapInfo, DIB_RGB_COLORS) == 0); + RETURN_LAST_ERROR_IF(SetDIBits(hdc.get(), bitmap, 0, bitmapInfo.bmiHeader.biHeight, pixelBits.get(), &bitmapInfo, DIB_RGB_COLORS) == 0); } return S_OK; } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - return wil::ResultFromCaughtException(); - } - static inline HRESULT GetDwmThemeColor(COLORREF& color, DWORD& opacity) + static inline HRESULT GetDwmThemeColor(DWORD& argb) { - DWORD dwmColor{}; BOOL opaque{}; - HRESULT hr{DwmGetColorizationColor(&dwmColor, &opaque)}; - - if (SUCCEEDED(hr)) - { - color = RGB(dwmColor >> 16, dwmColor >> 8, dwmColor); - opacity = opaque ? 255 : dwmColor >> 24; - } + HRESULT hr{DwmGetColorizationColor(&argb, &opaque)}; return hr; } diff --git a/TFMain/UxThemePatcher.cpp b/TFMain/UxThemePatcher.cpp index 2f0134f..0c115ad 100644 --- a/TFMain/UxThemePatcher.cpp +++ b/TFMain/UxThemePatcher.cpp @@ -10,28 +10,27 @@ #include "DXHelper.hpp" #include "UxThemePatcher.hpp" -namespace TranslucentFlyouts -{ - using namespace std; +using namespace std; +using namespace wil; +using namespace TranslucentFlyouts; #pragma data_seg("uxthemeOffset") - int UxThemePatcher::g_uxthemeVersion {-1}; - DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawItem_Offset{0}; - DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawItemCheck_Offset{0}; - DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawClientArea_Offset{0}; - DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawNonClientArea_Offset{0}; - DWORD64 UxThemePatcher::g_CThemeMenu_DrawItemBitmap_Offset{0}; +int UxThemePatcher::g_uxthemeVersion {-1}; +DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawItem_Offset{0}; +DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawItemCheck_Offset{0}; +DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawClientArea_Offset{0}; +DWORD64 UxThemePatcher::g_CThemeMenuPopup_DrawNonClientArea_Offset{0}; +DWORD64 UxThemePatcher::g_CThemeMenu_DrawItemBitmap_Offset{0}; #pragma data_seg() #pragma comment(linker,"/SECTION:uxthemeOffset,RWS") -} -TranslucentFlyouts::UxThemePatcher& TranslucentFlyouts::UxThemePatcher::GetInstance() +UxThemePatcher& UxThemePatcher::GetInstance() { static UxThemePatcher instance{}; return instance; } -TranslucentFlyouts::UxThemePatcher::UxThemePatcher() +UxThemePatcher::UxThemePatcher() { try { @@ -63,12 +62,12 @@ TranslucentFlyouts::UxThemePatcher::UxThemePatcher() } } -TranslucentFlyouts::UxThemePatcher::~UxThemePatcher() noexcept +UxThemePatcher::~UxThemePatcher() noexcept { ShutdownHook(); } -HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( +HRESULT WINAPI UxThemePatcher::DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, @@ -84,7 +83,7 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hTheme); RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdc); RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, pRect); - RETURN_HR_IF(E_INVALIDARG, Utils::IsBadReadPtr(pRect)); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, Utils::IsBadReadPtr(pRect)); RETURN_HR_IF_EXPECTED(E_INVALIDARG, IsRectEmpty(pRect) == TRUE); RETURN_HR_IF_EXPECTED( @@ -97,6 +96,13 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( MenuHandler::NotifyUxThemeRendering(); MenuHandler::NotifyMenuDarkMode(darkMode); + MenuHandler::NotifyMenuStyle(false); + + COLORREF color{DWMWA_COLOR_NONE}; + if (SUCCEEDED(GetThemeColor(hTheme, MENU_POPUPBORDERS, 0, TMT_FILLCOLORHINT, &color))) + { + MenuHandler::NotifyMenuBorderColor(color); + } RECT clipRect{*pRect}; if (pClipRect != nullptr) @@ -151,21 +157,26 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( if (iPartId == MENU_POPUPBORDERS) { - DWORD noOutline + RETURN_IF_WIN32_BOOL_FALSE( + PatBlt(hdc, clipRect.left, clipRect.top, clipRect.right - clipRect.left, clipRect.bottom - clipRect.top, BLACKNESS) + ); + if (!MenuHandler::GetInstance().HandlePopupMenuNCBorderColors(hdc, darkMode, clipRect)) { - RegHelper::GetDword( - L"Menu", - L"NoSystemOutline", - 0, - false - ) - }; - if (noOutline) + Utils::unique_ext_hdc dc{hdc}; + + ExcludeClipRect(dc.get(), clipRect.left + MenuHandler::systemOutlineSize, clipRect.top + MenuHandler::systemOutlineSize, clipRect.right - MenuHandler::systemOutlineSize, clipRect.bottom - MenuHandler::systemOutlineSize); + return GetInstance().m_actualDrawThemeBackground( + hTheme, + hdc, + iPartId, + iStateId, + pRect, + pClipRect + ); + } + else { - if (SUCCEEDED(menuRendering.DoCustomThemeRendering(hdc, darkMode, iPartId, iStateId, clipRect, *pRect))) - { - return S_OK; - } + return S_OK; } } // Separator @@ -221,7 +232,7 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( L"DarkMode_ImmersiveStart::Menu" : L"LightMode_ImmersiveStart::Menu" }; - wil::unique_htheme themeHandle{OpenThemeData(nullptr, themeClass.data())}; + unique_htheme themeHandle{OpenThemeData(nullptr, themeClass.data())}; RETURN_HR_IF_NULL(E_FAIL, themeHandle); return ::DrawThemeBackground( @@ -269,7 +280,7 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeBackground( return hr; } -HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeText( +HRESULT WINAPI UxThemePatcher::DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, @@ -287,7 +298,7 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeText( RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hTheme); RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdc); RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, pRect); - RETURN_HR_IF(E_INVALIDARG, Utils::IsBadReadPtr(pRect)); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, Utils::IsBadReadPtr(pRect)); RETURN_HR_IF_EXPECTED(E_INVALIDARG, IsRectEmpty(pRect) == TRUE); RETURN_HR_IF_EXPECTED( @@ -336,7 +347,7 @@ HRESULT WINAPI TranslucentFlyouts::UxThemePatcher::DrawThemeText( return hr; } -void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap(HWND hWnd, HDC hdc, HBITMAP hBitmap, bool fromPopupMenu, int iStateId, const RECT* lprc) +void __thiscall UxThemePatcher::CThemeMenu::DrawItemBitmap(HWND hWnd, HDC hdc, HBITMAP hBitmap, bool fromPopupMenu, int iStateId, const RECT* lprc) { HRESULT hr {S_OK}; @@ -351,10 +362,10 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap(H hr = [&]() { - RETURN_HR_IF_NULL(E_INVALIDARG, lprc); - RETURN_HR_IF(E_INVALIDARG, Utils::IsBadReadPtr(lprc)); - RETURN_HR_IF_NULL(E_INVALIDARG, hdc); - RETURN_HR_IF_NULL(E_INVALIDARG, hBitmap); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, lprc); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, Utils::IsBadReadPtr(lprc)); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdc); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hBitmap); RETURN_HR_IF_EXPECTED( E_NOTIMPL, !fromPopupMenu @@ -387,7 +398,7 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap(H #ifdef _WIN64 ptr(this, hWnd, hdc, bitmap ? bitmap.value().get() : hBitmap, fromPopupMenu, lprc, lprc); #else - ptr(this, nullptr, hWnd, hdc, bitmap ? bitmap.value().get() : hBitmap, fromPopupMenu, lprc, lprc); + ptr(this, nullptr, hWnd, hdc, bitmap ? bitmap.value().get() : hBitmap, fromPopupMenu, lprc, lprc); #endif return S_OK; }(); @@ -403,7 +414,7 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap(H return; } -void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap2(HWND hWnd, HDC hdc, HBITMAP hBitmap, bool fromPopupMenu, bool noStretch, int iStateId, const RECT* lprc) +void __thiscall UxThemePatcher::CThemeMenu::DrawItemBitmap2(HWND hWnd, HDC hdc, HBITMAP hBitmap, bool fromPopupMenu, bool noStretch, int iStateId, const RECT* lprc) { HRESULT hr{S_OK}; auto ptr @@ -417,10 +428,10 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap2( hr = [&]() { - RETURN_HR_IF_NULL(E_INVALIDARG, lprc); - RETURN_HR_IF(E_INVALIDARG, Utils::IsBadReadPtr(lprc)); - RETURN_HR_IF_NULL(E_INVALIDARG, hdc); - RETURN_HR_IF_NULL(E_INVALIDARG, hBitmap); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, lprc); + RETURN_HR_IF_EXPECTED(E_INVALIDARG, Utils::IsBadReadPtr(lprc)); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hdc); + RETURN_HR_IF_NULL_EXPECTED(E_INVALIDARG, hBitmap); RETURN_HR_IF_EXPECTED( E_NOTIMPL, !fromPopupMenu @@ -432,7 +443,7 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap2( auto bitmap{MenuRendering::GetInstance().PromiseAlpha(hBitmap)}; RETURN_LAST_ERROR_IF(bitmap && !bitmap.value().get()); - + #ifdef _WIN64 ptr(this, hWnd, hdc, bitmap ? bitmap.value().get() : hBitmap, fromPopupMenu, noStretch, lprc, lprc); #else @@ -453,7 +464,7 @@ void __thiscall TranslucentFlyouts::UxThemePatcher::CThemeMenu::DrawItemBitmap2( return; } -bool TranslucentFlyouts::UxThemePatcher::IsUxThemeAPIOffsetReady() +bool UxThemePatcher::IsUxThemeAPIOffsetReady() { if ( g_CThemeMenuPopup_DrawItem_Offset != 0 && @@ -469,7 +480,7 @@ bool TranslucentFlyouts::UxThemePatcher::IsUxThemeAPIOffsetReady() return false; } -void TranslucentFlyouts::UxThemePatcher::InitUxThemeOffset() +void UxThemePatcher::InitUxThemeOffset() { // TO-DO // Cache the offset information into the registry so that we don't need to calculate them every time @@ -605,53 +616,82 @@ void TranslucentFlyouts::UxThemePatcher::InitUxThemeOffset() } } -void TranslucentFlyouts::UxThemePatcher::PrepareUxTheme() try +void UxThemePatcher::PrepareUxTheme() try { InitUxThemeOffset(); } catch (...) { Utils::StartupConsole(); - wprintf_s(L"exception caught: 0x%x!\n", wil::ResultFromCaughtException()); + wprintf_s(L"exception caught: 0x%x!\n", ResultFromCaughtException()); Utils::OutputModuleString(IDS_STRING103); LOG_CAUGHT_EXCEPTION(); return; } -void TranslucentFlyouts::UxThemePatcher::StartupHook() +void UxThemePatcher::StartupHook() { if (m_internalError) { return; } - m_callHook.Attach( - m_actualCThemeMenuPopup_DrawItem, - m_actualDrawThemeText, - UxThemePatcher::DrawThemeText, - 2 + LOG_HR_IF( + E_FAIL, + m_callHook.Attach( + m_actualCThemeMenuPopup_DrawItem, + m_actualDrawThemeText, + UxThemePatcher::DrawThemeText, + 2 + ) != 0 ); - m_callHook.Attach( - m_actualCThemeMenuPopup_DrawItem, - m_actualDrawThemeBackground, - UxThemePatcher::DrawThemeBackground, - 4 + LOG_HR_IF( + E_FAIL, + m_callHook.Attach( + m_actualCThemeMenuPopup_DrawItem, + m_actualDrawThemeBackground, + UxThemePatcher::DrawThemeBackground, + 4 + ) != 0 ); - m_callHook.Attach( - m_actualCThemeMenuPopup_DrawClientArea, - m_actualDrawThemeBackground, - UxThemePatcher::DrawThemeBackground, - 1 + + LOG_HR_IF( + E_FAIL, + m_callHook.Attach( + m_actualCThemeMenuPopup_DrawItemCheck, + m_actualDrawThemeBackground, + UxThemePatcher::DrawThemeBackground, + 2 + ) != 0 + ); + + LOG_HR_IF( + E_FAIL, + m_callHook.Attach( + m_actualCThemeMenuPopup_DrawClientArea, + m_actualDrawThemeBackground, + UxThemePatcher::DrawThemeBackground, + 1 + ) != 0 ); - m_callHook.Attach( - m_actualCThemeMenuPopup_DrawNonClientArea, - m_actualDrawThemeBackground, - UxThemePatcher::DrawThemeBackground, - 1 + + LOG_HR_IF( + E_FAIL, + m_callHook.Attach( + m_actualCThemeMenuPopup_DrawNonClientArea, + m_actualDrawThemeBackground, + UxThemePatcher::DrawThemeBackground, + 1 + ) != 0 ); + if (m_hooked) + { + return; + } + PVOID detourDestination{nullptr}; if (g_uxthemeVersion == 0) { @@ -662,21 +702,29 @@ void TranslucentFlyouts::UxThemePatcher::StartupHook() detourDestination = Utils::member_function_pointer_cast(&UxThemePatcher::CThemeMenu::DrawItemBitmap2); } - Hooking::Detours::Write([&]() + if (detourDestination) { - Hooking::Detours::Attach(&m_actualCThemeMenu_DrawItemBitmap, detourDestination); - }); + Hooking::Detours::Write([&]() + { + Hooking::Detours::Attach(&m_actualCThemeMenu_DrawItemBitmap, detourDestination); + m_hooked = true; + }); + } } -void TranslucentFlyouts::UxThemePatcher::ShutdownHook() +void UxThemePatcher::ShutdownHook() { if (m_internalError) { return; } - m_callHook.Detach(); + if (!m_hooked) + { + return; + } + PVOID detourDestination{nullptr}; if (g_uxthemeVersion == 0) { @@ -687,8 +735,11 @@ void TranslucentFlyouts::UxThemePatcher::ShutdownHook() detourDestination = Utils::member_function_pointer_cast(&UxThemePatcher::CThemeMenu::DrawItemBitmap2); } - Hooking::Detours::Write([&]() + if (detourDestination) { - Hooking::Detours::Detach(&m_actualCThemeMenu_DrawItemBitmap, detourDestination); - }); + Hooking::Detours::Write([&]() + { + Hooking::Detours::Detach(&m_actualCThemeMenu_DrawItemBitmap, detourDestination); + }); + } } \ No newline at end of file diff --git a/TFMain/UxThemePatcher.hpp b/TFMain/UxThemePatcher.hpp index 06cd1f6..ca648ac 100644 --- a/TFMain/UxThemePatcher.hpp +++ b/TFMain/UxThemePatcher.hpp @@ -55,6 +55,7 @@ namespace TranslucentFlyouts #pragma data_seg() #pragma comment(linker,"/SECTION:uxthemeOffset,RWS") + bool m_hooked{false}; bool m_internalError{false}; [[maybe_unused]] PVOID m_actualCThemeMenuPopup_DrawItem{nullptr}; diff --git a/TFMain/dllmain.cpp b/TFMain/dllmain.cpp index 1a0e2e9..4a01e61 100644 --- a/TFMain/dllmain.cpp +++ b/TFMain/dllmain.cpp @@ -1,195 +1,5 @@ #include "pch.h" -#include "resource.h" -#include "Utils.hpp" -#include "Hooking.hpp" -#include "ThemeHelper.hpp" -#include "EffectHelper.hpp" -#include "MenuHandler.hpp" -#include "UxThemePatcher.hpp" -#include "ImmersiveContextMenuPatcher.hpp" - -namespace TranslucentFlyouts -{ - using namespace std; - // TranslucentFlyouts won't be loaded into one of these process - // These processes are quite annoying because TranslucentFlyouts will not be automatically unloaded by them - // Some of them even have no chance to show flyouts and other UI elements - const array g_blockList - { - L"sihost.exe"sv, - L"WSHost.exe"sv, - L"spoolsv.exe"sv, - L"dllhost.exe"sv, - L"svchost.exe"sv, - L"taskhostw.exe"sv, - L"searchhost.exe"sv, - L"RuntimeBroker.exe"sv, - L"smartscreen.exe"sv, - L"Widgets.exe"sv, - L"WidgetService.exe"sv, - L"GameBar.exe"sv, - L"GameBarFTServer.exe"sv, - L"ShellExperienceHost.exe"sv, - L"StartMenuExperienceHost.exe"sv, - L"msedgewebview2.exe"sv - }; - - // A class that in charge of global injection, initialization and uninitialization - // of different class which tweak the flyout appearance - class MainDLL - { - public: - static MainDLL& GetInstance() - { - static MainDLL instance{}; - return instance; - } - ~MainDLL() noexcept = default; - MainDLL(const MainDLL&) = delete; - MainDLL& operator=(const MainDLL&) = delete; - - static inline bool IsHookGlobalInstalled() - { - return g_hHook != nullptr; - } - static HRESULT InstallHook() - { - RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), IsHookGlobalInstalled()); - - UxThemePatcher::PrepareUxTheme(); - { - if (GetConsoleWindow()) - { - Utils::OutputModuleString(IDS_STRING104); - system("pause>nul"); - - Utils::ShutdownConsole(); - } - } - g_hHook = SetWinEventHook( - EVENT_MIN, EVENT_MAX, - HINST_THISCOMPONENT, - HandleWinEvent, - 0, 0, - WINEVENT_INCONTEXT - ); - RETURN_LAST_ERROR_IF_NULL(g_hHook); - - return S_OK; - } - static HRESULT UninstallHook() - { - RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_HOOK_NOT_INSTALLED), !IsHookGlobalInstalled()); - RETURN_IF_WIN32_BOOL_FALSE(UnhookWinEvent(g_hHook)); - g_hHook = nullptr; - - BroadcastSystemMessageW( - BSF_FORCEIFHUNG | BSF_FLUSHDISK | BSF_POSTMESSAGE, - nullptr, - WM_POWERBROADCAST, - PBT_APMRESUMESUSPEND, - 0 - ); - - return S_OK; - } - - static bool IsCurrentProcessInBlockList() - { - for (auto processName : g_blockList) - { - if (GetModuleHandleW(processName.data())) - { - return true; - } - } - - return false; - } - - void Startup() - { - if (m_startup) - { - return; - } - - m_menuHandler.StartupHook(); - m_uxthemePatcher.StartupHook(); - m_immersiveContextMenuPatcher.StartupHook(); - - m_startup = true; - } - void Shutdown() - { - if (!m_startup) - { - return; - } - - m_immersiveContextMenuPatcher.ShutdownHook(); - m_uxthemePatcher.ShutdownHook(); - m_menuHandler.ShutdownHook(); - - m_startup = false; - } - private: - MainDLL() = default; - static void CALLBACK HandleWinEvent( - HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hWnd, - LONG idObject, LONG idChild, - DWORD dwEventThread, DWORD dwmsEventTime - ) - { - auto& mainDLL{GetInstance()}; - - if ( - !IsHookGlobalInstalled() || - !mainDLL.m_startup || - idObject != OBJID_WINDOW || - idChild != CHILDID_SELF || - !hWnd || !IsWindow(hWnd) - ) - { - return; - } - - if (dwEvent == EVENT_OBJECT_CREATE) - { - if (Utils::IsWin32PopupMenu(hWnd)) - { - GetInstance().m_menuHandler.AttachPopupMenu(hWnd); - } - - HWND parentWindow{GetParent(hWnd)}; - if (Utils::IsWindowClass(hWnd, L"Listviewpopup") && Utils::IsWindowClass(parentWindow, L"DropDown")) - { - GetInstance().m_menuHandler.AttachListViewPopup(hWnd); - } - } - - if (dwEvent == EVENT_OBJECT_SHOW) - { - } - - if (dwEvent == EVENT_OBJECT_HIDE) - { - } - } - - static HWINEVENTHOOK g_hHook; - - bool m_startup{false}; - MenuHandler& m_menuHandler{MenuHandler::GetInstance()}; - UxThemePatcher& m_uxthemePatcher{UxThemePatcher::GetInstance()}; - ImmersiveContextMenuPatcher& m_immersiveContextMenuPatcher{ImmersiveContextMenuPatcher::GetInstance()}; - }; - -#pragma data_seg("hook") - HWINEVENTHOOK MainDLL::g_hHook {nullptr}; -#pragma data_seg() -#pragma comment(linker,"/SECTION:hook,RWS") -} +#include "TFMain.hpp" BOOL APIENTRY DllMain( HMODULE hModule, @@ -214,7 +24,8 @@ BOOL APIENTRY DllMain( { if ( MainDLL::IsHookGlobalInstalled() && - ThemeHelper::IsThemeAvailable() && + //ThemeHelper::IsThemeAvailable() && + //!ThemeHelper::IsHighContrast() && !GetSystemMetrics(SM_CLEANBOOT) ) { @@ -270,7 +81,6 @@ int WINAPI Main( modulePath ) #endif - }; SHELLEXECUTEINFOW sei { @@ -339,6 +149,7 @@ int WINAPI Main( Utils::ShutdownConsole(); } #else + SetProcessShutdownParameters(0, 0); HWND window { CreateWindowExW( diff --git a/TFMain/dx.h b/TFMain/dx.h index d482997..31acf3f 100644 --- a/TFMain/dx.h +++ b/TFMain/dx.h @@ -4,6 +4,7 @@ #include #include #include +#include #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "d3d11.lib") diff --git a/TFMain/framework.h b/TFMain/framework.h index 34b6de9..fa7cc10 100644 --- a/TFMain/framework.h +++ b/TFMain/framework.h @@ -5,12 +5,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -19,8 +19,9 @@ #include #include #include +#pragma comment(lib, "Oleacc.lib") +#pragma comment(lib, "windowsapp.lib") #pragma comment(lib, "Shlwapi.lib") -#pragma comment(lib, "gdiplus.lib") #pragma comment(lib, "dwmapi.lib") #pragma comment(lib, "delayimp.lib") #pragma comment(lib, "DbgHelp.lib") diff --git a/TFMain/wil.h b/TFMain/wil.h index 7d414d7..b85b508 100644 --- a/TFMain/wil.h +++ b/TFMain/wil.h @@ -6,5 +6,7 @@ #include "wil/result.h" #include "wil/registry.h" #include "wil/resource.h" +#include "wil/stl.h" +#include "wil/safecast.h" #include "wil/com.h" -#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) \ No newline at end of file +#define HINST_THISCOMPONENT ((HINSTANCE)&::__ImageBase) \ No newline at end of file