如何在Win32应用程序中检测Windows 10亮/暗模式?

c-s*_*ile 20 windows winapi win32gui windows-10

一点上下文:Sciter(纯win32应用程序)已经能够呈现UWP相似的UI:

在黑暗模式下: 在黑暗模式下

在光线模式下: 在光线模式下

Windows 10.1803在Settings applet中引入了Dark/Light开关,例如,如此处所示.

问题:如何确定Win32应用程序中"app mode"的当前类型?

use*_*670 19

好吧,看起来这个选项没有直接暴露给常规的win32应用程序,但可以通过AppsUseLightTheme(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)AppsUseLightTheme注册表项设置/检索它.

  • @SörenKuklau更好的方法是收听[`WM_WININICHANGE`广播](https://docs.microsoft.com/zh-cn/windows/desktop/winmsg/wm-wininichange)-`lParam ==“ ImmersiveColorSet”`。 (5认同)
  • @dgellow 是的,但它们都是“#define”作为相同的值,所以在编译器级别并不重要。 (4认同)
  • @SörenKuklau [RegNotifyChangeKeyValue](https://docs.microsoft.com/en-us/windows/desktop/api/Winreg/nf-winreg-regnotifychangekeyvalue) (2认同)

dge*_*low 9

要添加到@user7860670建议的解决方案,即:检查注册表项AppsUseLightTheme,我认为值得有一些代码示例。

Win32有RegGetValue从注册表中读取

C++

bool is_light_theme() {
    // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application

    // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian
    auto buffer = std::vector<char>(4);
    auto cbData = static_cast<DWORD>(buffer.size() * sizeof(char));
    auto res = RegGetValueW(
        HKEY_CURRENT_USER,
        L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
        L"AppsUseLightTheme",
        RRF_RT_REG_DWORD, // expected value type
        nullptr,
        buffer.data(),
        &cbData);

    if (res != ERROR_SUCCESS) {
        throw std::runtime_error("Error: error_code=" + std::to_string(res));
    }

    // convert bytes written to our buffer to an int, assuming little-endian
    auto i = int(buffer[3] << 24 |
        buffer[2] << 16 |
        buffer[1] << 8 |
        buffer[0]);

    return i == 1;
}
Run Code Online (Sandbox Code Playgroud)

使用windows-rs投影箱:

pub fn is_light_theme() -> bool {
    // based on https://stackoverflow.com/a/51336913/709884
    let mut buffer: [u8; 4] = [0; 4];
    let mut cb_data: u32 = (buffer.len()).try_into().unwrap();
    let res = unsafe {
        RegGetValueW(
            HKEY_CURRENT_USER,
            r#"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"#
                .to_wide()
                .as_pwstr(),
            "AppsUseLightTheme".to_wide().as_pwstr(),
            RRF_RT_REG_DWORD,
            std::ptr::null_mut(),
            buffer.as_mut_ptr() as _,
            &mut cb_data as *mut _,
        )
    };
    assert_eq!(
        res,
        ERROR_SUCCESS,
        format!("failed to read key from registry: err_code={}", res).as_str(),
    );

    // REG_DWORD is signed 32-bit, using little endian
    let light_mode = i32::from_le_bytes(buffer) == 1;
    light_mode
}

pub fn is_dark_theme() -> bool {
    !is_light_theme()
}

// convert &str to Win32 PWSTR
#[derive(Default)]
pub struct WideString(pub Vec<u16>);

pub trait ToWide {
    fn to_wide(&self) -> WideString;
}

impl ToWide for &str {
    fn to_wide(&self) -> WideString {
        let mut result: Vec<u16> = self.encode_utf16().collect();
        result.push(0);
        WideString(result)
    }
}

impl ToWide for String {
    fn to_wide(&self) -> WideString {
        let mut result: Vec<u16> = self.encode_utf16().collect();
        result.push(0);
        WideString(result)
    }
}

impl WideString {
    pub fn as_pwstr(&self) -> PWSTR {
        PWSTR(self.0.as_ptr() as *mut _)
    }
}
Run Code Online (Sandbox Code Playgroud)


jar*_*jar 8

编辑:只要您在启用 c++17 的情况下构建,就说明这适用于所有 Win32 项目。

如果您使用的是最新的 SDK,这对我有用。

#include <winrt/Windows.UI.ViewManagement.h>

using namespace winrt::Windows::UI::ViewManagement;

if (RUNNING_ON_WINDOWS_10) {
  UISettings settings;
  auto background = settings.GetColorValue(UIColorType::Background);
  auto foreground = settings.GetColorValue(UIColorType::Foreground);
}
Run Code Online (Sandbox Code Playgroud)

  • winrt 不是 win32,问题是关于专门的 win32。 (5认同)
  • @c-smile 毫无意外,微软通过 winrt 将其所有 UWP 内容公开给 C++ 应用程序。[UISettings 参考中的示例显示了 C++ 代码](https://docs.microsoft.com/en-us/uwp/api/Windows.UI.ViewManagement.UISettings) (4认同)
  • @H.Al-Amri `WindowsApp.lib` 包含您需要的一切。 (2认同)

Rob*_*Rob 7

Microsoft.Windows.SDK.ContractsNuGet包给人的.NET Framework 4.5+和.NET核心3.0+应用程序访问Windows 10的WinRT API,包括Windows.UI.ViewManagement.Settings中提到的答案jarjar。将此包添加到包含以下代码的 .NET Core 3.0 控制台应用程序:

using System;
using Windows.UI.ViewManagement;

namespace WhatColourAmI
{
    class Program
    {
        static void Main(string[] args)
        {

            var settings = new UISettings();
            var foreground = settings.GetColorValue(UIColorType.Foreground);
            var background = settings.GetColorValue(UIColorType.Background);

            Console.WriteLine($"Foreground {foreground} Background {background}");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

主题设置为Dark时的输出为:

前景#FF FFFFFF背景#FF 000000

当主题设置为Light 时,它是:

前景#FF 000000背景#FF FFFFFF

由于这是通过 Microsoft 提供的软件包公开的,该软件包指出:

此软件包包括所有受支持的 Windows 运行时 API,最高可达 Windows 10 版本 1903

可以肯定的是,这个 API 是可访问的!

注意:这不是明确检查主题是浅色还是深色,而是检查表明使用的主题是两者之一的一对值,因此,.. 这种方法的正确性有点可疑,但它在至少是一种“纯粹的”C# 方式来实现在其他地方用 C++ 概述的内容