@@ -58,15 +58,15 @@ BOOL Validate_AllowDarkModeForWindow(const BYTE* functionPtr)
5858
5959#ifdef _M_X64
6060 /* Win10 builds from 20236 */
61- if ((functionPtr [0x52 ] == 0xBA ) && // mov edx,
62- (* (const DWORD * )(functionPtr + 0x53 ) == 0xA91E )) // 0A91Eh
61+ if ((functionPtr [0x52 ] == 0xBA ) && // mov edx,
62+ (* (const DWORD * )(functionPtr + 0x53 ) == 0xA91E )) // 0A91Eh
6363 {
6464 return TRUE;
6565 }
6666
6767 /* Win10 builds from 17763 to 19041 */
68- if ((functionPtr [0x15 ] == 0xBA ) && // mov edx,
69- (* (const DWORD * )(functionPtr + 0x16 ) == 0xA91E )) // 0A91Eh
68+ if ((functionPtr [0x15 ] == 0xBA ) && // mov edx,
69+ (* (const DWORD * )(functionPtr + 0x16 ) == 0xA91E )) // 0A91Eh
7070 {
7171 return TRUE;
7272 }
@@ -102,27 +102,86 @@ TYPE_AllowDarkModeForWindow Locate_AllowDarkModeForWindow()
102102 return (TYPE_AllowDarkModeForWindow )candidate ;
103103}
104104
105+ BOOL Validate_AllowDarkModeForWindowWithTelemetryId (const BYTE * functionPtr )
106+ {
107+ #ifdef _M_X64
108+ /* This function is rather long, but it uses an ATOM value of 0xA91E which is unlikely to change */
109+
110+ /* Win10 builds from 21301 */
111+ if ((functionPtr [0x31 ] == 0xBA ) && // mov edx,
112+ (* (const DWORD * )(functionPtr + 0x32 ) == 0xA91E )) // 0A91Eh
113+ {
114+ return TRUE;
115+ }
116+
117+ return FALSE;
118+ #else
119+ #error Unsupported processor type
120+ #endif
121+ }
122+
123+ typedef BOOL (WINAPI * TYPE_AllowDarkModeForWindowWithTelemetryId )(HWND a_HWND , BOOL a_Allow , int a_TelemetryID );
124+ TYPE_AllowDarkModeForWindowWithTelemetryId Locate_AllowDarkModeForWindowWithTelemetryId ()
125+ {
126+ const HMODULE hUxtheme = GetModuleHandle (L"uxtheme.dll" );
127+ if (!hUxtheme )
128+ return 0 ;
129+
130+ /*
131+ * Function is only exported by ordinal.
132+ * Hopefully one day Microsoft will finally export it by name.
133+ */
134+ const BYTE * candidate = (const BYTE * )GetProcAddress (hUxtheme , MAKEINTRESOURCEA (140 ));
135+ if (!candidate )
136+ return 0 ;
137+
138+ /*
139+ * In next Windows version, some other function can end up having this ordinal.
140+ * Compare function's code to known signature to make sure.
141+ */
142+ if (!Validate_AllowDarkModeForWindowWithTelemetryId (candidate ))
143+ return 0 ;
144+
145+ return (TYPE_AllowDarkModeForWindowWithTelemetryId )candidate ;
146+ }
147+
105148#ifndef NO_AllowDarkModeForWindow
106149JNIEXPORT jboolean JNICALL OS_NATIVE (AllowDarkModeForWindow )
107150(JNIEnv * env , jclass that , jlong arg0 , jboolean arg1 )
108151{
109152 /* Cache the search result for performance reasons */
110153 static TYPE_AllowDarkModeForWindow fn_AllowDarkModeForWindow = 0 ;
154+ static TYPE_AllowDarkModeForWindowWithTelemetryId fn_AllowDarkModeForWindowWithTelemetryId = 0 ;
111155 static int isInitialized = 0 ;
112156 if (!isInitialized )
113157 {
114158 fn_AllowDarkModeForWindow = Locate_AllowDarkModeForWindow ();
159+ fn_AllowDarkModeForWindowWithTelemetryId = Locate_AllowDarkModeForWindowWithTelemetryId ();
115160 isInitialized = 1 ;
116161 }
117162
118- if (!fn_AllowDarkModeForWindow )
119- return 0 ;
163+ if (fn_AllowDarkModeForWindow )
164+ {
165+ jboolean rc = 0 ;
166+ OS_NATIVE_ENTER (env , that , AllowDarkModeForWindow_FUNC );
167+ rc = (jboolean )fn_AllowDarkModeForWindow ((HWND )arg0 , arg1 );
168+ OS_NATIVE_EXIT (env , that , AllowDarkModeForWindow_FUNC );
169+ return rc ;
170+ }
120171
121- jboolean rc = 0 ;
122- OS_NATIVE_ENTER (env , that , AllowDarkModeForWindow_FUNC );
123- rc = (jboolean )fn_AllowDarkModeForWindow ((HWND )arg0 , arg1 );
124- OS_NATIVE_EXIT (env , that , AllowDarkModeForWindow_FUNC );
125- return rc ;
172+ // In Win11, 'AllowDarkModeForWindow' is a thin wrapper for 'AllowDarkModeForWindowWithTelemetryId'.
173+ // It's hard to verify the wrapper, but it's easy enough to verify the target.
174+ // For this reason, call 'AllowDarkModeForWindowWithTelemetryId' here.
175+ if (fn_AllowDarkModeForWindowWithTelemetryId )
176+ {
177+ jboolean rc = 0 ;
178+ OS_NATIVE_ENTER (env , that , AllowDarkModeForWindow_FUNC );
179+ rc = (jboolean )fn_AllowDarkModeForWindowWithTelemetryId ((HWND )arg0 , arg1 , 0 );
180+ OS_NATIVE_EXIT (env , that , AllowDarkModeForWindow_FUNC );
181+ return rc ;
182+ }
183+
184+ return 0 ;
126185}
127186#endif
128187
@@ -139,9 +198,9 @@ BOOL Validate_SetPreferredAppMode(const BYTE* functionPtr)
139198 return FALSE;
140199
141200 return
142- (functionPtr [0x00 ] == 0x8B ) && (functionPtr [0x01 ] == 0x05 ) && // mov eax,dword ptr [uxtheme!g_preferredAppMode]
143- (functionPtr [0x06 ] == 0x87 ) && (functionPtr [0x07 ] == 0x0D ) && // xchg ecx,dword ptr [uxtheme!g_preferredAppMode]
144- (functionPtr [0x0C ] == 0xC3 ); // ret
201+ (functionPtr [0x00 ] == 0x8B ) && (functionPtr [0x01 ] == 0x05 ) && // mov eax,dword ptr [uxtheme!g_preferredAppMode]
202+ (functionPtr [0x06 ] == 0x87 ) && (functionPtr [0x07 ] == 0x0D ) && // xchg ecx,dword ptr [uxtheme!g_preferredAppMode]
203+ (functionPtr [0x0C ] == 0xC3 ); // ret
145204#else
146205 #error Unsupported processor type
147206#endif
@@ -196,6 +255,16 @@ JNIEXPORT jint JNICALL OS_NATIVE(SetPreferredAppMode)
196255}
197256#endif
198257
258+ jboolean isDarkThemeAvailable () {
259+ if (!Locate_SetPreferredAppMode ())
260+ return JNI_FALSE ;
261+
262+ if (!Locate_AllowDarkModeForWindow () && !Locate_AllowDarkModeForWindowWithTelemetryId ())
263+ return JNI_FALSE ;
264+
265+ return JNI_TRUE ;
266+ }
267+
199268#ifndef NO_IsDarkModeAvailable
200269JNIEXPORT jboolean JNICALL OS_NATIVE (IsDarkModeAvailable )
201270(JNIEnv * env , jclass that )
@@ -205,7 +274,7 @@ JNIEXPORT jboolean JNICALL OS_NATIVE(IsDarkModeAvailable)
205274 static int isInitialized = 0 ;
206275 if (!isInitialized )
207276 {
208- isAvailable = ( Locate_SetPreferredAppMode () && Locate_AllowDarkModeForWindow () );
277+ isAvailable = isDarkThemeAvailable ( );
209278 isInitialized = 1 ;
210279 }
211280
0 commit comments