如何在 Win32 应用程序中检测 Windows 10 light/dark 模式?
How to detect Windows 10 light/dark mode in Win32 application?
一些上下文:Sciter(纯 win32 应用程序)已经能够呈现 UWP 类似的 UI:
深色模式:
在轻模式下:
Windows 10.1803 在设置小程序中引入 Dark/Light 开关 as seen here for example.
问题:如何确定 Win32 应用程序中 "app mode" 的当前类型?
好吧,这个选项似乎没有直接暴露给常规 Win32 应用程序,但是可以通过 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
注册表路径中的 AppsUseLightTheme
键设置/检索它。
编辑:指出这适用于所有 Win32 项目,只要您在启用 c++17 的情况下进行构建。
如果您使用的是最新的 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);
}
Microsoft.Windows.SDK.Contracts
NuGet package gives .NET Framework 4.5+ and .NET Core 3.0+ applications access to Windows 10 WinRT APIs, including Windows.UI.ViewManagement.Settings
mentioned in 。将此包添加到包含以下代码的 .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}");
}
}
}
主题设置为深色时的输出为:
Foreground #FFFFFFFF Background #FF000000
当主题设置为 Light 时:
Foreground #FF000000 Background #FFFFFFFF
因为这是通过 Microsoft 提供的包公开的,其中声明:
This package includes all the supported Windows Runtime APIs up to Windows 10 version 1903
可以肯定地说,API 是有意为之的!
注意: 这并没有明确检查主题是 Light 还是 Dark但是检查一对 建议 使用的主题是两者之一的值,所以,.. 这种方法的正确性有点值得怀疑,但它至少是 "pure" C# 实现其他地方用 C++ 概述的方法
添加@user7860670 建议的解决方案,即:检查注册表项 AppsUseLightTheme
,我认为值得提供一些代码示例。
要从注册表读取 Win32 有 RegGetValue。
C++
bool is_light_theme() {
// based on
// 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;
}
生锈
使用 windows-rs 投影箱:
pub fn is_light_theme() -> bool {
// based on
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 _)
}
}
这是 @user7860670
的答案的 C# 解决方案
using Microsoft.Win32;
try
{
int res = (int)Registry.GetValue("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", -1);
}
catch
{
//Exception Handling
}
res 包含 windows
上默认主题的值
0:深色主题
1 : 浅色主题
-1 : 找不到 AppsUseLightTheme
一些上下文:Sciter(纯 win32 应用程序)已经能够呈现 UWP 类似的 UI:
深色模式:
在轻模式下:
Windows 10.1803 在设置小程序中引入 Dark/Light 开关 as seen here for example.
问题:如何确定 Win32 应用程序中 "app mode" 的当前类型?
好吧,这个选项似乎没有直接暴露给常规 Win32 应用程序,但是可以通过 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
注册表路径中的 AppsUseLightTheme
键设置/检索它。
编辑:指出这适用于所有 Win32 项目,只要您在启用 c++17 的情况下进行构建。
如果您使用的是最新的 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);
}
Microsoft.Windows.SDK.Contracts
NuGet package gives .NET Framework 4.5+ and .NET Core 3.0+ applications access to Windows 10 WinRT APIs, including Windows.UI.ViewManagement.Settings
mentioned in
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}");
}
}
}
主题设置为深色时的输出为:
Foreground #FFFFFFFF Background #FF000000
当主题设置为 Light 时:
Foreground #FF000000 Background #FFFFFFFF
因为这是通过 Microsoft 提供的包公开的,其中声明:
This package includes all the supported Windows Runtime APIs up to Windows 10 version 1903
可以肯定地说,API 是有意为之的!
注意: 这并没有明确检查主题是 Light 还是 Dark但是检查一对 建议 使用的主题是两者之一的值,所以,.. 这种方法的正确性有点值得怀疑,但它至少是 "pure" C# 实现其他地方用 C++ 概述的方法
添加@user7860670 建议的解决方案,即:检查注册表项 AppsUseLightTheme
,我认为值得提供一些代码示例。
要从注册表读取 Win32 有 RegGetValue。
C++
bool is_light_theme() {
// based on
// 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;
}
生锈
使用 windows-rs 投影箱:
pub fn is_light_theme() -> bool {
// based on
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 _)
}
}
这是 @user7860670
的答案的 C# 解决方案using Microsoft.Win32;
try
{
int res = (int)Registry.GetValue("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", -1);
}
catch
{
//Exception Handling
}
res 包含 windows
上默认主题的值0:深色主题
1 : 浅色主题
-1 : 找不到 AppsUseLightTheme