Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win10 dark theme - how to use in WINAPI?

Starting with October 2018 Update (version 1809) Win10 has support for Dark theme in Windows Explorer.

It can be configured here:

  • UI: Desktop | Context Menu | Personalize | Colors | Choose your default app mode = Dark
  • Registry : HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme = DWORD:0

While this setting exists for a while now, it only affected UWP applications. However, with this Windows 10 release, it also affects Windows Explorer, which is a Desktop application. This means that Windows now have internal support for it. Still, Desktop applications other then Windows Explorer are not affected at the moment.

I'd like to use it in my application. How is it implemented under the hood? Is there some way (manifest, WINAPI, etc) to subscribe for new dark theme?

Update 1:
I noticed that Windows Explorer Control Panel is partially light and partially dark, so it should be a per-window setting, rather then per-process setting.

One other example: Open File dialogs become dark in all Desktop applications, while the application itself remains in old light theme.

Update 2:
I tried SetWindowTheme(hwnd, L"Explorer", NULL); for TreeView and ListView. This visibly changes TreeView style (+ expand button becomes V), but the window remains white.

like image 709
Codeguard Avatar asked Nov 27 '18 13:11

Codeguard


People also ask

How do I activate the black theme in Windows 10?

Select Start > Settings . Select Personalization > Colors. In the list for Choose your mode, select Custom. In the list for Choose your default Windows mode, select Dark.

How do I use the Dark theme in Windows 7?

Both Windows 7 and Windows 8 have several built-in High Contrast themes you can use to get a dark desktop and applications. Right-click your desktop, select Personalize, and select one of the High Contrast themes. Feel free to try each and see which you prefer.

How do I change dark mode in Visual Studio?

On the menu bar, select Tools > Options. In the options list, select Environment > General. In the Color theme list, choose between the default Dark theme, the Blue theme, the Blue (Extra Contrast) theme, and the Light theme. Or, choose the Use system setting option to select the theme that Windows uses.


1 Answers

See https://github.com/ysc3839/win32-darkmode

This guy has it all laid out in some nice reusable code (MIT license).

It seems that Dark Mode is still an area of development in Windows 10, but I believe that Microsoft will eventually properly document and expose it to desktop apps.

Until then, we are stuck with undocumented ordinal-only imports, then custom draw and WM_CTLCOLOR* messages to dictate how controls that don't yet have native Dark Mode support are painted.

The most fundamental of the new Windows APIs are SetPreferredAppMode (uxtheme@135), to be called prior to any window creation, and AllowDarkModeForWindow (uxtheme@133), to be called on any Window that intends to use native Windows 10 dark mode support.

Here is the full list ordinal-only imports from that project:

using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build); // 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, removed since 18334 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 // Insider 18290 using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138 // Insider 18334 using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, since 18334 using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139 

InitDarkMode imports and initializes dark mode, in a safe manner, carefully checking for min and max supported Windows 10 builds:

void InitDarkMode() {        fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));     if (RtlGetNtVersionNumbers)     {         DWORD major, minor;         RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);         g_buildNumber &= ~0xF0000000;         if (major == 10 && minor == 0 && 17763 <= g_buildNumber && g_buildNumber <= 18363) // Windows 10 1809 10.0.17763 - 1909 10.0.18363         {             HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);             if (hUxtheme)             {                 _OpenNcThemeData = reinterpret_cast<fnOpenNcThemeData>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49)));                 _RefreshImmersiveColorPolicyState = reinterpret_cast<fnRefreshImmersiveColorPolicyState>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104)));                 _GetIsImmersiveColorUsingHighContrast = reinterpret_cast<fnGetIsImmersiveColorUsingHighContrast>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106)));                 _ShouldAppsUseDarkMode = reinterpret_cast<fnShouldAppsUseDarkMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132)));                 _AllowDarkModeForWindow = reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));                  auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));                 if (g_buildNumber < 18334)                     _AllowDarkModeForApp = reinterpret_cast<fnAllowDarkModeForApp>(ord135);                 else                     _SetPreferredAppMode = reinterpret_cast<fnSetPreferredAppMode>(ord135);                  //_FlushMenuThemes = reinterpret_cast<fnFlushMenuThemes>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));                 _IsDarkModeAllowedForWindow = reinterpret_cast<fnIsDarkModeAllowedForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));                  if (_OpenNcThemeData &&                     _RefreshImmersiveColorPolicyState &&                     _ShouldAppsUseDarkMode &&                     _AllowDarkModeForWindow &&                     (_AllowDarkModeForApp || _SetPreferredAppMode) &&                     //_FlushMenuThemes &&                     _IsDarkModeAllowedForWindow)                 {                     g_darkModeSupported = true;                      AllowDarkModeForApp(true);                     _RefreshImmersiveColorPolicyState();                      g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();                      FixDarkScrollBar();                 }             }         }     } } 

Elsewhere, he takes advantage of WM_CTLCOLOR* messages and custom draw notifications to paint dark where Windows doesn't (yet) do it for us.

Note the FixDarkScrollBar. That is an IAT hook on OpenNcThemeData to over-ride the scrollbar theme selection by the listview class in comctl32. That is the part that bothers me most and I'm looking to axe it. I'm sure he is too.

I've adapted this code to my own application and it works well. I am, however, uncomfortable using these undocumented ordinal-only APIs (even as safely as possible), and fully expect Microsoft to eventually announce and document dark mode for Win32 apps and make this work redundant.

like image 53
dyasta Avatar answered Sep 20 '22 10:09

dyasta