Skip to content

Instantly share code, notes, and snippets.

@rounk-ctrl
Last active April 7, 2024 16:00
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save rounk-ctrl/b04e5622e30e0d62956870d5c22b7017 to your computer and use it in GitHub Desktop.
Save rounk-ctrl/b04e5622e30e0d62956870d5c22b7017 to your computer and use it in GitHub Desktop.
Win32 Dark Mode

Dark Mode APIs.

API Signatures.

ShouldAppsUseDarkMode()

Checks whether system is using dark mode or not.
Signature:

using fnShouldAppsUseDarkMode = bool (WINAPI*)(); // ordinal 132

AllowDarkModeForWindow(In HWND hWnd, In bool allow)

Switches the control's theme to the dark variant if available.
Signature:

using fnAllowDarkModeForWindow = bool (WINAPI*)(HWND hWnd, bool allow); // ordinal 133

SetPreferredAppMode(In PreferredAppMode appMode)

Dark Context menu for the app. I don't know what else this does.
Signature(1903+):

using fnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, in 1903

PreferredAppMode enum

enum class PreferredAppMode
{
   Default,
   AllowDark,
   ForceDark,
   ForceLight,
   Max
};

Actually using them.

First we need to load uxtheme dll as a HMODULE.

HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);

Then we need to get the Process Address of the functions. We already know their ordinal numbers.
First initialize a instance of the signature.

fnSetPreferredAppMode SetPreferredAppMode;

then set the process address by using GetProcAddress

SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));

You can do like this for any API. Dont forget to free it

FreeLibrary(hUxtheme);

Usage.

Enables dark context menus which change automatically depending on the theme.

SetPreferredAppMode(PreferredAppMode::AllowDark);

Dark Controls:

First we need to know which all controls support Dark Mode.
If you open aero.msstyles in msstylesEditor, you would be able to see a lot of DarkMode elements.

image
And no you cant just do SetWindowTheme([handle to the control], L"DarkMode_Explorer", NULL);

Button:
SetWindowTheme([handle to the control], L"Explorer", NULL);
AllowDarkModeForWindow([handle to the control], true);
SendMessageW([handle to the control], WM_THEMECHANGED, 0, 0);
Edit:
SetWindowTheme([handle to the control], L"CFD", NULL);
AllowDarkModeForWindow([handle to the control], true);
SendMessageW([handle to the control], WM_THEMECHANGED, 0, 0);
ComboBox:
SetWindowTheme([handle to the control], L"CFD", NULL);
AllowDarkModeForWindow([handle to the control], true);
SendMessageW([handle to the control], WM_THEMECHANGED, 0, 0);
TreeView (maybe):
SetWindowTheme([handle to the control], L"Explorer", NULL);
AllowDarkModeForWindow([handle to the control], true);
SendMessageW([handle to the control], WM_THEMECHANGED, 0, 0);
Dark Scrollbars:

For dark scrollbars you need to iat hook and change the scrollbar theme before its created.
First download this header file and add it to your project. IatHook.h
Then use this function before the creation of HWND.

void FixDarkScrollBar()
{
	HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
	if (hComctl)
	{
		auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData
		if (addr)
		{
			DWORD oldProtect;
			if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect))
			{
				auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME {
					if (wcscmp(classList, L"ScrollBar") == 0)
					{
						hWnd = nullptr;
						classList = L"Explorer::ScrollBar";
					}
					return _OpenNcThemeData(hWnd, classList);
				};

				addr->u1.Function = reinterpret_cast<ULONG_PTR>(static_cast<fnOpenNcThemeData>(MyOpenThemeData));
				VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect);
			}
		}
	}
}
@0xF5T9
Copy link

0xF5T9 commented Sep 19, 2022

Hi, thank you for sharing the info.
May I ask how to set the menu bar to dark mode? I've tried to send TB_SETWINDOWTHEME message (DarkMode_Explorer) but nothing changed.

It happen in the original example code as well and not just my project
The menu bar is white by default and dropdown menu always in dark mode

I'm sorry if this isn't the right place to ask.

EDIT: I'm using latest Windows Version
EDIT: End up custom draw the menu bar, Thank you @rounk-ctrl.

@rounk-ctrl
Copy link
Author

Oh, for that you need to custom paint it. i think notepad++ has an implementation.

btw its menu bar not toolbar

@jjYBdx4IL
Copy link

I'm calling SetPreferredAppMode(PreferredAppMode::AllowDark) when the windows are already visible. I have to toggle fullscreen on and off to make the change affect menu popups. Is there any way to push the change through programmatically?

@rounk-ctrl
Copy link
Author

you can call that function at the beginning of your app, before any window is created. it will auto change the popup menu theme and stuff if you switch between dark and light

@jjYBdx4IL
Copy link

I can't. It's not my app. I'm just plugging in a plugin dll.

https://github.com/jjYBdx4IL/ReaperThemeHackDll

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment