Win10 深色主题 - 如何在 WINAPI 中使用? [英] Win10 dark theme - how to use in WINAPI?

查看:87
本文介绍了Win10 深色主题 - 如何在 WINAPI 中使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

2018 年 10 月更新(版本 1809) 开始,Win10 支持 Windows 资源管理器中的深色主题.

可以在这里配置:

  • 用户界面:<代码>桌面 |上下文菜单 |个性化 |颜色 |选择您的默认应用模式 = Dark
  • 注册表:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme = DWORD:0

虽然此设置已存在一段时间,但它仅影响 UWP 应用程序.但是,在此 Windows 10 版本中,它也会影响 Windows 资源管理器,它是一个桌面应用程序.这意味着 Windows 现在对它有内部支持.不过,除 Windows 资源管理器之外的桌面应用程序目前不受影响.

我想在我的应用程序中使用它.它是如何在幕后实施的?是否有某种方式(清单、WINAPI 等)订阅新的黑暗主题?

更新 1:
我注意到 Windows 资源管理器控制面板部分亮部分暗,所以它应该是每个窗口的设置,而不是每个进程的设置.

另一个例子:打开文件对话框在所有桌面应用程序中都变暗,而应用程序本身仍保持旧的浅色主题.

更新 2:
我为 TreeViewListView 尝试了 SetWindowTheme(hwnd, L"Explorer", NULL);.这明显改变了 TreeView 样式(+ 展开按钮变为 V),但窗口保持白色.

解决方案

参见 https://github.com/ysc3839/win32-darkmode

这家伙用一些很好的可重用代码(MIT 许可)来安排一切.

似乎暗模式仍然是 Windows 10 中的一个开发领域,但我相信微软最终会正确记录并将其公开给桌面应用程序.>

在那之前,我们一直在使用未记录的仅有序导入,然后是自定义绘制和 WM_CTLCOLOR* 消息来指示如何绘制尚不具有原生暗模式支持的控件.

最基本的新 Windows API 是 SetPreferredAppMode (uxtheme@135),在任何窗口创建之前调用,以及AllowDarkModeForWindow (uxtheme@133),在任何打算使用原生 Windows 10 暗模式支持的窗口上调用.

以下是该项目中仅按序导入的完整列表:

using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD Major, LPDWORD minor, LPDWORD build);//1809 17763使用 fnShouldAppsUseDarkMode = bool (WINAPI *)();//序号 132使用 fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow);//序号 133使用 fnAllowDarkModeForApp = bool (WINAPI *)(bool allow);//序号 135,自 18334 起移除使用 fnFlushMenuThemes = void (WINAPI *)();//序号 136使用 fnRefreshImmersiveColorPolicyState = void (WINAPI *)();//序号 104使用 fnIsDarkModeAllowedForWindow = bool (WINAPI *)(HWND hWnd);//序号 137使用 fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI *)(IMMERSIVE_HC_CACHE_MODE 模式);//序号 106使用 fnOpenNcThemeData = HTHEME(WINAPI *)(HWND hWnd, LPCWSTR pszClassList);//序号 49//内幕18290使用 fnShouldSystemUseDarkMode = bool (WINAPI *)();//序号 138//内幕18334使用 fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode);//序号 135,从 18334 开始使用 fnIsDarkModeAllowedForApp = bool (WINAPI *)();//序号 139

InitDarkMode 以安全的方式导入和初始化暗模式,仔细检查 Windows 10 支持的最小和最大版本:

void InitDarkMode(){fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));如果(RtlGetNtVersionNumbers){DWORD 主要、次要;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.1836{HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);如果(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));如果(g_buildNumber <18334)_AllowDarkModeForApp = reinterpret_cast(ord135);别的_SetPreferredAppMode = reinterpret_cast(ord135);//_FlushMenuThemes = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));_IsDarkModeAllowedForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));if (_OpenNcThemeData &&_RefreshImmersiveColorPolicyState &&_ShouldAppsUseDarkMode &&_AllowDarkModeForWindow &&(_AllowDarkModeForApp || _SetPreferredAppMode) &&//_FlushMenuThemes &&_IsDarkModeAllowedForWindow){g_darkModeSupported = true;AllowDarkModeForApp(true);_RefreshImmersiveColorPolicyState();g_darkModeEnabled = _ShouldAppsUseDarkMode() &&!IsHighContrast();FixDarkScrollBar();}}}}}

在其他地方,他利用 WM_CTLCOLOR* 消息和自定义绘制通知在 Windows(尚未)为我们做的地方绘制黑暗.

注意 FixDarkScrollBar.这是 OpenNcThemeData 上的 IAT 挂钩,用于覆盖 comctl32 中的列表视图类的滚动条主题选择.这是最让我烦恼的部分,我正在寻求解决它.我相信他也是.

我已将此代码改编为我自己的应用程序,并且运行良好.但是,我对使用这些未记录的仅有序 API 感到不舒服(即使尽可能安全),并且完全希望 Microsoft 最终宣布并记录 Win32 应用程序的暗模式并使这项工作变得多余.

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.

解决方案

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.

这篇关于Win10 深色主题 - 如何在 WINAPI 中使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆