web-dev-qa-db-fra.com

Thème sombre Win10 - comment l'utiliser dans WINAPI?

À partir de October 2018 Update (version 1809) Win10 prend en charge le thème sombre dans l'Explorateur Windows.

Il peut être configuré ici:

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

Bien que ce paramètre existe depuis un certain temps maintenant, il n'a affecté que les applications UWP. Cependant, avec cette version de Windows 10, cela affecte également l'Explorateur Windows, qui est une application de bureau. Cela signifie que Windows dispose désormais d'un support interne. Pourtant, les applications de bureau autres que Windows Explorer ne sont pas affectées pour le moment.

Je voudrais l'utiliser dans mon application. Comment est-il mis en œuvre sous le capot? Existe-t-il un moyen (manifeste, WINAPI, etc.) de s'abonner à un nouveau thème sombre?

Mise à jour 1:
J'ai remarqué que le Panneau de configuration de l'Explorateur Windows est partiellement clair et partiellement sombre, il devrait donc s'agir d'un paramètre par fenêtre, plutôt que par paramètre.

Un autre exemple: les boîtes de dialogue Ouvrir un fichier deviennent sombres dans toutes les applications de bureau, tandis que l'application elle-même reste dans l'ancien thème clair.

Mise à jour 2:
J'ai essayé SetWindowTheme(hwnd, L"Explorer", NULL); pour TreeView et ListView. Cela modifie visiblement le style TreeView (le bouton de développement + Devient V), mais la fenêtre reste blanche.

11
Codeguard

Après avoir creusé, j'ai pu trouver ces deux approches. Les deux sont sans papiers et peuvent changer sans préavis.

1

SetWindowTheme(hwnd, L"DarkMode_Explorer", NULL);

2

using TYPE_AllowDarkModeForWindow = bool (WINAPI *)(HWND a_HWND, bool a_Allow);
static const TYPE_AllowDarkModeForWindow AllowDarkModeForWindow = (TYPE_AllowDarkModeForWindow)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133));
AllowDarkModeForWindow(a_HWND, true);
SetWindowTheme(hwnd, L"Explorer", NULL);

AVERTISSEMENT: Ordinal 133 peut avoir une API complètement différente derrière d'autres versions de Windows, y compris les versions Win10 plus récentes/plus anciennes.

Les deux approches appliquent certains effets, mais pas tout.
Par exemple, TreeView obtient des barres de défilement sombres et un arrière-plan sombre pour l'élément sélectionné, mais le reste de l'arrière-plan reste par défaut.

Malheureusement, jusqu'à présent, ce n'est pas comme "appeler une fonction et c'est tout". Il semble que même avec le thème correct appliqué, certaines couleurs d'arrière-plan doivent être traitées manuellement.

5
Codeguard

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

Ce mec a tout prévu dans du code réutilisable de Nice (licence MIT).

Il semble que Mode sombre soit toujours un domaine de développement dans Windows 10, mais je crois que Microsoft finira par documenter correctement et l'exposer aux applications de bureau.

Jusque-là, nous sommes bloqués avec des importations ordinales non documentées, puis dessin personnalisé et WM_CTLCOLOR* des messages pour dicter comment les contrôles qui ne sont pas encore compatibles avec le mode sombre natif sont peints.

Les plus fondamentales des nouvelles API Windows sont SetPreferredAppMode (uxtheme@135), à appeler avant toute création de fenêtre, et AllowDarkModeForWindow (uxtheme@133), à appeler sur toute fenêtre qui a l'intention d'utiliser la prise en charge native du mode sombre de Windows 10.

Voici la liste complète des importations ordinales uniquement de ce projet:

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 importe et initialise le mode sombre, de manière sûre, en vérifiant soigneusement les versions Windows 10 min et max prises en charge:

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();
                }
            }
        }
    }
}

Ailleurs, il profite de WM_CTLCOLOR* messages et notifications de dessin personnalisées dans Paint dark où Windows ne le fait pas (encore) pour nous.

Notez le FixDarkScrollBar. Il s'agit d'un hook IAT sur OpenNcThemeData pour remplacer la sélection de thème de la barre de défilement par la classe listview dans comctl32. C'est la partie qui me dérange le plus et je cherche à la supprimer. Je suis sûr qu'il l'est aussi.

J'ai adapté ce code à ma propre application et cela fonctionne bien. Cependant, je suis mal à l'aise d'utiliser ces API non documentées uniquement ordinales (même de manière aussi sûre que possible), et je m'attends à ce que Microsoft annonce et documente éventuellement le mode sombre pour les applications Win32 et rende ce travail redondant.

2
dyasta