4

For theming purposes, I'm looking to detect the color of the Windows taskbar (in my case, for a tray icon).

I'm using Java, but any solutions are welcome as I'd happily convert them over as needed.

  • My first attempt was to read the registry.
    • This worked great for desktops that provided this value, but falls short when the registry does not provide it.
  • My second attempt was to take a screenshot of the taskbar and try to guess if it's dark or light themed.

    • This even works when autohide is on. Unfortunately it returns a black background regardless of what I do:

        WinDef.HWND tray = User32.INSTANCE.FindWindow("Shell_TrayWnd", null);
        BufferedImage bi = GDI32Util.getScreenshot(tray);
        SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, new JLabel((new ImageIcon(bi)))));
      

      enter image description here

Assuming I don't want to rely on the white/black color of the Windows logo, is there a way to detect this?

Related:

tresf
  • 7,103
  • 6
  • 40
  • 101
  • 1
    I thought `GetSysColor()` would work but that only appears to work with Windows Themes, and not the new "Dark Mode". It does look like you might be able to create a new Explorer window on-demand and test its attributes, but I'll leave that to you to experiment with... – Daniel Widdis Mar 24 '20 at 21:55
  • I don’t know if I reproduce the same issue as you, when I change light color to dark color, the icon will darken, click it, it will be corrected. See [this](https://i.stack.imgur.com/E306W.png). – Strive Sun Mar 25 '20 at 06:38
  • @StriveSun-MSFT, your example is using a Windows icon. I'm creating my own. – tresf Mar 27 '20 at 03:46

3 Answers3

3

So far, I have not encountered the lack of SystemUsesLightTheme and AppsUseLightTheme in the registry.

But I think recreating the key-values is worth trying.

Here is code sample(C++):

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <Windows.h>

using namespace std;


int main() {


    HKEY key;
    if (RegOpenKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"), &key) != ERROR_SUCCESS)
    {
        cout << "unable to open registry";
    }

    DWORD value_data = 0;

    if (RegSetValueEx(key, TEXT("SystemUsesLightTheme"), 0, REG_DWORD, (const BYTE*)&value_data, sizeof(value_data)) != ERROR_SUCCESS)
    {
        RegCloseKey(key);
        cout << "Unable to set registry value value_name";
    }
    else
    {
        cout << "value_name was set" << endl;
    }

}
Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • @tresf Hi, is the answer useful to you? – Strive Sun Mar 27 '20 at 01:33
  • The registry works as soon as dark/custom/light are selected. This bug report is about a scenario where they are NOT set (such as after the default install of certain Windows versions). If your solution is to blindly set these values, no, that doesn't help. :) – tresf Mar 27 '20 at 03:47
  • @tresf Yes, that's part of what I'm trying to do. If the registry is missing key values, we can try to create them and change the color(dark/custom/light) to see if the key values changes. In addition, do you mind sharing the certain windows versions? – Strive Sun Mar 27 '20 at 05:14
  • I believe it's not Windows specific, but rather original-windows-specific, as we can't reproduce the missing keys on a fresh install. One would think that makes this rare, but on the contrary, the number of machines I find this on that are up-to-date are staggerily high, so I can only assume it's traced back to an edition who's version information is since long lost in the upgrade process. https://github.com/qzind/tray/issues/597 – tresf Mar 27 '20 at 05:20
  • @tresf Thank you for sharing. When the old version of windows is updated to the new version, such as 1903 - > 1909, this problem may occur. Do I understand it correctly? – Strive Sun Mar 27 '20 at 06:10
  • That's the current theory however the originating Windows version is unknown. Worse, it's not necessarily useful information since the problem persists on updated machines. – tresf Mar 27 '20 at 15:30
  • @tresf Since there is no more useful information, and the problem is not reproducible, it seems that at present, it can only be found whether there is an API that can directly obtain the theme color, rather than through the registry – Strive Sun Mar 31 '20 at 10:14
  • I too would like to know which API provides this information. I just installed Windows 10 Home and it defaults to the lighter taskbar, whereas the offending machines that inspired this bug have the darker taskbar. – tresf Mar 31 '20 at 17:48
  • @tresf You can refer [this](https://stackoverflow.com/a/55670413/11128312) which does not require special permission or hacking into the registry. – Strive Sun Apr 01 '20 at 02:47
  • That's very nice. Isn't that the windowing though? Some Windows versions ship mismatched, and the question is only interested in the taskbar. – tresf Apr 01 '20 at 07:06
  • @tresf I think detecting the light and dark modes of the windows theme is the most effective way which can reflect the color mode of taskbar. I'm not familiar with the code of `winrt`, this is the solution I find that is feasible for your reference. – Strive Sun Apr 01 '20 at 07:29
  • But the themes are mismatched on a default install. The taskbar is dark, windows are light. To state "most effective way" seems incorrect. The original question is about the task bar, so if that information is not available it doesn't fix the problem. – tresf Apr 01 '20 at 16:40
  • I'm upvoting this because in the end, the registry values are really the only way. A less intrusive approach is to assume a missing `AppsUseLightTheme=1` whereas a missing `SystemUsesLightTheme=0`. I've provided this in a dedicated answer. – tresf Apr 11 '20 at 00:14
  • Also, one example of a Windows version WITHOUT these keys is Windows 10 1507. – tresf Apr 11 '20 at 00:15
3

When the documented registry entries are missing, it appears something in the OS is coded to fallback the following settings:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\

  • If AppsUseLightTheme is missing, assume it's 1
    • ... and then make the dark/light Windowing decisions based on this value.
  • If SystemUsesLightTheme is missing, assume it's 0
    • ... and then make the dark/light Taskbar/SystemTray decisions based on the this value.

The glory details...

Although fresh Windows Home installs defaults to the Light theme, these fresh installers also set the registry keys properly, so the combination of a missing registry key and a light taskbar is extremely unlikely (and probably impossible). To a similar point, studying modern OSs may -- improperly -- suggest the defaults come from the file C:\Windows\resources\Themes\aero.theme**, but don't be fooled! Older OSs didn't have a differentiating entry either... More below.

Instinct would suggest that the CurrentTheme or perhaps the InstallTheme registry values would serve as a sane fallback value, but changing these values appear to be for historical purposes and do not appear to actually change the light/dark theme.

reg query HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes /v InstallTheme
>>> returns the path to aero.theme

type %SystemRoot%\resources\Themes\aero.theme |find "SystemMode"
>>> returns SystemMode=dark

Even changing the InstallTheme for the entire machine (HKEY_LOCAL_MACHINE) doesn't modify this behavior of preferring SystemMode=dark (note, even this entry wasn't available in older Windows 10 versions. For example, Windows 10 v1507 doesn't have this entry in the theme file either).

Chasing the aero.theme hit some dead ends too. Attempts to directly modify aero.theme failed due to permissions, but copying aero.theme to the Desktop and changing SystemMode=dark to SystemMode=light and then double-clicking the theme file will make the taskbar go white, but only on newer Windows versions that supported the light theme.

So, yes, I have to agree with @strive-sun-msft the SystemUsesLightTheme registry entry is the best location. When testing, even the Task Bar itself monitors this, deleting it will reset it back to black. Unfortunately that fallback black Task Bar color remains to be a mystery. I can only assume it's hard-coded into the task bar itself.

Another workaround for this behavior is to just install the aero.theme file again by running it if the registry entries are missing. On newer Windows 10 versions, simply running this file will create the missing entries. Unfortunately, this doesn't work on older Windows 10 versions and worse, this will reset any custom preferences set by the user.

So the least intrusive way to detect the color of the taskbar is to read the registry and if the keys are missing, simply assume the theme Windows 10 shipped with is still in effect: Dark Taskbar, Light Windows.

tresf
  • 7,103
  • 6
  • 40
  • 101
2

You can use the Windows Registry Value SystemUsesLightTheme that is available at this Registry Path: Software\Microsoft\Windows\CurrentVersion\Themes\Personalize for detecting the dark/light theme.

I found a library called JRegistry that allows you to access this value.

        RegistryKey windowsPersonalizeKey = new RegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
        RegistryValue systemUsesLightThemeValue = windowsPersonalizeKey.getValue("SystemUsesLightTheme");
        if (systemUsesLightThemeValue != null) {
            //this value is available

            //getting the actual value
            byte[] data = systemUsesLightThemeValue.getByteData();
            byte actualValue = data[0];
            boolean windows10Dark = actualValue == 0;

            if (windows10Dark) {
               //the theme is dark
            } else {
                // the theme is light
            }
        }

Also, if you want to listen to this value dynamically:

        RegistryKey registryKey = new RegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
        RegistryWatcher.addRegistryListener((RegistryEvent registryEvent) -> {
            RegistryKey changedKey = registryEvent.getKey();
            if (changedKey.equals(registryKey)) {
                RegistryValue value = changedKey.getValue("SystemUsesLightTheme");
                //....
            }
        });
        RegistryWatcher.watchKey(registryKey);