/*---------------------------------------------------------------------------*/ // Author : Richard Yu // Web : https://github.com/ysc3839/win32-darkmode // License : MIT License // https://github.com/ysc3839/win32-darkmode/blob/master/LICENSE /*---------------------------------------------------------------------------*/ #include "stdafx.h" #include "OsInfoFx.h" #include "IatHook.h" enum IMMERSIVE_HC_CACHE_MODE { IHCM_USE_CACHED_VALUE, IHCM_REFRESH }; // 1903 18362 enum class PreferredAppMode { Default, AllowDark, ForceDark, ForceLight, Max }; enum WINDOWCOMPOSITIONATTRIB { WCA_UNDEFINED = 0, WCA_NCRENDERING_ENABLED = 1, WCA_NCRENDERING_POLICY = 2, WCA_TRANSITIONS_FORCEDISABLED = 3, WCA_ALLOW_NCPAINT = 4, WCA_CAPTION_BUTTON_BOUNDS = 5, WCA_NONCLIENT_RTL_LAYOUT = 6, WCA_FORCE_ICONIC_REPRESENTATION = 7, WCA_EXTENDED_FRAME_BOUNDS = 8, WCA_HAS_ICONIC_BITMAP = 9, WCA_THEME_ATTRIBUTES = 10, WCA_NCRENDERING_EXILED = 11, WCA_NCADORNMENTINFO = 12, WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, WCA_VIDEO_OVERLAY_ACTIVE = 14, WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, WCA_DISALLOW_PEEK = 16, WCA_CLOAK = 17, WCA_CLOAKED = 18, WCA_ACCENT_POLICY = 19, WCA_FREEZE_REPRESENTATION = 20, WCA_EVER_UNCLOAKED = 21, WCA_VISUAL_OWNER = 22, WCA_HOLOGRAPHIC = 23, WCA_EXCLUDED_FROM_DDA = 24, WCA_PASSIVEUPDATEMODE = 25, WCA_USEDARKMODECOLORS = 26, WCA_LAST = 27 }; struct WINDOWCOMPOSITIONATTRIBDATA { WINDOWCOMPOSITIONATTRIB Attrib; PVOID pvData; SIZE_T cbData; }; using fnRtlGetNtVersionNumbers = void (WINAPI*)(LPDWORD major, LPDWORD minor, LPDWORD build); using fnSetWindowCompositionAttribute = BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*); // 1809 17763 using fnShouldAppsUseDarkMode = bool (WINAPI*)(); // ordinal 132 using fnAllowDarkModeForWindow = bool (WINAPI*)(HWND hWnd, bool allow); // ordinal 133 using fnAllowDarkModeForApp = bool (WINAPI*)(bool allow); // ordinal 135, in 1809 using fnFlushMenuThemes = void (WINAPI*)(); // ordinal 136 using fnRefreshImmersiveColorPolicyState = void (WINAPI*)(); // ordinal 104 using fnIsDarkModeAllowedForWindow = bool (WINAPI*)(HWND hWnd); // ordinal 137 using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI*)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106 using fnOpenNcThemeData = HTHEME(WINAPI*)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49 // 1903 18362 using fnShouldSystemUseDarkMode = bool (WINAPI*)(); // ordinal 138 using fnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, in 1903 using fnIsDarkModeAllowedForApp = bool (WINAPI*)(); // ordinal 139 fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr; fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr; fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr; fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr; fnFlushMenuThemes _FlushMenuThemes = nullptr; fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr; fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr; fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr; fnOpenNcThemeData _OpenNcThemeData = nullptr; // 1903 18362 fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr; fnSetPreferredAppMode _SetPreferredAppMode = nullptr; bool g_darkModeSupported = false; bool g_darkModeEnabled = false; DWORD g_buildNumber = 0; bool AllowDarkModeForWindow(HWND hWnd, bool allow) { if (g_darkModeSupported) return _AllowDarkModeForWindow(hWnd, allow); return false; } bool IsHighContrast() { HIGHCONTRASTW highContrast = { sizeof(highContrast) }; if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE)) return highContrast.dwFlags & HCF_HIGHCONTRASTON; return false; } void AllowDarkModeForApp(bool allow) { if (_AllowDarkModeForApp) _AllowDarkModeForApp(allow); else if (_SetPreferredAppMode) _SetPreferredAppMode(allow ? PreferredAppMode::AllowDark : PreferredAppMode::Default); } void RefreshTitleBarThemeColor(HWND hWnd) { BOOL dark = FALSE; if (_IsDarkModeAllowedForWindow(hWnd) && _ShouldAppsUseDarkMode() && !IsHighContrast()) { dark = TRUE; } if (g_buildNumber < 18362) SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast(static_cast(dark))); else if (_SetWindowCompositionAttribute) { WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) }; _SetWindowCompositionAttribute(hWnd, &data); } } constexpr bool CheckBuildNumber(DWORD buildNumber) { return (buildNumber >= 17763); } void FixDarkScrollBar() { HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if (hComctl) { auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData if (addr) { DWORD oldProtect; if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect)) { auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME { if (wcscmp(classList, L"ScrollBar") == 0) { hWnd = nullptr; classList = L"Explorer::ScrollBar"; } return _OpenNcThemeData(hWnd, classList); }; addr->u1.Function = reinterpret_cast(static_cast(MyOpenThemeData)); VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect); } } } } BOOL InitDarkMode() { HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); HMODULE user32 = GetModuleHandleW(L"user32.dll"); if (!ntdll || !user32) return FALSE; auto RtlGetNtVersionNumbers = reinterpret_cast(GetProcAddress(ntdll, "RtlGetNtVersionNumbers")); if (RtlGetNtVersionNumbers) { DWORD major, minor; RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber); g_buildNumber &= ~0xF0000000; if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber)) { HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if (hUxtheme) { _OpenNcThemeData = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49))); _RefreshImmersiveColorPolicyState = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104))); _GetIsImmersiveColorUsingHighContrast = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106))); _ShouldAppsUseDarkMode = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132))); _AllowDarkModeForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133))); auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135)); if (g_buildNumber < 18362) _AllowDarkModeForApp = reinterpret_cast(ord135); else _SetPreferredAppMode = reinterpret_cast(ord135); //_FlushMenuThemes = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136))); _IsDarkModeAllowedForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137))); _SetWindowCompositionAttribute = reinterpret_cast(GetProcAddress(user32, "SetWindowCompositionAttribute")); if (_OpenNcThemeData && _RefreshImmersiveColorPolicyState && _ShouldAppsUseDarkMode && _AllowDarkModeForWindow && (_AllowDarkModeForApp || _SetPreferredAppMode) && //_FlushMenuThemes && _IsDarkModeAllowedForWindow) { g_darkModeSupported = true; AllowDarkModeForApp(true); _RefreshImmersiveColorPolicyState(); g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast(); // FixDarkScrollBar(); } } } } return (BOOL)g_darkModeEnabled; } BOOL SetDarkMode(HWND hWnd) { BOOL bDarkMode = FALSE; if (IsDarkModeSupport()) { bDarkMode = InitDarkMode(); AllowDarkModeForWindow(hWnd, bDarkMode); RefreshTitleBarThemeColor(hWnd); } return bDarkMode; } void UnsetDarkMode(HWND hWnd) { if (IsDarkModeSupport()) { InitDarkMode(); AllowDarkModeForWindow(hWnd, FALSE); RefreshTitleBarThemeColor(hWnd); } } void SetDarkModeControl(HWND hWnd, BOOL bDarkMode) { if (IsDarkModeSupport()) { SetWindowTheme(hWnd, L"Explorer", nullptr); AllowDarkModeForWindow(hWnd, bDarkMode); SendMessageW(hWnd, WM_THEMECHANGED, 0, 0); } }