web-dev-qa-db-ja.com

Win10ダークテーマ-WINAPIでの使用方法

October 2018 Update (version 1809)以降、Win10はWindowsエクスプローラーのダークテーマをサポートしています。

ここで設定できます:

  • UI:_Desktop | Context Menu | Personalize | Colors | Choose your default app mode = 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になります)が、ウィンドウは白のままです。

11
Codeguard

少し掘り下げた後、私はこれら2つのアプローチを見つけることができました。どちらも文書化されておらず、予告なく変更される場合があります。

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

警告:序数133は、新しい/古いWin10ビルドを含む他のバージョンのWindowsで、背後に完全に異なるAPIを持っている可能性があります。

どちらのアプローチも、すべてではなく、いくつかの効果を適用します。
たとえば、TreeViewは、選択したアイテムの暗いスクロールバーと暗い背景を取得しますが、残りの背景はデフォルトのままです。

残念ながら、これまでのところ、「関数を呼び出して、それで終わり」ではありません。正しいテーマが適用されていても、一部の背景色は手動で処理する必要があるようです。

5
Codeguard

https://github.com/ysc3839/win32-darkmode を参照してください

この男は、いくつかの素敵な再利用可能なコード(MITライセンス)ですべてをレイアウトしています。

ダークモードはまだWindows 10の開発分野のようですが、私は信じるマイクロソフトが最終的に適切に文書化し、デスクトップアプリに公開することを確信しています。

それまでは、文書化されていない序数のみのインポートに悩まされていました。その後、カスタム描画とWM_CTLCOLOR*メッセージにより、ネイティブダークモードがまだサポートされていないコントロールの描画方法が指示されます。

新しいWindows APIの最も基本的なものは、SetPreferredAppModeuxtheme@135)であり、ウィンドウの作成前に呼び出されます。およびAllowDarkModeForWindowuxtheme@133)は、ネイティブのWindows 10ダークモードサポートを使用する予定のすべてのウィンドウで呼び出されます。

以下は、そのプロジェクトからの順序のみのインポートの完全なリストです。

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は、ダークモードを安全な方法でインポートおよび初期化し、サポートされている最小および最大のWindows 10ビルドを慎重にチェックします。

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

他の場所では、彼はWM_CTLCOLOR*メッセージとカスタム描画通知を利用して、Windowsが(まだ)実行していないダークをペイントします。

FixDarkScrollBarに注意してください。これは、OpenNcThemeDataのIATフックで、comctl32のリストビュークラスによるスクロールバーテーマの選択をオーバーライドします。それは私が最も気になる部分であり、私はそれを回避しようとしています。きっと彼もそうだと思います。

このコードを自分のアプリケーションに適合させましたが、うまく機能します。しかし、私はこれらの文書化されていない序数のみのAPIを(可能な限り安全に)使用することに不快であり、Microsoftが最終的にWin32アプリのダークモードを発表および文書化し、この作業を冗長にすることを完全に期待しています。

2
dyasta