Get Windows 10 Theme Color In Classic C++ WinAPI (Win32) Application
Asked Answered
A

3

7

I was searching how to get system theme color. I found GetSysColor and GetSysColorBrush. Then I tested it with something like that:

    cout << GetSysColorBrush(COLOR_HIGHLIGHT) << endl; //checking the value if it's changing when 
                                                                   //changing system color

    WNDCLASSW wc = {0};
    wc.hbrBackground = GetSysColorBrush(COLOR_HIGHLIGHT);
    wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = L"WindowClass";

    if(!RegisterClassW(&wc)) return -1;

    CreateWindowW(L"WindowClass", L"Window Name", WS_VISIBLE | WS_POPUP, 0, 0, windowWidth - 500, 
                                           windowHeight - 500, NULL, NULL, NULL, NULL);

I thought it works, because I had default blue theme and the window was blue (exactly same color), then i changed my theme to green but window was still blue (after restarting program obviously).

And now my question: Is it possible to get current system theme color?

Appendectomy answered 29/7, 2020 at 17:53 Comment(6)
The Windows 10 theme colors are available through UISettings. I don't know whether that type is available in desktop applications.Inadvertent
@Inadvertent Yeah, but it's UWP and I want to know if it can be done somehow only with winapi (win32) functionsAppendectomy
Maybe it can be done getting registry values?Appendectomy
@RemyLebeau What I need to put in first argument (HTHEME)?Appendectomy
Immersive colors/theme are not documented AFAIK. Here is a c# program easy to adapt in C/C++ that dumps them with their name: gist.github.com/smourier/d9de36c49e19aa9923d5143965057405Faizabad
How do I programmatically obtain the user’s selected accent color in Windows 10?Mortonmortuary
I
9

The Windows 10 theme colors are available through the UISettings type. It is available to classic desktop applications as well.

The following code uses C++/WinRT to retrieve the currently selected accent color:

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

#include <iostream>

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

int main()
{
    UISettings const ui_settings {};
    auto const accent_color { ui_settings.GetColorValue(UIColorType::Accent) };

    std::wcout << L"R: " << accent_color.R
               << L" G: " << accent_color.G
               << L" B: " << accent_color.B << std::endl;
}
Inadvertent answered 29/7, 2020 at 20:29 Comment(2)
Can I use C++/WinRT with Code::Blocks or do I need to use VS?Appendectomy
C++/WinRT generates C++ code for the language projection. Officially it supports MSVC only, with Clang being used during CI builds. GCC is neither supported nor verified to work. Even though C++/WinRT generates conforming C++17 code, it seemingly hits a lot of uncharted territory. You'll have to find out whether your version of GCC is happy with the generated code (and manually set up all the dependency management). Alternatively you can use WRL to get a bare minimum of WinRT support in C++.Inadvertent
H
1

Use GetThemeSysColor() and GetThemeSysColorBrush() when Visual Styles are enabled.

Hero answered 29/7, 2020 at 18:15 Comment(1)
Which parameters should I use? I tried brute forcing through a lot of them and none returned my actual accent color.Richers
R
-1

If you don't want or can't use any libraries you can do it with just Win32 API. There is no documented way to do this with Win32 API (only WinRT as in other answer), but you can pull it from registry, which is a lot safer than using undocumented functions (which also exist), which can crash in unexpected ways. While with registry you always can handle errors and fall back to value you would like to.

You can use RegOpenKeyExW, RegQueryValueExW and RegCloseKey to read value from registry and decode color components from DWORD with GetRValue, GetGValue and GetBValue macros.

Accent color that user has chosen in settings can be read from key

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\History\Colors\ColorHistory0

OS modifies that a bit for different places differently, but there you can get it raw as user selected. Works also for automatic option.

One place where OS is modifying it is window borders if "Show accent color on title bars and windows borders" is set. That color you can get too in following key:

Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\AccentColor

To find out whether colored window borders are turned on you can use this key:

Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorPrevalence

NOTE: Please keep in mind I have tested this only on Windows 11. I would assume keys are the same for all Windows version which have DWM. On Windows XP you could always use hardcoded fallback value or even GetSysColor(COLOR_HIGHLIGHT).

Richers answered 14/12, 2023 at 22:14 Comment(10)
"There is no documented way to do this" - Of course there is, as I explained in my answer. Why would you want to avoid using a system service?Inadvertent
@Inadvertent As said it differently - there is no documented way to do this with native Win32 API. There is documented way with WinRT library, but it has drawbacks, that's why I present alternative solution in case WinRT doesn't fit for somebody. Backwards compatibility, different compilers and cross-compilation could be some reasons against WinRT. Also as I mentioned there are several different accent colors, I present how to get two of them, while your answer only has one of them (haven't tried which one of those though).Richers
The Windows Runtime is part of Windows' API surface. I don't understand what "drawbacks" it has. You can call it from any language that can call a C API. You can access it with any compiler that understands C's ABI. That is any compiler that can compile Windows code. What's the "drawback"?Inadvertent
It works only on Windows 10 and above because it links statically to DLLs which do not exist on older Windows. You won't be able to gracefully handle errors and make fallback, because it will just crash. You can't also dynamically load it with LoadLibraryW/GetProcAddress it seems (github.com/Microsoft/cppwinrt/issues/40). I guess you could find out those DLLs and load them yourself, but that again will be already relying on undocumented things.Richers
"It works only on Windows 10 and above because it links statically to DLLs which do not exist on older Windows." - That's incorrect. It works on Windows 10 because that's the release that introduced the WinRT type. It is late-bound like COM. The load-time dynamic linking you hint to is the WinRT infrastructure, which is available on all supported versions of Windows. If you wish to support unsupported versions of Windows, then either don't use C++/WinRT or move your C++/WinRT code into a module you can run-time dynamic link.Inadvertent
By "works" I mean doesn't crash on older Windows. Surely APIs won't work on older versions because there are no required runtime libraries. However with dynamic linking on older versions you would just get "library/function not found" error, instead of crash, which you can catch. Putting all WinRT code to your own DLL sure will work, but it is more effort to do and maintain this. And you said yourself "If you wish to support unsupported versions of Windows, then either don't use C++/WinRT", this is exactly one of reasons for my answer, which is perfectly valid, even if you don't like it.Richers
By "don't use C++/WinRT" I did not imply to not use the Windows Runtime altogether. Just don't use this particular language projection. You seem to be recommending your "solution" because you don't understand the one I proposed. As support contracts go, it can run on Windows 8 and up. It needs Windows 10 to produce a meaningful value. What's the support contract for your solution?Inadvertent
What do you mean by using different projection? Is there documented way to use runtime directly as DLL? Visual Studio let's me choose only Windows 10-11 target to compile for. My solution compiles even for Windows 2000 and returns valid accent color since version where it was added (I don't remember which one exactly that is, was it Win8?).Richers
I don't recommend my solution as way to go to always, it is just an alternative, which some people may find useful. Plus WinRT doesn't provide all settings one may want (for example whether accent color is used on borders, at least I haven't found it in the docs), so you have to use registry anyway (or give up on that setting). Plus if one need only accent, then using whole WinRT may be seen as overkill by some.Richers
roapi.h is pretty much the entirety of the platform support required for the Windows Runtime, of which only two functions are strictly necessary. You must be entirely uninitiated to think the "whole WinRT" was "overkill". I never suggested using a "different projection". I was suggesting to not use a projection at all. But then, that's hardly an option for folks who are terrified by the "Empty Project" Visual Studio template. Anyway, what's the support story for your solution again?Inadvertent

© 2022 - 2024 — McMap. All rights reserved.